抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

参考阮一峰老师对于this的原理理解

一:this的定义

严格模式下非严格模式下this的在全局中会有差别

来自阮一峰老师对于this的由来的理解

由于函数可以在不同的运行环境中运行,所以需要一种机制,能够在函数的内部获取当前运行环境,因此this就出现来,this的设计目的就是为了在函数的内部,指代函数当前的运行环境

所以在绝大多数的情况下,函数当前的运行环境决定了this的值

例如:

1
2
3
4
5
6
7
8
9
10
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
  • image-20230911205503092

二:绑定规则

根据不同的使用场合,this有不同的值,主要分为下面几种情况:

  • 默认绑定
  • 隐式绑定
  • new绑定
  • 显示绑定

1. 默认绑定

默认绑定的意思就是,当函数独立执行,不作为一个对象的方法调用时,this绑定到全局对象中,但在严格模式下,this会绑定到undefined

1
2
3
4
5
6
function foo ()
{
console.log(this); // 在浏览器中通常指向 window 对象
}

foo();

2. 隐式绑定

当函数作为对象的方法调用时,this 绑定到调用该方法的对象

1
2
3
4
5
6
7
8
var obj = {
foo: function () { console.log(this) },
bar: 1
};

var foo = obj.foo;

obj.foo() // 1

这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
a: {
foo: function () { console.log(this) },
},
bar: 1
};


var foo = obj.foo;
var bar = 2;

obj.a.foo()

foo执行的环境是a对象,所以this指向a对象

image-20230911210131323

再例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 值。

1
2
3
4
5
6
7
8
9
10
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};

var obj2 = {
bar: 100
}

obj.foo.call(obj2)

image-20230911212217170

4. new 绑定

当函数用作构造函数(使用 new 关键字创建对象)时,this 绑定到新创建的对象。

1
2
3
4
5
6
7
function fn ()
{
this.bar = 1
}

var obj = new fn()
console.log(obj.bar);
  • 通过new关键字改变了this的执行,指向了obj

当函数返回一个对象

1
2
3
4
5
6
7
8
9
10
function fn ()
{
this.bar = 1
return {
bar: 10
}
}

var obj = new fn()
console.log(obj.bar);
  • 当函数返回一个对象时,通过new关键字将this指向改变指向返回的对象,不指向obj
    • image-20230911212816249

当返回一些简单类型时候

1
2
3
4
5
6
7
8
function fn ()
{
this.bar = 1
return true
}

var obj = new fn()
console.log(obj.bar);
  • this还是指向obj

返回null

1
2
3
4
5
6
7
8
function fn ()
{
this.bar = 1
return null
}

var obj = new fn()
console.log(obj.bar);
  • 虽然null是object类型
  • 但是还是指向obj

三:箭头函数

JS中箭头函数与普通函数在this上有着重要的不同。

箭头函数this的绑定是在箭头函数创建的时候就确定的好的,是静态this绑定,它没有自己的上下文,它会捕获最近的普通函数的this

普通函数this值取决于,函数是如何被调用的,是根据调用方式动态确定的

在全局上下文中

1
2
3
4
5
6
var a = 1
const fn = () =>
{
console.log(this.a);
}
fn()

image-20230911214426329

  • fn箭头函数会自动捕获最近的最近的普通函数上下文,通常是全局对象window

在对象方法中

1
2
3
4
5
6
7
8
9
var a = 10
const obj = {
a: 1,
fn: () =>
{
console.log(this.a);
}
}
obj.fn()

image-20230911214641724

  • fn箭头函数的this值不取决于被调用时动态绑定,而是在静态创建时候,与最近最近的普通函数上下文this值一致
  • fn箭头函数最近最近的普通函数上下文是window全局
  • 因此this指向window

作为事件回调

1
<button id="btn">点击</button>
1
2
3
4
5
6
7
8
9
10
11

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 显示绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 显示绑定

1
2
3
4
5
6
7
8
9
10
11
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 关键字创建对象实例时,它会覆盖之前的显式绑定

评论