javascript易错题,高手都很容易掉进去的题目

1、考察this

var length = 10;
function fn() {
  console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};

obj.method(fn, 1);

输出:10 2

  第一次输出10应该没有问题。我们知道取对象属于除了点操作符还可以用中括号,所以第二次执行时相当于arguments调用方法,this指向arguments,而这里传了两个参数,故输出arguments长度为2。

2、var和函数的提前声明

function fn(a) {
  console.log(a); 
  var a = 2;
  function a() {}
  console.log(a); 
}

fn(1);

输出:function  a() {} 2

  我们知道var和function是会提前声明的,而且function是优先于var声明的(如果同时存在的话),所以提前声明后输出的a是个function,然后代码往下执行a进行重新赋值了,故第二次输出是2。

3、变量隐式声明

if('a' in window) {
  var a = 10;
}

alert(a);

答案:10

  前面我说过function和var会提前声明,而其实{...}内的变量也会提前声明。于是代码还没执行前,a变量已经被声明,于是 'a' in window 返回true,a被赋值。(这里要特别注意的是,只是声明提前,赋值还是在原来的地方)

4、函数声明优于变量声明

console.log(typeof fn);
function fn() {};
var fn;

答案:function

  因为函数声明优于变量声明。我们知道在代码逐行执行前,函数声明和变量声明会提前进行,而函数声明又会优于变量声明,这里的优于可以理解为晚于变量声明后,如果函数名和变量名相同,函数声明就能覆盖变量声明。所以以上代码将函数声明和变量声明调换顺序还是一样结果。

5、实现如下语法的功能:var a = (5).plus(3).minus(6); //2

Number.prototype.plus = function(a) {
  return this + a;
};
 
Number.prototype.minus = function(a) {
  return this - a;
};
 
var a = (5).plus(3).minus(6);
console.log(a); // 2

直接在Number对象上加扩展方法即可,传说中这样很不好,but我也想不到更好的办法了...

6、实现如下语法的功能:var a = add(2)(3)(4); //9

function add(a) {
  var temp = function(b) {
    return add(a + b);
  }
  temp.valueOf = temp.toString = function() {
    return a;
  };
  return temp;
}
var ans = add(2)(3)(4);
console.log(ans); // 9

对valueOf和toString的考察,具体可以参考《valueOf和toString》 

6-1、写一个 sum 方法,使得以上代码得到预期结果

console.log(sum(2,3));   // Outputs 5
console.log(sum(2)(3));  // Outputs 5
function sum(x) {
  if (arguments.length == 2) {
    return arguments[0] + arguments[1];
  } else {
    return function(y) { return x + y; };
  }
}

或者这样

function sum(x, y) {
  if (y !== undefined) {
    return x + y;
  } else {
    return function(y) { return x + y; };
  }
}

 7、以下代码的输出是什么?

var a={},
    b={key:'b'},
    c={key:'c'};
 
a[b]=123;
a[c]=456;
 
console.log(a[b]);

一道有趣的题目,答案是 456。

我们知道,Javascript 中对象的 key 值,一定会是一个 string 值,如果不是,则会隐式地进行转换。当执行到 a[b]=123] 时,b 并不是一个 string 值,将 b 执行 toString() 方法转换(得到 “[object Object]“),a[c] 也是相同道理。所以代码其实可以看做这样执行:

var a={},
    b={key:'b'},
    c={key:'c'};
 
// a[b]=123;
a["[object Object]"]=123;
 
// a[c]=456;
a["[object Object]"]=456;
 
console.log(a["[object Object]"]);

 8、以上代码可能会由于递归调用导致栈溢出,如何规避这个问题?

var list = readHugeList();
 
var nextListItem = function() {
    var item = list.pop();
 
    if (item) {
        // process the list item...
        nextListItem();
    }
};

首先,任何递归都可以用迭代来代替,所以改写成迭代方式肯定没有问题。

var list = readHugeList();
 
var nextListItem = function() {
    var item = list.pop();
 
    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};

利用 setTimeout 的异步性质,完美地去除了这个调用栈。

如果你还是摸不着头脑,简单举个栗子:

var list = [0, 1];
 
var nextListItem = function() {
    var item = list.pop();
 
    if (item) {
      nextListItem();
    }
 
    console.log(item);
};
 
nextListItem();

上面的代码会依次输出 0 和 1,因为程序中形成了一个调用栈,1 被压到了栈底,最后出栈。

把程序改成这样:

var list = [0, 1];
 
var nextListItem = function() {
    var item = list.pop();
 
    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
 
    console.log(item);
};
 
nextListItem();

这回就是 1 和 0 了,因为 setTimeout 的回调只有当主体的 js 执行完后才会去执行,所以先输出了 1,自然也就没有栈这一说法了。

事实上,并不是所有递归都能这样改写,如果下一次递归调用依赖于前一次递归调用返回的值,就不能这么改了。

9、以下代码输出什么?

console.log(1 +  "2" + "2");
console.log(1 +  +"2" + "2");
console.log(1 +  -"1" + "2");
console.log(+"1" +  "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);

+”2″ 能将字符串 “2″ 转换成整数 2,-”2″ 同理,而两个变量进行 “+” 运算时,如果都是数字和字符串,则分别进行数字相加和字符串拼接,如果一个是数字一个是字符串,则将数字转为字符串,如果是 “-” 运算呢?则将字符串转为数字。

“A” – “B” 会返回 NaN,因为 “A” 和 “B” 无法转成数字进行运算,这里不要以为 “A” 和 “B” 能转为 ASCII码 进行运算(不要和 C 语言搞混了)。而 NaN 和字符串相加,会转成 “NaN” 和字符串去拼接,NaN 和任何数字相加结果还是 NaN。

posted @ 2016-01-13 13:02  点点乐淘淘  阅读(600)  评论(0)    收藏  举报