参考
阮一峰
老师对于this的原理理解
一:this的定义
在严格模式下
和非严格模式下
,this
的在全局中会有差别
来自阮一峰
老师对于this的由来的理解
由于函数可以在不同的运行环境中运行,所以需要一种机制,能够在函数的内部获取当前运行环境,因此this就出现来,
this
的设计目的就是为了在函数的内部,指代函数当前的运行环境
所以在绝大多数的情况下,函数当前的运行环境
决定了this
的值
例如:
js
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // 1
foo() // 2
- 对于
obj.foo()
来说,是通过obj
这个对象来找到foo()
,所以foo()
的运行环境obj
对象,因此this
的值就是Obj对象 - 对于
foo()
来说,foo()函数的运行环境是window,因此this的值就是window
二:绑定规则
根据不同的使用场合,this
有不同的值,主要分为下面几种情况:
- 默认绑定
- 隐式绑定
- new绑定
- 显示绑定
1. 默认绑定
默认绑定的意思就是,当函数独立执行,不作为一个对象的方法调用时,
this
绑定到全局对象中,但在严格模式下,this
会绑定到undefined
js
function foo ()
{
console.log(this); // 在浏览器中通常指向 window 对象
}
foo();
2. 隐式绑定
当函数作为对象的方法调用时,
this
绑定到调用该方法的对象
js
var obj = {
foo: function () { console.log(this) },
bar: 1
};
var foo = obj.foo;
obj.foo() // 1
这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,
this指向的也只是它上一级的对象
js
var obj = {
a: {
foo: function () { console.log(this) },
},
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.a.foo()
foo执行的环境是a对象,所以this指向a对象
再例如:
js
var obj = {
a: {
bar: 1,
foo: function ()
{
console.log(this.bar); //2
console.log(this) //window
},
},
};
var bar = 2;
const fn = obj.a.foo
fn()
-
在该代码中,定义了一个变量
fn
,被将其赋值为obj.a.foo
,这意味着fn
现在引用了,obj对象中,a属性的foo方法。 -
最后调用
fn()
,执行foo()
函数 -
将
obj.a.foo
复制给fn
,只是将foo
函数的引用复制给了fn
,但并没有立即执行。所以fn
只是函数的引用,它的上下文还是跟obj.a.foo
相关 -
但是当调用
fn()
时,这才是真正执行foo
函数的时候,但由于fn
是在全局上下文中调用的,JS将函数上下文this
赋值为window
3. 显示绑定
使用
call()
、apply()
或bind()
方法显式地指定函数的this
值。
js
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var obj2 = {
bar: 100
}
obj.foo.call(obj2)
4. new 绑定
当函数用作构造函数(使用
new
关键字创建对象)时,this
绑定到新创建的对象。
js
function fn ()
{
this.bar = 1
}
var obj = new fn()
console.log(obj.bar);
- 通过
new
关键字改变了this的执行,指向了obj
当函数返回一个对象
js
function fn ()
{
this.bar = 1
return {
bar: 10
}
}
var obj = new fn()
console.log(obj.bar);
- 当函数返回一个对象时,通过new关键字将this指向改变指向返回的对象,不指向obj
当返回一些简单类型时候
js
function fn ()
{
this.bar = 1
return true
}
var obj = new fn()
console.log(obj.bar);
- this还是指向obj
返回null
js
function fn ()
{
this.bar = 1
return null
}
var obj = new fn()
console.log(obj.bar);
- 虽然null是object类型
- 但是还是指向obj
三:箭头函数
JS中箭头函数与普通函数在
this
上有着重要的不同。箭头函数
this
的绑定是在箭头函数创建的时候就确定的好的,是静态this
绑定,它没有自己的上下文,它会捕获最近的普通函数的this
值普通函数
this
值取决于,函数是如何被调用的,是根据调用方式动态
确定的
在全局上下文中
js
var a = 1
const fn = () =>
{
console.log(this.a);
}
fn()
fn
箭头函数会自动捕获最近的最近的普通函数上下文,通常是全局对象window
在对象方法中
js
var a = 10
const obj = {
a: 1,
fn: () =>
{
console.log(this.a);
}
}
obj.fn()
fn
箭头函数的this值不取决于被调用时动态绑定,而是在静态创建时候,与最近最近的普通函数上下文this
值一致fn
箭头函数最近最近的普通函数上下文是window全局- 因此
this
指向window
作为事件回调
html
<button id="btn">点击</button>
js
const btn = document.getElementById('btn')
var a = 10
const obj = {
a: 1,
fn: () =>
{
console.log(this.a); // 10
}
}
btn.addEventListener('click', obj.fn)
- 点击按钮输出还是10
- 箭头函数作为回调函数时,其
this
绑定通常与定义它的上下文相同。
四:优先级
1. 隐式绑定 VS 显示绑定
js
function foo() {
console.log( this.a );
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2
- 显示绑定优先级要高于隐式绑定
2. new绑定 VS 显示绑定
js
function Person(name) {
this.name = name;
}
const alice = new Person("Alice");
const person = { name: "Bob" };
const boundGreet = greet.bind(person);
const aliceWithBinding = new boundGreet(); // 使用 new 绑定,this 绑定到新对象 aliceWithBinding
console.log(aliceWithBinding.name); // 输出: undefined,因为 new 绑定覆盖了显式绑定
new
绑定的优先级更高。当使用new
关键字创建对象实例时,它会覆盖之前的显式绑定