这个问题属于老生常谈了,我们都知道作为对象方法调用时,方法内的this指向这个对象,用一句话来形容就是
谁调用了函数,
this就指向谁。
this 指向
这里列出this指向的 4 种情况
- 作为对象的方法调用
- 作为普通函数调用
- 构造器调用
- call 或 apply
作为对象的方法调用
就是一开始提到的,this指向该对象。
1 | var obj = { |
getA函数作为obj对象的方法调用,函数内的this就是指向这个对象obj。
有一个很常见的场景就是dom绑定事件,比如:
1 | document.querySelector('#id').onclick = function () { |
this肯定指向document.querySelector('#id')这个对象,因为onclick函数是作为该对象的方法调用的。
作为普通函数调用
同样是上面的代码,将getA单独进行调用
1 | var bar = obj.getA |
当函数不作为对象的属性被调用,函数内的this指向全局对象。
在 ECMAScript 5 的
strict模式下this不再指向全局对象,而是undefined。这是否意味着之前指向全局是错误的?
构造器调用
1 | function Person(name) { |
在写面向对象的 JavaScript 时用到的构造函数,我们都知道构造函数和原型方法中的this是指向返回的对象的。上面使用new返回了ltaoo这个对象,所以this.name = name就是ltaoo.name = name。
call 和 apply
即显式指定this值。
1 | var obj1 = { |
为什么
上面的知识点随便都能查到,但也仅限于此,为什么this在不同情况下是不同的值呢?
作为普通函数调用
1 | function foo() { |
如果是在浏览器环境下,并且是非严格模式,肯定打印出window,有人说是因为foo在全局环境下声明,所以是挂载在全局对象下,所以实际上是这样的window.foo(),这和我们之前的知识点也是吻合的:
谁调用函数,
this就指向谁。
1 | function foo () { |
答案还是window,但此时window上有bar吗,很明显没有,所以上面的说法不正确。
再来看一个例子:
1 | var name = 'hello' |
OK,毫无疑问是吧,如果修改成这样呢?
1 | ;(0 || obj.getName)() |
分析一下,首先是执行(0 || obj.getName),所以返回obj.getName这个函数,再调用这个函数,结果是hello。
当然这里似乎是符合了作为普通函数调用的规则。
引用类型
从这里开始就很虚幻了,引用类型可以表示为拥有两个属性的对象:
1 | var referenceType = { |
那这个引用类型在哪里出现,从哪里获取值(base 和 propertyName 的值)呢?
有两种情况
- 当处理一个标识符时
- 处理属性访问器时
处理标识符
首先明确标识符是什么,标识符是变量名、函数名、函数参数名和全局对象中未识别的属性名。
1 | var foo = 10 |
这样就有了一个referenceType:
1 | fooReferenceType = { |
当在使用foo这个变量时,内部会去查找该变量对应的值,类似这样:
1 | function getValue (value) { |
属性访问器
1 | foo.bar() |
referenceType 到 this
那到底怎么样从referenceType可以推出this值呢?
在一个函数上下文中,
this由调用者提供,由调用函数的方式来决定。如果调用括号()左边是引用类型的值,this将设为引用类型值的base对象,在其他情况下,这个值为null。当this值为null时,会被转换为全局对象。
OK,这是整篇笔记的核心,来举个例子:
1 | function foo() { |
看到函数名,意识到这是一个标识符,所以有referenceType为:
1 | fooReferenceType = { |
怎么说明这不是第二种情况,即()坐标不是引用类型的值呢?
1 | ;(0 || foo)() |
这种情况上面也提到了,先执行(0 || foo),这时已经遇到foo标识符,所以先getValue了,再()调用,就符合了第二种情况,()左边不是引用类型值,所以this为null,但是会被转换为全局对象。
1 | function foo () { |
两次打印都是undefined ? 为什么,头疼。