这个问题属于老生常谈了,我们都知道作为对象方法调用时,方法内的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
? 为什么,头疼。