this指向
一、es5中三种函数调用形式
func(p1, p2)
obj.child.method(p1, p2)
func.call(context, p1, p2)
这三种形式中前两种其实是第三种的语法糖,第三种形式才是正常的调用形式。所以func(p1, p2)在严格模式下等价于func.call(undefined, p1, p2),在非严格模式下等价于func.call(window, p1, p2),obj.child.method(p1, p2)等价于obj.child.method.call(obj.child, p1, p2)。
函数被独立调用
在严格模式下,this 指向 undefined,非严格模式下 this 指向 window。
// 严格模式下
"use strict"
var a = 10;
function foo() {
var a = 20;
console.log(this.a); // this -> undefined
}
foo(); // Uncaught TypeError: Cannot read property 'a' of undefined
// 非严格模式下
var a = 10;
function foo() {
var a = 20;
console.log(this.a); // this -> window
}
foo(); // 10
函数被 obj 调用
严格模式和非严格模式,this都指向 obj。
var a = 10;
var obj = {
a: 20,
fn() {
console.log(this.a);
}
}
obj.fn(); // 20 this -> obj
最后,来看一个综合例子
var age = 5;
var person = {
age : 10,
getAge: function(){
console.log(this.age)
},
setAge: {
age: 18,
getAge: function(){
console.log(this.age)
}
}
}
var bar = person.getAge
// 情况一:函数定义在对象中
person.getAge() // 等价于 person.getAge.call(person)
// 输出 10
bar() // 等价于 bar.call(window)
// 输出 5
person.setAge.getAge() // 等价于 person.setAge.getAge.call(person.setAge)
二、变量的this
全局变量的this
全局变量有:var定义的变量、未用关键字定义的变量
全局变量在严格模式下无 this 绑定,在非严格模式中被绑定到 window 对象上。
// 严格模式
"use strict";
var a = 1
console.log(this.a) // 1 this -> window
// 非严格模式
var a = 1
console.log(this.a) // 1 this -> window
局部变量的this
局部变量是定义在函数中的变量,在严格和非严格模式下都不会挂载到对象上,所以局部变量无法用 this 取到。
var a = 2
function A() {
var a=1
console.log(a) // 1
console.log(this.a) // 2 this -> window
}
A()
console.log(this) // window
console.log(this.a) // 2
let 变量的this
let变量在严格模式和非严格模式下都不会被挂载到 window 对象上。
// a 不会被挂载到window上
let a = 10;
function foo() {
var a = 20;
console.log(this.a); // this -> window
// undefined
}
console.log(this.a) // undefined
foo();
对象属性中的 this
在严格和非严格模式下,对象属性中的this指向 window,这跟变量的this指向和函数的this指向都是不一样的。
// 严格模式
"use strict"
var a = 10;
var obj = {
a: 20,
b: this.a, // this -> window
}
console.log(obj.b) // 10
// 非严格模式
var a = 10;
var obj = {
a: 20,
b: this.a, // this -> window
}
console.log(obj.b) // 10
三、this绑定原则
默认绑定
- 函数独立调用时严格模式下 this 绑定为 undefined,非严格模式下绑定为 window
setTimeOut和setInterval中的this指向,在严格和非严格模式下指向的都是全局对象。
"use strict" (or none)
var a = 10;
function foo() {
var a = 20;
setTimeout(function() {
var a = 30;
console.log( this.a ); // 10 this -> window
})
}
foo();
隐式绑定
-
调用对象函数的时候
obj.child.method(p1, p2),this 默认指向函数的上一层对象,即obj.child; -
dom事件中的 this 默认指向 dom 元素对象。
显式绑定
-
call
-
apply
-
bind
关于这三个函数的作用,在这里不做过多讲解。
new 绑定
用构造函数 new 一个实例的时候,会把实例的this自动绑定会实例,关于如何手动实现 new,可以看这里:手动实现new创建对象
上面四种绑定的优先级为: new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
四、箭头函数中的this
首先,我们要清楚一个概念,箭头函数没有自己的this、auguments、原型、构造函数,所以箭头函数是不能使用call、apply、bind绑定 this 的,但是箭头函数在运行时会向上一层查找自己需要的this、arguments等。
var a = 10;
var obj = {
a: 20,
fn: () => {
var a = 30;
console.log(this.a);
}
}
obj.fn(); // 不会调用函数 obj.fn.call(obj)
// 向 obj 上一层查找 this
// 找到 this -> window,输出 10
var a = 10;
var obj = {
a: 20,
fn: function() {
(() => {
var a = 30;
console.log( this.a );
})();
}
}
obj.fn(); // fn 不是箭头函数,隐式使用 obj.fn.call(obj)
// 执行 fn,遇到箭头函数,箭头函数无 this
// 使用上一层 obj 的 this,输出 20
有了上面两个的分析,你应该很快就能分析出下面这个例子输出什么了
var a = 10;
var obj = {
a: 20,
fn: function() {
(() => {
var a = 30;
(() => {
var a = 40;
(() => {
console.log( this.a );
})()
})();
})();
}
}
obj.fn();
答案是 20!
参考
本文完全参考下面两位大神的文章,强烈建议大家认真读一下!