写点什么

深入理解 JS 中的 this

用户头像
Verlime
关注
发布于: 2020 年 07 月 22 日
深入理解 JS 中的 this

在 JavaScript 中 this 是在函数运行时内部自动创建的一个对象,作为执行上下文的一个重要属性,它会随着执行环境的变化而改变。



那么首先来看一下 this 的几种用法。



this 的几种用法

单独使用,this 表示全局对象

console.log(this) // 指向 window



在函数中使用,this 表示全局对象

function foo() {
console.log(this) // 指向 window
}



在对象方法中使用,this 表示该方法所属的对象

var obj = {
a: 1,
foo: function () {
console.log(this.a); // 1
}
}



在构造函数中使用,this 指向实例化后的新对象

function Foo(name) {
this.name = name;
}
Foo.prototype.say = function () {
console.log(this.name);
}
var foo = new Foo('alan');
foo.say(); // alan



在事件方法中使用,this 指向接收事件的元素

el.addEventListener('click', function () {
console.log(this); // 指向 el 元素
})



通过 call 或 apply 改变 this 指向,指向传入的第一个参数

var obj = {
a: 1,
foo: function (params) {
console.log(this.a, params);
}
}
var bar = {a: 2};
obj.foo.call(bar, 'baz'); // 2, baz
obj.foo.apply(bar, ['baz']); // 2, baz



this 的实现原理

接下来再看一个例子:

var a = 2;
var obj = {
a: 1,
foo: function () {
console.log(this.a);
}
}
obj.foo(); // 1
var bar = obj.foo;
bar(); // 2



此时我们发现 obj.foo() 和把 obj.foo 赋给一个新变量后的执行结果并不一样,我们知道是 this 的指向发生了改变,但是发生改变的原因是什么呢?这个可以从 JS 在内存中的存储结构说起。



来看一下上面代码中对象 obj 的存储结构:





a 没什么可说的,作为基本类型直接存储在栈内存中,而函数 foo 作为一个引用类型,在栈空间中存储的只是一个指针,指向了堆空间实际的存储地址。



那么函数作为一个单独的值,可以在不同的上下文中执行,引用阮老师的话就是:



由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this 就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。



因此,当执行 obj.foo() 时,函数 foo 是在 obj 环境中运行的,即 this.a 等价于 obj.a



而当 obj.foo复制给 bar 时,对象 obj 及变量 bar 的内存结构是这样的:





从图中可以看出,变量 bar 和 obj.foo 指向的是同一个地址,但是它们的执行环境却不同,当 bar 执行时,bar 的执行环境是在全局上下文中,因此输出 a 的值为 2



相关参考:



Photo by Pete Walls on Unsplash

发布于: 2020 年 07 月 22 日阅读数: 55
用户头像

Verlime

关注

一个前端工程师 2018.01.01 加入

每天进步一点点ヽ(•̀ω•́ )ゝ

评论

发布
暂无评论
深入理解 JS 中的 this