this
本文最后更新于:1 年前
this
根据函数的调用方式的不同,this 会指向不同的对象:
这些this的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this的指向不同,一般指向我们的调用者。
调用方式 | this指向 |
---|---|
普通函数调用 | window |
定时器函数 | window |
立即执行函数 | window |
构造函数调用 | 实例对象 原型对象里面的方法也指向实例对象 |
对象方法的调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
- 全局this -> window
- 构造函数的this指向实例化对象
- 预编译函数this -> window
- bind/call/apply 改变this指向,this 指向指定的那个对象
- 箭头函数没有 this,它的 this 只取决于包裹箭头函数的第一个普通函数的 this
- 以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。
- 以事件绑定函数的形式调用时,this 指向绑定事件的对象
首先来看几个使用场景:
1 |
|
下面对这几种用法进行分析:
直接调用 foo(称为默认绑定)不管 foo 函数放在哪里,this 都是全局对象(window、global)
对于 obj.foo 这种用法(称为隐式绑定)只需要记住,谁调用了函数,this 就指向谁
对于 new 的方式来说,this 永远被绑定在了实例上,任何方式都无法改变 this
函数内部的this
普通函数(未实例化)内部的this默认指向window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function test(b) {
this.d = 3 //window.d=3
let a = 1
function c() {}
}
test(123)
console.log(d) //3
console.log(this.d) //3
console.log(window.d) //3
// AO = {
// arguments: [123],
// this:window
// b:undefined
// 123
// a:undefined
// c:function c() {}
// }构造函数内部的this
构造函数实例化之后this指向实例化对象
对于 new 的方式来说,this 永远被绑定在了实例上,任何方式都无法改变 this预编译函数this
预编译函数this指向window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function Test() {
//new的时候构造函数内部产生成this,然后生成__proto__被赋值构造函数的prototype,生成原型链
// let this = {
// __proto__: Test.prototype
// }
this.name = '123'
}
let test = new Test()
// new 之前this指向window
// AO = {
// this: window
// }
// new 之后this接收属性和原型对象,生成原型链
// AO = {
// this:{
// name:'123', //接收属性
// __proto__: Test.prototype //生成原型链
// }
// }
//new的时候产生GO
// GO = {
// Test: function test() {...}
// test:{ //new 之后
// name:'123', //接收属性
// __proto__: Test.prototype //生成原型链
// }
// }bind/call/apply
bind/call/apply调用时this 指向指定的那个对象
bind 和 call/apply 的异同如下:
相同:都会修改函数中的 this,并将 this 指向传入的对象
不同
- bind:修改 this 后,返回一个硬编码的新函数,需要手动执行新函数
- call/apply:不返回新函数,修改 this 后会立即执行原函数
call和apply传递的参数不一样,call传递参数arg1,arg2…形式,apply必须数组形式[arg]
看明白下面的代码,bind 和 call/apply 的区别就清楚了:
1 |
|
apply 方法的第一个参数是作为函数上下文的对象,此时函数里的 this 便指向了构造函数Person。
所以实例对象p可以借用构造函数Person里边的属性
1 |
|
1 |
|
对于 bind / call / apply,如果第一个参数为 null、undefined 或 不传,那么就会应用默认绑定规则,也就是函数中的 this 会泄漏到全局对象(window, global)上。
为了防止这个副作用,可以通过传一个空对象 ∅ = Object.create(null) 来保护全局对象。
箭头函数的 this
首先要知道,箭头函数没有 this,它的 this 只取决于包裹箭头函数的第一个普通函数的 this。举个例子:
1 |
|
这个例子中,由于包裹箭头函数的第一个普通函数是 foo,所以很显然结果为 window。
另外,对箭头函数使用 call、apply、bind 这类函数是无效的(箭头函数没有 this 嘛,因此绑定上下文的函数无法生效)。
箭头函数中,this是静态的,this始终指向函数声明时所在作用于下的this的值
其他方面的 this
隐式绑定中 this 丢失
给隐式绑定起别名后,直接调用别名就会出现 this 丢失的问题
1 |
|
对象内部函数嵌套函数 this 丢失
1 |
|
teamSummary函数里面又嵌了个函数,这导致内部的this的指向发生了错乱。
那如何修改:
方法一、let self = this
1 |
|
方法二、bind函数
1 |
|
方法三、 箭头函数
1 |
|
隐式绑定中多次调用
隐式绑定多次调用时,this 的指向只取决于最后一次
1 |
|
严格模式下的 this
严格模式主要影响 this 的默认绑定规则,但是不影响函数调用中的 this。来看下面的代码:
1 |
|
1 |
|
通常来说,不应该在代码中混用 strict 模式和非 strict 模式。上述情况主要出现在调用第三方库时,其严格程度和你的代码可能有所不同,因此需要注意此类兼容细节。
this 总结
- 当多个规则同时出现,就需要根据优先级来决定 this 最终指向哪里。优先级情况如下:new 关键字 > bind 这类函数(显示绑定) > obj.foo() 这种方式(隐式绑定) > foo() 直接调用
- 箭头函数没有自己的 this,仅继承外层函数的,因此无法通过 bind/call/apply 修改。
- 隐式绑定中,要注意 this 丢失的问题
- 隐式绑定中,多次调用时 this 只取决于最后一次
- 严格模式会使 this 的默认绑定失效,但不影响函数调用中的 this
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!