前文
来吧来吧,又要长脑子了,关于this指向那些不得不知的秘密,下面就让我们一一揭晓~
this绑定规则
首先我们要清楚:JS 中的 this 代表的是当前行为执行的主体
下面都是普通函数的this绑定规则,加粗的是重点~
- this指向的是函数运行时所在的对象;
- 全局环境this相当于window; 若没有调用函数则指向全局
- 嵌套函数的 this取决于最里一层的调用方式
- 使用call、apply、bind绑定的,this指向绑定的对象。
- 方法调用的时候,没有任何前缀,则指向window,例如
function fn(){console.log(this)}; fn()
- new构造函数,指向new出来的对象;
- 以下情况,this 会 100% 指向 window:
- 立即执行函数(IIFE)
- setTimeout 中传入的非箭头函数、setInterval 中传入的非箭头函数
箭头函数:
- 箭头函数指向外层引用的this(这个外层指的是非箭头函数)
- 箭头函数指向在定义的时候就是固定好的,不可以修改,call、apply也不行
const name = 2;
const p1 = {
name:'inner',
foo:()=>{console.log(this.name)}
}
p1.foo()
- 这里指向的就是外层this,就是p1的this,所以就是p1.foo()的结果是2
具体案例
1、方法调用 & 函数调用
const obj = {
name: 'Alice',
greet: function() {
console.log(this.name);
}
};
obj.greet(); // 1)输出什么?
const greetFunc = obj.greet;
greetFunc(); // 2)输出什么?
输出结果:
1)输出:‘Alice’,方法调用,this 指向调用该方法的对象 obj。
2)输出:undefined,函数调用,this 默认指向全局对象或 undefined。
2、多层嵌套
const obj = {
name: 'Bob',
outer: function() {
function inner() {
console.log(this.name);
}
inner();
}
};
obj.outer(); // 输出什么?
输出结果: undefined
inner 是普通函数,直接调用时 this 指向全局对象或 undefined,而非 obj。
3、箭头函数的多层嵌套
- 指向的是最近一层的普通函数的this
const obj = {
name: 'Charlie',
outer: function() {
const inner = () => {
console.log(this.name);
};
inner();
}
};
obj.outer();
输出结果: ‘Charlie’
- 箭头函数的 this ,定义时候就决定了,是继承自外层作用域(outer 的 this),而 outer 是方法调用,this 指向 obj。
- 如果
outer
也是箭头函数,指向的就是全局啦,结果也就变成undefined了
4、回调函数中的 this
const obj = {
name: 'David',
handleClick: function() {
setTimeout(function() {
console.log(this.name);
}, 100);
}
};
obj.handleClick();
输出结果: undefined
setTimeout 的回调函数是普通函数,this 默认指向全局对象或 undefined。
修复方法:定时器使用箭头函数或绑定 this:
setTimeout(() => {
console.log(this.name); // 输出 'David'
}, 100);
5:构造函数中的 this
function Person(name) {
this.name = name;
this.greet = function () {
console.log(this.name);
};
}
const alice = new Person("Alice");
alice.greet(); // 1)输出什么?
const greetFunc = alice.greet;
greetFunc(); // 2)输出什么?
结果输出:
1)输出:‘Alice’,方法调用,this 指向实例 alice。
2)输出:undefined, 函数调用,this 默认指向全局对象或 undefined。
6:多层 call 绑定
const obj1 = { name: "Frank" };
const obj2 = { name: "Grace" };
function logName() {
console.log(this.name);
}
logName.call(obj1).call(obj2); // 输出什么?
结果输出: ‘Frank’
logName.call(obj1)
立即执行,输出 ‘Frank’。 由于 logName 没有返回值,后续的 .call(obj2) 会报错(实际题目可能有陷阱)。
7:事件监听中的 this
<button id="btn">Click Me</button>
<script>
const button = document.getElementById('btn');
button.addEventListener('click', function() {
console.log(this); // 输出什么?
});
</script>
结果输出: <button id="btn">
事件监听回调中,this 指向触发事件的 DOM 元素(即按钮本身)。
8:箭头函数&普通函数&&call大汇总
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => {
console.log(this.name)
},
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
// 正题来咯
person1.foo1(); // 1)输出什么?
person1.foo1.call(person2); // 2)输出什么?
// 箭头函数person1的this
person1.foo2(); // 3)输出什么?
person1.foo2.call(person2); // 4)输出什么?
// 多个嵌套函数,则是指向最里一层调用
person1.foo3()(); // 5)输出什么?
person1.foo3.call(person2)(); // 6)输出什么?
person1.foo3().call(person2); // 7)输出什么?
// 箭头函数的this取值就是定义时候所在的对象
person1.foo4()(); // 8)输出什么?
person1.foo4.call(person2)(); // 9)输出什么?
person1.foo4().call(person2); // 10)输出什么?
结果输出:
- 分析1\2,属于普通方法调用,所以this指向调用者,以及call绑定者,所以1)是
person1
,2)是person2
- 分析3\4,属于箭头函数调用,定义的时候就已经确定了,this指向的是全局,故3和4都是
window
- 分析5\6\7,属于嵌套函数的调用,指向的是最里层的this,除了7改变了最里层的this以外,其他都属于无效操作,指向还是全局,故结果为5):
window
, 6):window
, 7):person2
- 分析8\9\10,属于箭头嵌套函数的调用,this指向的始终是
foo4
的this,故结果为8):person1
, 而9)改变了foo4的指向所以结果为:person2
, 但是10)改的是箭头函数的this属于无效改动:所以结果还是10)person1
总结
做了那么几道题,有点意思了吧,要还是不会的话评论一下,我再找找题~
所有的this指向基本就是如下几个规则
- 默认绑定:函数直接调用时,this 指向全局对象(非严格模式)或 undefined(严格模式)。
- 隐式绑定:方法调用时,this 指向调用该方法的对象。
- 显式绑定:通过 call、apply、bind 显式指定 this。
- new 绑定:构造函数中,this 指向新创建的实例。
- 箭头函数:继承外层作用域的 this,无法通过调用方式改变。
默认绑定、隐式绑定、显示绑定、new 绑定。优先级从低到高。