【面试小题】你不是很懂this吗,做道题试试?

255 阅读2分钟

题目

请写出代码执行的结果。

var length = 10;

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

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

obj.method(fn, 1);

先不要看答案,思考思考。会输出 5 吗?

答案

浏览器环境下:

10
2

node 环境:

undefined
2

思考

这段代码十分有趣,是一个 this 作用域的问题。

这里其实是耍了一个小聪明,已经不是单纯的考 this,在 this 结合了 arguments 后,意图把人迷惑。

arguments[0]()的打印结果是 2,能猜出 this 指向 arguments 所以输出arguments.length

为什么 this 指向了 arguments?

在 JS 里 arguments 是一个类数组对象(是对象,但是长的比较像数组,又欠缺一些数组的方法),其存储的是函数的参数。arguments可以这样理解:

arguments = {
  0: fn,
  1: 第二个参数, //没有
  2: 第三个参数, //没有
  ...,
  length: 1 //只有一个参数
}

这里 arguments[0]指代的就是method函数的第一个参数:fnarguments[0]()似乎可以看做是arguments.0(),(但应该不允许后者这样写),所以 arguments[0]()的意思就是:fn(),就合理解释了 this 指向 arguments。

浏览器中的 this

js 中 this 指的是当前对象,如果在全局范围内使用 this,浏览器环境下则指代当前页面 window

如果在函数中使用 this,则 this 指代什么是根据当前函数是在什么对象上调用。

所以,第一次执行 fn 比较没有争议,浏览器打印是 10。

除此之外,在内联事件中,也要注意:

当代码被内联处理函数调用时,它的 this 指向监听器所在的 DOM 元素

<button onclick="console.log(this)">what is this</button>

nodejs 中的 this

全局中的 this

node 全局中 this 与 global 对象没有任何的关系,全局中的 this 默认是一个空对象,指向的是 module.exports。这就涉及到 nodejs 的模块化了,nodejs 中存在模块作用域,感兴趣的可以自行百度了解。

函数中的 this

在函数中 this 指向的是 global 对象,和全局中的 this 不是同一个对象,在函数中通过 this 定义的变量就是相当于给 global 添加了一个属性,此时与全局中的 this 已经没有关系了。

构造函数中的 this

在构造函数中 this 指向的是它的实例,而不是 global,这个比较好理解。

其他 JS 执行环境

有些 JS 执行环境下还会存在 this 是undefined的情况,报错:

TypeError: Cannot read property 'length' of undefined