每个函数都有一个隐式的
this
形参。将函数作为方法调用时,这个参数会被设置为用于访问该方法的对象。这和大多数面向对象语言中的this
(或self
)含义相同。但是 JavaScript 在「关联到对象的方法」与「独立函数」这两者之间,使用了单一的定义形式。这使this
导致了许多程序员的困惑和 bug。 ——————Brendan Eich(JS之父)
this指向到问题是公认的!创造JS的人都这么说它。所谓的灵活指向不过是缝合怪。
所以说,对于 this
的强限制是非常有必要的,比如ES6的发布。
现在面试还在考查 this
的使用作为主要晒人的手段是不理智不合理的,希望各位面试官能提升自己的修养!
当然,现在依旧有大量的老项目充斥着各种 this
。面对这样的项目,我建议面试者另选公司,建议面试官早日理智。
如果在现在的市场有其他更好的选择的话
它所谓的灵活是什么?
new关键字让我在批量创造对象的时候省却了4个步骤;函数不用显性的写上return,它自己会添加,并且return undeinfed。在调用函数的时候,自然也会有有类似的操作。
我们想来看调用函数的四种方式:
fn(a, b)
obj.fn(a,b)
fn.call(Object, a, b)
fn.apply(Object, [a, b])
其中apply
和call
的差别只是参数的类型不一样。它们的第一个参数Object
大多数的是直接传this进去。和fn(a, b)
和obj.fn(a,b )
有this
和arguments
两个隐式的参数不同,它们是显式的!
那是不是可以说 fn(a, b)
和obj.fn(a, b)
以及fn.apply(Object, [a. b])
都是 fn.call(Object, a, b)
的语法糖!!!
- fn(a, b) ⇒ fn.call(undefine, a, b)
- obj.fn(a, b) ⇒ fn.call(obj, a, b)
- fn.apply(Object, [a, b]) ⇒ fn.call(Object, a, b)
也就是说,其实我们调用函数的时候,只有一种方式,就是fn.call(Object, a, b)
!
在这个前提之下, 我们来看看下面的这个经典面试题:
const obj = {
foo: function() {
console.log(this)
}
}
const myObj = obj.foo
myObj() // window
obj.foo() // function foo() {}
myObj(undefined)
obj.foo(obj)
我们用上面的来定义一下:
myObj()
⇒ myObj.call(undefined)
obj.foo()
⇒ obj.foo.call(obj)
所以说,obj.foo()答应出来的就是 function foo() { }。由于在浏览器当中,当传入的Object是undefined
或者 null
的时候,它默认指向windows
。
基于这样的现象,我们可以引用《你不知道的JavaScript》中对于 this
的概括:
- 上下文是在函数被调用的时候创建的
- 上下文中包括了
this
换一句话说就是: this
** 的行为是在运行时决定的!**
造成它们的打印的结果的不同,就是函数的运行的时候创建的上下文不同,在这里,我们完全可以把上下文这个概念等同于 this
。
myObj运行的时候,foo函数已经挂载到了上下文全局中,所以它的 this
打印的结果是 window。而obj.foo 运行时,foo函数挂载在对象内部,所以this打印是函数自己。
上面例子说明的Object就是上下文,一般写作context。
它带来了什么灾难
当它和事件循环机制在一起的时候,如下题:
const object = {
message: 'Hello, World!',
logMessage() {
console.log(this.message); // => ?
}
};
setTimeout(object.logMessage, 1000);
按照说明的分析,既然是方法的调用,那么这里打印的是不是 object的属性呢 ?
结果很遗憾, 是window。setTimeout属于宏任务,它会等待微任务在执行栈中执行完毕再将object.logMessage
放入执行栈当中。可是此时它已经变成了函数的调用,因为object对象已经被销毁,所以是object.logMessage.call(undefined)
。
所以我们在ES6中迎来了箭头函数!
它没有this
这个隐式的参数。也就意味着,如果它会延着作用域链一直忘找 this
这个参数的存在。纯粹得太多了。善莫大焉!
总结
- 只有一种调用函数的方式,
fn.call(Object, a, b)
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 27 天, 点击查看活动详情 ”