文章目录
前言
我只想说,JS也太难懂了。
参考文章:https://juejin.im/post/5b6676e6f265da0fa00a3a12
类别
1 全局环境中的“this”
在全局环境中, this相当于全局对象 global。但这只在 node中才有效。
原因是在JavaScript文件中, this等同于 module.exports而不是 global
2 函数中的“this”
函数中的 this值通常是由函数的调用方来定义。因此,每次执行函数,函数内的 this值可能都不一样。(严格模式产生影响)
如果我们使用 node运行下面的代码,得到的输出为 true 。如果在文件的顶部添加 “use strict”(严格模式),并再次运行它,将会得到一个 false输出,因为现在的 this值为 undefined 。
function rajat() {
console.log(this === global)
}
rajat()
3 构造函数中的“this”
JavaScript没有特定的构造函数。我们所能做的就是使用 new运算符将函数调用转换为构造函数调用,如上一节所示。
构造函数被调用时,会创建一个新对象并将其设置为函数的 this参数。构造函数会隐式的返回这个对象,除非我们明确的返回了另外一个对象。
return语句不会覆盖构造函数调用的唯一情形是,return语句返回的不是一个对象。在这种情况下,对象将包含原始值。
4 方法中的“this”
A 当函数作为对象的方法被调用时, this指向的是该对象,也称为函数调用的接收器(receiver)。
假设 hero对象有一个 dialogue方法 ,那么 dialogue中的 this值指向 hero本身。此时的 hero也被称为 dialogue方法调用的接收者。
B 如果我将 dialogue的引用存储在另一个变量中,并将该变量作为函数调用。 node运行代码 , this将返回 undefined ,因为该方法已经丢失了接收器。 this此时指向 global ,而不是 hero 。
const hero = {
heroName: "Batman",
dialogue() {
console.log(`I am ${this.heroName}!`);
}
};
hero.dialogue();
const saying = hero.dialogue;
saying();
function f() {
return '姓名:'+ this.name;
}
var A = {
name: '张三',
describe: f
};
var B = {
name: '李四',
describe: f
};
A.describe() // "姓名:张三"
B.describe() // "姓名:李四"
5 call() 和 apply()
虽然函数的 this值是隐式设置的,我们在调用函数时也可以使用 call()和 apply()明确设置 this参数。
function dialogue () {
console.log (`I am ${this.heroName}`);
}
const hero = {
heroName: 'Batman',
};
dialogue.call(hero)
// or
dialogue.apply(hero)
6 bind()
当我们将一个方法作为回调函数传递给另一个函数时,总是存在丢失方法的原有接收器的风险,使得 this参数指向全局对象。
bind()方法可以将 this参数固定的绑定到一个值上。下面的代码片段, bind会创建一个新的 dialogue函数,并将 this值设置为 hero 。(译者注:bind()方法会创建一个新函数,称为绑定函数-bound function-BF,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
const hero = {
heroName: "Batman",
dialogue() {
console.log(`I am ${this.heroName}`);
}
};
setTimeOut(hero.dialogue.bind(hero), 1000);
7 箭头函数中的“this”
箭头函数内的 this与其他类型的JavaScript函数有很大的不同。箭头函数会永久地捕获 this值,阻止 apply或 call后续更改它。
为了解释箭头函数中的 this是如何工作的,我们来写一个箭头函数:
箭头函数内的 this值无法明确设置。此外,使用 call 、 apply或 bind等方法给 this传值,箭头函数会忽略。箭头函数引用的是箭头函数在创建时设置的 this值。
(译者注:箭头函数中没有this绑定,必须通过查找作用域链来决定它的值,如果箭头函数被非箭头函数包裹,那么this值由外围最近一层非箭头函数决定,否则为undefined。)
const batman = this;
const bruce = () => {
console.log(this === batman);
};
bruce();
难以理解:
const counter = {
count: 0,
increase() {
setInterval(function() {
console.log(++this.count);
}, 1000);
}
}
counter.increase();
使用 node index.js运行此代码,只会得到一个 NaN的列表。这是因为 this.count已经不是指向 counter对象了。它实际上指向的为 global对象。
const counter = {
count: 0,
increase () {
setInterval (() => {
console.log (++this.count);
}, 1000);
},
};
counter.increase();
注意 :不要将 ++this.count 写成 this.count + 1。后者只会增加count的值一次,每次迭代都会返回相同的值。
解答:
在第4点:方法中的“this”中,当函数作为对象的方法被调用时, this指向的是该对象,也称为函数调用的接收器。在这里,increase方法的接收器是counter,但是里面的函数不是。
而使用箭头之后,会自动绑定到counter
Class中的“this”
类通常包含一个 constructor , this可以指向任何新创建的对象。
不过在作为方法时,如果该方法作为普通函数被调用, this也可以指向任何其他值。与方法一样,类也可能失去对接收器的跟踪。
.我们将之前创建的 Hero函数改造为类。该类包含一个构造函数和一个 dialogue()方法。最后,创建一个类的实例并调用 dialogue方法。
实质
参考文章
JavaScript 语言之所以有 this 的设计,跟内存里面的数据结构有关系。
var obj = { foo: 5 };
上面的代码将一个对象赋值给变量obj
。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 }
,然后把这个对象的内存地址赋值给变量obj
。也就是说,变量obj
是一个地址(reference)。后面如果要读取obj.foo
,引擎先从obj
拿到内存地址,然后再从该地址读出原始的对象,返回它的foo
属性。
原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的foo属性,实际上是以下面的形式保存的。
{
foo: {
[[value]]: 5
[[writable]]: true
[[enumerable]]: true
[[configurable]]: true
}
}
注意,foo
属性的值保存在属性描述对象的value
属性里面。
这样的结构是很清晰的,问题在于属性的值可能是一个函数。
JavaScript 允许在函数体内部,引用当前环境的其他变量。
现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。