js中的this绑定规则
JavaScript中的this
关键字用于指向当前函数的执行上下文(execution context
)。
不同于作用域是编译时确定的,this
的值在运行时确定,并且取决于函数的调用方式。JavaScript中this
的绑定规则比较复杂,以下是常见的有四种情况:
1. 独立调用(默认规则)
const foo = function (){
console.log(this)
}
foo() // window (node环境下为global对象)
独立调用时,this默认指向全局 在浏览器环境下全局指向window,而node环境下全局指向global
2. 隐式绑定(对象调用)
const obj = {
name:'张三',
foo:function (){
console.log(this.name)
}
}
obj.foo() // 张三 foo函数内的this会被绑定到obj对象
隐式绑定发生在对象调用方法的方式,也是比较常见的this绑定方式
3.显式绑定(call apply bind)
const obj = {
name:'asd',
age:10
}
const fn = function (num1,num2){
console.log(this.age,num1 + num2)
}
fn.call(obj,10,20) // 10 30
fn.apply(obj,[10,20]) // 10 30
// bind会返回绑定好this的函数,而不是直接调用,后续需要手动调用
fn.bind(obj)(10,20) // 10 30
显式绑定是通过call,apply,bind方法,将函数内部的this绑定到传入的对象上
4.new绑定 (通过new构造函数绑定this)
function Person(name,age){
this.name = name
this.age = age
}
//`new`关键字调用了`Person`函数,从而创建了一个新的对象,并将`this`绑定到了该对象上
const p1 = new Person('张三',16)
console.log(p1.name,p1.name) // '张三' 16
new 绑定是在执行构造函数时,将构造函数内的this绑定到要构建的实例对象上
优先级:
new绑定 > 显式绑定 > 隐式绑定 > 默认规则
其中new绑定与call,apply同时使用无意义,但可以与bind,new优先级依旧最高
function foo() {
console.log(this); // foo {} foo函数创建的实例对象
}
const bar = foo.bind("asd");
const obj = new bar();
特殊情况
1. 箭头函数
箭头中的this默认指向父级执行上下文,call,apply,bind无法改变箭头函数的this指向
const obj = {
name: "obj",
foo1: () => {
console.log(this);
},
foo2: function () {
console.log(this);
},
};
const temp = {
name: "temp",
};
//需要注意对象定义时的大括号不构成块级作用域,所以此处的剪头函数的this指向其父级执行上下文
obj.foo1(); // window
obj.foo1.call(temp); // window
obj.foo2(); // obj
obj.foo2.call(temp); //temp
tips: 有个容易混淆的地方,对象定义中的箭头函数foo,其this
(执行上下文)指向对象定义时的父级执行上下文(对象的大括号不构成块级作用域)
window.name = 'window'
const obj = {
name:'obj',
foo:() =>{
console.log(this.name)
},
fn:function(){
console.log(this.name)
}
}
obj.foo() // window 箭头函数作用域指向父级执行上下文
obj.fn() // obj
2. setTimeout
setTimeout
函数由浏览器内部Chromium
实现,源码采用apply
显式形式将this
绑定到Window
上
fakeWin.setTimeout = function(fn, time) {
fakeWin.setTimeout.called = true;
fakeWin.setTimeout.that=this;
if (typeof fn === 'string') f
eval(fn);
}else{
fn.apply(this, Array.prototype.slice.call(arguments, 2)) ;
}
}
传入函数内的执行上下文(即 this
值)取决于你如何调用该函数,了解了绑定原理,让我们结合this
规则来判断this
的值
setTimeout(function(e){
console.log(this) // window (this已被绑定到window)
},100)
setTimeout(() =>{
console.log(this) // window (箭头函数无法被apply改变this指向,其内部又不存在this,所以去上层作用域找,找到全局也就是window)
},100)
// 传入普通函数时默认指向window
const obj = {
fn:function(){
setTimeout(function(){
console.log(this)
},1000)
}
}
obj.fn() // window(this已被绑定到window,隐式绑定权重小于显示绑定)
//传入箭头函数时指向父级执行上下文
const obj = {
name:'obj'
fn:function(){
setTimeout(() =>{
console.log(this) // 箭头函数无法被apply改变this指向
},1000)
}
}
obj.fn() // {name:'obj',fn: ƒ}
// tip:
// 最后一行fn函数中的this被隐式绑定到了obj对象上,而箭头函数中不存在this,会去上层作用域找(fn函数中的this),此时this也就等于obj对象
3. DOM事件对象
DOM对象在绑定事件监听器时
传入普通函数 内部的的this
指向触发该事件的DOM元素
传入钩子函数 内部的this
指向定义该箭头函数的父级执行上下文
<button id="myButton">点击我</button>
const myButton = document.getElementById('myButton');
// 普通函数
myButton.addEventListener('click', function(event) {
// 在这里,`this`将指向 `myButton` 元素
console.log(this); // 输出:<button id="myButton">点击我</button>
});
//箭头函数
//指向定义该箭头函数的父级执行上下文
myButton.addEventListener('click', (event)=>{
// 在这里,`this`将指向父级执行上下文
console.log(this); // 输出:window
});