这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战
This 原理
JS 之所以有this的设计取决于内存的数据结构
-
对象
let obj = {foo: 5}上文是将一个对象赋值给
obj。JS 引擎会先生成对象,再将对象内存地址赋值给obj。即实际是以这样的形式保存:foo属性值保存在value属性。 -
函数
问题在于属性值是函数时:
let obj = { foo: function () {} }这时,JS 会将函数单独保存在内存,再将函数地址赋值给
foo的value属性。由于函数可以在不同环境上下文执行:
let f = function () {}; let obj = { f: f }; // 单独执行 f(); // obj 环境执行 obj.f(); -
环境变量:
JS 允许函数体内部引用当前环境其他变量:
let f = function () { console.log(x); // x 由运行环境提供 }问题来了,由于函数可在不同环境执行。所以需要一种机制:在函数体内获得当前运行环境(context)。因此,This 出现了,其设计目的就是在函数内部指代函数当前运行环境。
let f = function () { console.log(this.x); // 指当前运行环境的`x` }再有:
let f = function () { console.log(this.x); } let x = 1; let obj = { f: f, x: 2, } // 单独执行 f() // 1 // obj 环境 obj.f() // 2上面代码中:函数
f全局环境执行,this.x指向全局环境x:obj环境执行,指向obj.x:所以为什么
let foo = obj.foo,foo就变成在全局环境执行:let obj = { foo: function () { console.log(this.bar) }, bar: 1 } let foo = obj.foo; let bar = 2; obj.foo() // 1 foo() // 2因为
obj.foo()是通过obj找到foo,所以在obj环境执行;一旦let foo = obj.foo,变量foo直接指向函数本身,foo()变成在全局环境执行。
this 与 function
函数执行时,this 关键字并不会指向正在运行的函数本身,而是指向调用该函数的对象。所以,如果你想在函数内部获取函数自身的引用,只能使用函数名或者使用arguments.callee属性(严格模式下不可用),如果该函数是一个匿名函数,则你只能使用后者。
this 与箭头函数
如定义为箭头函数,this 值与定义时环境无关。
this 与 Class
原型和静态方法(static)的 this
调用原型或静态方法时没指定 this 的值时,方法内的 this 将为undefined:
class Animal {
speak() { return this }
static eat() { return this }
}
let obj = new Animal();
// new-会绑定this
obj.speak(); // Animal{}
Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined
如果静态方法包含this关键字,这个this指的是类,而不是实例。
new
new关键字进行如下操作:
- 创建新对象
{} - 为
{}添加__proto__属性指向构造函数的原型对象(.prototype) - 将该对象的 this 指向构造函数并传参调用构造函数
- 该函数没有返回对象就返回
this
实例属性
实例属性需定义在在类的方法里:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
静态或原型的属性需定义在类定义外面
案例
-
闭包与 for 循环
for (var i = 0; i < 5; i++) { (function fn(i) { setTimeout(function() { console.log(i) }, 0) })(i) } // 0 1 2 3 4 for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i) }, 0) } // 5 5 5 5 5
参考链接
JavaScript 的 this 原理 - 阮一峰的网络日志
\