为初学者介绍 JavaScript 中的 this 关键字

来源:互联网 发布:网络暴力产生问题 编辑:程序博客网 时间:2024/05/16 15:50

JavaScript 中的 this 关键字在不同情况下指的是什么,很多时候会有点复杂。幸运的是,有五个规则可以用来确定 this。尽管有些情况下这些规则不适用,但应该可以在绝大多数情况下解决我们的困惑。所以,让我们开始吧!

1. this通常由函数的 执行上下文 决定。执行上下文是指一个函数是 如何 被调用的。

2. 很重要的是一个函数每次被调用时,函数中的 this可能会指向不同的东西。

3. 如果现在还不能理解上面两点的含义,没有关系,本文结尾时在总结里还会提到。

1 全局对象

现在让我们在实践中学习。 打开 Chrome 开发者控制台(Windows:Ctrl + Shift +J)(Mac:Cmd +Option + J)并键入以下内容:

console.log(this);

我们得到了什么

// Window {...}

window对象!这是因为在全局范围内,this是指全局对象。在浏览器中,全局对象是 window对象。

为了帮助我们更好地理解为什么 thiswindow 对象,我们来深入了解一下。在控制台中,创建一个新变量并命名为myName

var myName = 'Brandon';

我们可以通过调用 myName 再次访问这个变量:

myName

但是你知道在全局范围内声明的每个变量都附加在“window”对象上吗?让我们测试一下:

window.myName
window.myName === myName

所以当我们在全局环境中运行 console.log(this) 的时候,我们知道这个 this指向全局对象。 由于浏览器中的全局对象是“window”对象,所以这样的输出结果是有道理的:

console.log(this)

现在让我们把这个 this 放在一个函数里。回想一下我们以前的定义:this 的值通常由函数 如何 被调用而确定。考虑到这一点,你期望这个函数返回什么?在浏览器控制台中,复制下面的代码,然后按回车键。

function test() {

关键字 this 再次指向了全局(window)对象。这是因为关键字 this不在一个已声明的对象内,因此默认指向全局(window)对象。这个概念现在可能有点难以理解,但随着进一步阅读理解会更加深入。有一件事要注意,如果使用严格模式,在上面的例子中,这个this 将是undefined

2 已声明的对象

当关键字 this 在已声明的对象内部使用时,this 指向离被调用的方法 最近的父对象。 看看下面的代码,声明了对象person,并在方法full 中使用 this

var person = {
person.full();

为了更好地说明 this 实际上是引用该对象的,请将以下代码复制到浏览器控制台中。它与上面的代码大致相同,只是执行了 console.log(this) ,所以我们可以看它到底返回什么。

var person = {

正如所看到的,控制台返回了 person 对象,证明 this 已经指向了 person

还记得刚刚说过 this 指向离被调用方法 最近的父对象 吗?在我们有嵌套对象的情况下会发生什么?看下面的示例代码。我们有一个 person 对象,包含与之前相同的 firstlastfull键。这一次,我们还嵌套了一个personTwo 对象。personTwo 包含相同的三个键。

var person = {

当我们分别调用两个 full方法时会发生什么?我们来看看吧。

person.full();
person.personTwo.full();
this 指向离被调用方法 最近的父对象 。当 person.full() 被调用时,函数里的 this 被绑定到person 对象。同时,当person.personTwo.full() 被调用时,在full 函数内,this 被绑定到personTwo 对象!

3 New 关键字

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

让我们来看一个例子:

function Car(make, model) {

你可能会猜测 this应该绑定到全局对象 - 如果我们不用关键字 new 的话,你就是正确的。 当我们使用new 时,this 的值会指向一个空对象,在本例中是myCar

var myCar = new Car('Ford', 'Escape');
console.log(myCar);

为了理解以上的例子,你需要了解什么是 new 关键字,但这其实是另一个全新的话题。 所以现在,如果你不确定,就看到关键字 new,只要知道 this 指向的是一个全新的空白对象就可以了。

4 CallBindApply

最后,但同样很重要的是,我们可以使用 call()bind()apply() 来明确地设置this 的值。 这三个是非常相似的,但重要的是了解其中的小区别。

call()apply() 都会立即被调用。call()可以接受任意数量的参数:this 和之后其他参数。apply() 只需要两个参数:this 和一个包含附加参数的数组。

有木有跟上我的思路?下面这个例子应该会解释得更清楚。看下面的代码,我们正在尝试对几个数求和。将其复制到浏览器控制台并调用该函数。

function add(c, d) {
add(3,4);

add 函数输出 NaN(不是一个数字)。这是因为 this.athis.b 未定义。他们不存在 而且不能将数字喝未定义的东西求和。

让我们添加一个新的对象。我们可以使用 call()apply() 在我们的对象的作用域中调用该函数:

function add(c, d) {
var ten = {a: 1, b: 2};
add.call(ten, 3, 4);
add.apply(ten, [3,4]);

当我们使用 add.call() 时,第一个参数表示 this 被绑定到该参数。随后的参数被传递到我们调用的函数中。因此,在add() 中,this.a 指的是ten.athis.b 指的是ten.b,我们得到 1 + 2 + 3 + 4 的返回结果,也就是 10。

add.apply() 是类似的。第一个参数表示 this 被绑定到该参数。 后续的参数是要在函数中使用的参数组成的数组。bind() 又是什么呢?bind() 中的参数与call() 相同,但是bind() 不会立即被调用。相反,bind() 返回一个上下文this 已经绑定为第一个参数的函数。因此,当我们最开始不知道运行时需要的所有参数时,就可以用bind()。下面这个例子应该会解释得更清楚:

var small = {
var large = {

将上述内容复制到控制台。 然后调用:

small.go(2,3,4);

这里没什么新鲜的。但是,如果我们想要直接使用 large.a的值呢?我们可以使用 call()/apply()

small.go.call(large,2,3,4);

现在,如果我们不知道所有 3 个参数呢?我们可以使用 bind()

var bindTest = small.go.bind(large,2);

如果我们在控制台输出我们上面的变量 bindTest,我们可以看到真相

console.log(bindTest);

记住,bind 会返回一个已经 this 已经被绑定的函数!所以我们的 this 已经成功地绑定到我们的large 对象上。我们也已经传递了第二个参数2。 后来,当知道其它参数后时,我们可以接着将它们传递进函数:

bindTest(3,4);

为了更加清晰,这里将所有的代码放在一个代码块中。通看一遍,并将其复制到控制台,去真正了解发生了什么!

var small = {

5 箭头函数

这本身就是一个很大的话题,因此我写了一篇文章: 为初学者介绍箭头函数

总结

为自己鼓掌吧!在大多数情况下,你现在应该可以推断出 this 是指向什么了!记住以下几件事:

1.this 通常由函数的 执行上下文 决定。

2.在全局范围内,this 指的是全局对象(window 对象)。

3.当使用 new 关键字(一个构造函数)时,this 绑定到正在创建的新对象。

4.我们可以用 call()bind()apply() 来明确设置 this 的值。

5.箭头函数不影响 this —— this 的绑定由函数的 执行上下文 决定(即基于原始上下文)。


原创粉丝点击