「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」
简介
在JS中this
的绑定规则有默认绑定
、隐式绑定
、显示绑定
、new绑定
四种。绑定的优先级依次是 new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定
,下面我们来一一探讨。
文章里的每个案例都是我亲自编写并验证的,建议阅读文章时,可以在浏览器执行案例,会更有利于理解。
默认绑定
默认绑定,在不能应用其它绑定规则时使用的默认规则,通常是使用全局变量或者独立函数调用。
- 全局环境下,this始终指向全局对象(window),无论是否严格模式。
- 对于延时函数内部的回调函数的this指向全局对象window,无论是否严格模式。
- 普通函数内部的this分两种情况,严格模式和非严格模式。非严格模式下,this 默认指向全局对象window。严格模式下,this指向undefined。
- 箭头函数下,this始终指向全局对象(window),无论是否严格模式。
console.log("全局环境下的this: ", this); //window
setTimeout(function () {
console.log(this); //window
});
setTimeout(() => {
console.log(this); //window
});
function f1() {
console.log("方法下的this: ", this); //window
}
f1();
function f2() {
"use strict";
console.log("严格模式下方法下的this: ", this); // undefined
}
f2();
const f3 = () => {
console.log("箭头函数方法下的this: ", this); //window
};
f3();
const f4 = () => {
"use strict";
console.log("严格模式下箭头函数方法下的this: ", this); //window
};
f4();
复制代码
隐式绑定
在隐式绑定中通常函数作为对象的方法被调用。
- 当函数作为对象里的方法被调用时,它们的
this
是调用该函数的对象。 - 多层嵌套的对象,内部方法的
this
指向离被调用函数最近的对象。
const obj1 = {
name: "randy1",
say() {
return this.name;
},
b: {
name: "randy2",
say() {
return this.name;
},
},
};
console.log(obj1.name); // randy1
console.log(obj1.b.name); // randy2
复制代码
显示绑定
显式绑定比较好理解,就是通过call、apply、bind的方式,显式的修改this所指向的对象。
apply(this, [args])
、call(this, args)
、bind(this, args)()
这三者的区别是apply参数列表是数组,bind需要再次调用。- 如果
call
、apply
或者bind
传入的第一个参数值是undefined
或者null
,严格模式下this
的值为传入的值null /undefined
。非严格模式下,实际应用的默认绑定规则,this
指向全局对象(node环境为global,浏览器环境为window)。
var name = "global name";
function say(age) {
console.log(`${this.name}今年${age}啦!`);
}
const user = { name: "randy" };
const user2 = { name: "randy2" };
say(24); // global name今年24啦!
say.apply(user, [25]); // randy今年25啦!
say.apply(user2, [26]); // randy2今年26啦!
say.call(user, 25); // randy今年25啦!
say.call(user2, 26); // randy2今年26啦!
say.bind(user, 25)(); // randy今年25啦!
say.bind(user2, 26)(); // randy2今年26啦!
say.apply(null, [25]); // global name今年25啦!
say.apply(undefined, [26]); // global name今年26啦!
复制代码
new 绑定
- 当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。
- 构造器返回的默认值是this所指的那个对象,也可以手动返回其他的新对象。
- 如果构造函数返回了一个新的非空对象,则this指向新对象,否则指向我们创建的对象。
function People() {
this.name = "randy";
}
const p1 = new People();
console.log(p1.name); // randy
function People2() {
this.name = "randy";
return { name: "demi" }; //手动设置返回{ name: 'demi' }新对象
}
const p2 = new People2();
console.log(p2.name); // demi
复制代码
扩展
事件中的this
事件中的this指向又有所区别,e.target
指向触发事件的元素,e.currentTarget
指向绑定事件的元素,下面我用例子说明
<div onclick="click1(event)" id="div1">
<button>事件中的this</button>
</div>
复制代码
function click1(e) {
// 触发事件的元素
console.log(e.target); // <button>事件中的this</button>
// 绑定事件的元素
console.log(e.currentTarget); // <div onclick="click1(event)" id="div1"><button>事件中的this</button></div>
console.log(this); // window
}
const div1 = document.getElementById("div1");
div1.addEventListener("click", function (e) {
// 触发事件的元素
console.log(e.target); // <button>事件中的this</button>
// 绑定事件的元素
console.log(e.currentTarget); // <div onclick="click1(event)" id="div1"><button>事件中的this</button></div>
// 绑定事件的元素
console.log(this); // <div onclick="click1(event)" id="div1"><button>事件中的this</button></div>
console.log(this === e.currentTarget); // true
});
复制代码
箭头函数中的this
- 箭头函数的
this
是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this
就继承了定义函数的对象。 - 箭头函数中的
this
只取决包裹箭头函数的第一个普通函数的this
,否则应用的是默认绑定规则。 - 箭头函数不能通过
apply call bind
改变this
。 - 箭头函数不能使用
arguments
。得使用reset
参数 - 箭头函数不能用于构造函数。
- 不可以使用
yield
命令,因此箭头函数不能用作Generator
函数。
下面我将举例重点说明下箭头函数的绑定问题,其他特性就不详细举例了,小伙伴们可以自行测试。
const obj = {
hi: function () {
console.log(this);
return () => {
console.log(this);
};
},
sayHi: function () {
return function () {
console.log(this);
return () => {
console.log(this);
};
};
},
say: () => {
console.log(this);
},
};
let hi = obj.hi(); //输出obj对象
hi(); //输出obj对象
let sayHi = obj.sayHi();
let fun1 = sayHi(); //输出window
fun1(); //输出window
obj.say(); //输出window
复制代码
下面我们来分析下。
obj.hi()
应用隐式绑定规则,this就是obj对象,所以输出obj。hi()
箭头函数里再输出this,应用我们上面说的特性,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this,所以也输出obj对象。obj.sayHi()
返回一个普通的函数。sayHi()
就是调用刚返回的普通函数,应用默认绑定规则,返回window对象。fun1()
箭头函数里再输出this,应用我们上面说的特性,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this,所以也输出window对象。obj.say()
箭头函数里再输出this,应用我们上面说的特性,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this,如果没有被普通函数包裹,就会应用默认绑定规则所以输出window对象。
如何准确判断this
说了这么多,那我们到底如何来判断this指向问题呢?
- 函数是否在 new 中调用(new 绑定),如果是,那么 this 绑定的是新创建的对象。
- 函数是否通过 call,apply 调用,或者使用了 bind(即硬绑定),如果是,那么 this 绑定的就是指定的对象。
- 函数是否在某个上下文对象中调用(隐式绑定),如果是的话,this 绑定的是那个上下文对象。
- 如果以上都不是,那么使用默认绑定。如果在严格模式下,则绑定到 undefined,否则绑定到全局对象。
- 如果把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
- 如果是箭头函数,箭头函数的 this 只取决包裹箭头函数的第一个普通函数的 this。如果没有被普通函数包裹实际应用的是默认绑定规则。
系列文章
后记
本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~~