这段JS代码的作用域为什么是window了?

var a=1;
var b={
  a:2,
  b:function(){
    console.log(this.a);
  }(), 
  f:this.f=function(){
    console.log(this.a);
  }
};
function f(){ console.log(3); }
f();
b.f();
(b.f)();
(0,b.f)();

有这样一段代码,答案是1、1、2、2、1,第一个答案是1是因为b.b是个立即执行函数,但是为什么后面的答案是1221了??

阅读 2.7k
2 个回答

首先我觉得这是一道人为构造的“戏过了的”JS题目,涉及到函数声明提升、this指向、函数表达式、逗号表达式,但是日常谁写出这样类似的代码肯定是活不久了的,下面尝试分解一下:

首先有一个知识点是声明提升,这里有一个全局作用域下的f()函数,这个会被提升到顶端,所以你原来的代码实际上执行起来是这样的顺序:

//这个函数声明提升啦
function f(){ console.log(3); }

var a=1;
var b={
  a:2,
  b:function(){
    console.log(this.a);
  }(), 
  f:this.f=function(){
    console.log(this.a);
  }
};
//function f(){ console.log(3); }
f();
b.f();
(b.f)();
(0,b.f)();

而在对象b中的属性f中,是一个函数表达式,b.f:this.f=function(){...}相当于b.f=this.f=function(){...},这个赋值是立即发生的,只是函数体内不立即执行,所以也就是说,b.f和this.f都指向了这个匿名函数,而这个this是window,变成了b.f=window.f=function(){...},于是这个window.f覆盖了由于声明提升到顶的那个f(那个f也是在window下的),明白了这个再看:

  1. 第一个“1”,题目理解没错,是因为b.b是个立即执行的函数,输出的

  2. 第二个“1”,是f()的输出,也就是window.f(),这个根据上面的分析,看到由于声明提升和对象内覆盖赋值,这个执行的是console.log(this.a);函数体,this指向window,所以是1;

  3. 第三个“2”,是b.f()的输出,因为b.f也是指向了那个函数体,但此时this指向调用这个函数的对象b,所以是输出2;

  4. 第四个“2”,无非是上面的那个变成了函数表达式,b.f还是对原来函数的引用,this的值得到维持,没啥,还是一样的;

  5. 第五个“1”,在执行函数之前括号里是一个逗号表达式,我们知道逗号表达式,是取最后一项的值作为整个表达式的值,但是这个时候,this的值不能得到维持,因为这个逗号表达式相当于把原来的函数体赋值给了整个表达式做表达式的值,这时候的this就变成了window,所以输出1

为了说明这个坑爹的结果,尤其是第5个值的输出,我去翻了《JS高程》:

clipboard.png

这题目也太 操蛋了

推荐问题
宣传栏