当我开始学习JavaScript时,花了一些时间来理解JavaScript中的this关键字并且能够快速识别this关键字所指向的对象。我发现理解this关键字最困难的事情是,您通常会忘记在您已阅读或观看过一些JavaScript课程或资源中解释的不同案例情况。在ES6中引入箭头函数后,事情变得更加混乱,因为箭头函数this以不同的方式处理关键字。我想写这篇文章来陈述我学到的东西,并尝试以一种可以帮助任何正在学习JavaScript并且难以理解this关键字的人的方式来解释它。

您可能知道,执行任何JavaScript行的环境(或scope)称为“执行上下文”。Javascript运行时维护这些执行上下文的堆栈,并且当前正在执行存在于该堆栈顶部的执行上下文。this变量引用的对象每次更改执行上下文时都会更改。

默认情况下,执行上下文是全局的,这意味着如果代码作为简单函数调用的一部分执行,则该this变量将引用全局对象。在浏览器的情况下,全局对象是window对象。例如,在Node.js环境中,this值是一个特殊对象global

例如,尝试以下简单的函数调用:

function foo () {console.log("Simple function call");console.log(this === window);
}
foo();

调用foo(),得到输出:

“Simple function call”
true

证明这里的this指向全局对象,此例中为window

注意,如果实在严格模式下,this的值将是undefined,因为在严格模式下全局对象指向undefined而不是window

试一下如下示例:

function foo () {'use strict';console.log("Simple function call");console.log(this === window);
}
foo();

输出:

“Simple function call”
false

我们再来试下有构造函数的:

function Person(first_name, last_name) {this.first_name = first_name;this.last_name = last_name;this.displayName = function() {console.log(`Name: ${this.first_name} ${this.last_name}`);};
}

创建Person实例:

let john = new Person('John', 'Reid');
john.displayName();

得到结果:

"Name: John Reid"

这里发生了什么?当我们调用 new PersonJavaScript会在Person函数内创建一个新对象并把它保存为this。接着,first_name, last_namedisplayName 属性会被添加到新创建的this对象上。如下:

你会注意到在Person执行上下文中创建了this对象,这个对象有first_name, last_namedisplayName 属性。希望您能根据上图理解this对象是如何创建并添加属性的。

我们已经探讨了两种相关this绑定的普通案例我不得不提出下面这个更加困惑的例子,如下函数:

function simpleFunction () {console.log("Simple function call")console.log(this === window);
}

我们已经知道如果像下面这样作为简单函数调用,this关键字将指向全局对象,此例中为window对象。

simpleFunction()

因此,得到输出:

“Simple function call”
true

创建一个简单的user对象:

let user = {count: 10,simpleFunction: simpleFunction,anotherFunction: function() {console.log(this === window);}
}

现在,我们有一个simpleFunction属性指向simpleFunction函数,同样添加另一个属性调用anotherFunction函数方法。

如果调用user.simpleFunction(),得到输出:

“Simple function call”
false

为什么会这样呢?因为simpleFunction()现在是user对象的一个属性,所以this指向这个user对象而不是全局对象。

当我们调用user.anotherFunction,也是一样的结果。this关键字指向user对象。所以,console.log(this === window);应该返回false:

false

再来,以下操作会返回什么呢?

let myFunction = user.anotherFunction;
myFunction();

现在,得到结果:

true

所以这又发生了什么?在这个例子中,我们发起普通函数调用。正如之前所知,如果一个方法以普通函数方式执行,那么this关键字将指向全局对象(在这个例子中是window对象)。所以console.log(this === window);输出true

再看一个例子:

var john = {name: 'john',yearOfBirth: 1990,calculateAge: function() {console.log(this);console.log(2016 - this.yearOfBirth);function innerFunction() {console.log(this);}innerFunction();}
}

调用john.calculateAge()会发生什么呢?

{name: "john", yearOfBirth: 1990, calculateAge: ƒ}
26
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

calculateAge函数内部, this 指向 john对象,但是,在innerFunction函数内部,this指向全局对象(本例中为window),有些人认为这是JS的bug,但是规则告诉我们无论何时一个普通函数被调用时,那么this将指向全局对象。

我所学的JavaScript函数也是一种特殊的对象,每个函数都有call, apply, bind方法。这些方法被用来设置函数的执行上下文的this值。

function Person(firstName, lastName) {this.firstName = firstName;this.lastName = lastName;this.displayName = function() {console.log(`Name: ${this.firstName} ${this.lastName}`);}
}

创建两个实例:

let person = new Person("John", "Reed");
let person2 = new Person("Paul", "Adams");

调用:

person.displayName();
person2.displayName();

结果:

Name: John Reed
Name: Paul Adams

call:

person.displayName.call(person2);

上面所做的事情就是设置this的值为person2对象。因此,

Name: Paul Adams

apply:

person.displayName.apply([person2]);

得到:

Name: Paul Adams

callapply唯一的区别就是参数的传递形式,apply应该传递一个数组,call则应该单独传递参数。

我们用bind来做同样的事情,bind返回一个新的方法,这个方法中的this指向传递的第一个参数。

let person2Display = person.displayName.bind(person2);

调用person2Display,得到Name: Paul Adams结果。

箭头函数

ES6中,有一个新方法定义函数。如下:

let displayName = (firstName, lastName) => {console.log(Name: ${firstName} ${lastName});
};

不像通常的函数,箭头函数没有他们自身的this关键字。他们只是简单的使用写在函数里的this关键字。他们有一个this词法变量。

ES5:

var box = {color: 'green', // 1position: 1, // 2clickMe: function() { // 3document.querySelector('body').addEventListener('click', function() {var str = 'This is box number ' + this.position + ' and it is ' + this.color; // 4alert(str);});}
}

如果调用:

box.clickMe()

弹出框内容将是This is box number undefined and it is undefined'.

我们一步一步来分析是怎么回事。在//1//2行,this关键字能访问到colorposition属性因为它指向box对象。在clickMe方法内部,this关键字能访问到colorposition属性因为它也指向box对象。但是,clickMe方法为querySelector方法定义了一个回调函数,然后这个回调函数以普通函数的形式调用,所以this指向全局对象而非box对象。当然,全局对象没有定义colorposition属性,所以这就是为什么我们得到了undefined值。

我们可以用ES5的方法来修复这个问题:

var box = {color: 'green',position: 1,clickMe: function() {var self = this;document.querySelector('body').addEventListener('click', function() {var str = 'This is box number ' + self.position + ' and it is ' + self.color;alert(str);});}
}

添加 var self = this,创建了一个可以使用指向box对象的this关键字的闭包函数的工作区。我们仅仅只需要在回调函数内使用self变量。

调用:

box.clickMe();

弹出框内容This is box number 1 and it is green

怎么使用箭头函数能够达到上述效果呢?我们将用箭头函数替换点击函数的回调函数。

var box = {color: 'green',position: 1,clickMe: function() {document.querySelector('body').addEventListener('click', () => {var str = 'This is box number ' + this.position + ' and it is ' + this.color;alert(str);});}
}

箭头函数的神奇之处就是共享包裹它的this词法关键字。所以,本例中外层函数的this共享给箭头函数,这个外层函数的this关键字指向box对象,因此,colorposition属性将是有正确的green1值。

再来一个:

var box = {color: 'green',position: 1,clickMe: () => {document.querySelector('body').addEventListener('click', () => {var str = 'This is box number ' + this.position + ' and it is ' + this.color;alert(str);});}
}

oh!现在又弹出了‘This is box number undefined and it is undefined’.。为什么?

click事件监听函数闭包的this关键字共享了包裹它的this关键字。在本例中它被包裹的箭头函数clickMeclickMe箭头函数的this关键字指向全局对象,本例中是window对象。所以this.colorthis.position将会是undefined因为window对象没有positioncolor属性。

我想再给你看个在很多情况下都会有帮助的map函数,我们定义一个Person构造函数方法如下:

function Person(firstName, lastName) {this.firstName = firstName;this.lastName = lastName;this.displayName = function() {console.log(`Name: ${this.firstName} ${this.lastName}`);}
}

Person的原型上添加myFriends方法:

Person.prototype.myFriends = function(friends) {var arr = friends.map(function(friend) {return this.firstName + ' is friends with ' + friend;});console.log(arr);
}

创建一个实例:

let john = new Person("John", "Watson");

调用john.myFriends(["Emma", "Tom"]),结果:

["undefined is friends with Emma", "undefined is friends with Tom"]

本例与之前的例子非常相似。myFriends函数体内有this关键字指向回调对象。但是,map闭包函数内是一个普通函数调用。所以map闭包函数内this指向全局对象,本例中为window对象,因此this.firstNameundefined。现在,我们试着修复这个情况。

  1. myFriends函数体内指定this为其它变量如self,以便map函数内闭包使用它。
Person.prototype.myFriends = function(friends) {// 'this' keyword maps to the calling objectvar self = this;var arr = friends.map(function(friend) {// 'this' keyword maps to the global object// here, 'this.firstName' is undefined.return self.firstName + ' is friends with ' + friend;});console.log(arr);
}
  1. map闭包函数使用bind
Person.prototype.myFriends = function(friends) {// 'this' keyword maps to the calling objectvar arr = friends.map(function(friend) {// 'this' keyword maps to the global object// here, 'this.firstName' is undefined.return this.firstName + ' is friends with ' + friend;}.bind(this));console.log(arr);
}

调用bind会返回一个map回调函数的副本,this关键字映射到外层的this关键字,也就是是调用myFriends方法,this指向这个对象。

  1. 创建map回调函数为箭头函数。
Person.prototype.myFriends = function(friends) {var arr = friends.map(friend => `${this.firstName} is friends with ${friend}`);console.log(arr);
}

现在,箭头函数内的this关键字将共享未曾包裹它的词法作用域,也就是说实例myFriends

所有以上解决方案都将输出结果:

["John is friends with Emma", "John is friends with Tom"]

在这一点上,我希望我已经设法使this关键字概念对您来说有点平易近人。在本文中,我分享了我遇到的一些常见情况以及如何处理它们,但当然,在构建更多项目时,您将面临更多情况。我希望我的解释可以帮助您在接近this关键字绑定主题时保持坚实的基础。如果您有任何问题,建议或改进,我总是乐于学习更多知识并与所有知名开发人员交流知识。请随时写评论,或给我留言!

揭秘JavaScript中“神秘”的this关键字相关推荐

  1. JavaScript中的普通函数与构造函数比较

    问题 什么是构造函数? 构造函数与普通函数区别是什么? 用new关键字的时候到底做了什么? 构造函数有返回值怎么办? 构造函数能当普通函数调用吗? this this永远指向当前正在被执行的函数或方法 ...

  2. JavaScript中的常量:什么时候使用它,有必要吗?

    本文翻译自:Const in JavaScript: when to use it and is it necessary? I've recently come across the const k ...

  3. JavaScript中this关键字使用方法详解

    在面向对象编程语言中,对于this关键字我们是非常熟悉的.比如C++.C#和Java等都提供了这个关键字,虽然在开始学习的时候觉得比较难,但只要理解了,用起来是非常方便和意义确定的.JavaScrip ...

  4. this调用语句必须是构造函数中的第一个可执行语句_谈谈JavaScript中的函数构造式和new关键字...

    您是否曾困惑于 Javascript 中的new关键字呢?是否曾想理解关于 function 和 constructor 的区别是什么呢? 大多数 Javascript 的新开发者不太想要使用new关 ...

  5. 深入解析Javascript中this关键字的使用

    深入解析Javascript中面向对象编程中的this关键字 在Javascript中this关键字代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比如: function TestFun ...

  6. 深入理解JavaScript中的this关键字

    在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程序时能够游刃有余. 对于this变量最 ...

  7. JavaScript中的“ this”关键字

    JavaScript'this'关键字 (JavaScript 'this' keyword) The this keyword is widely used in JavaScript. It ha ...

  8. php中this的使用技巧,JavaScript中this关键字使用方法详解

    JavaScript中this关键字使用方法详解 在面向对象编程语言中,对于this关键字我们是非常熟悉的.比如C++.C#和Java等都提供了这个关键字,虽然在开始学习的时候觉得比较难,但只要理解了 ...

  9. 谈谈JavaScript中的function constructor和new关键字

    您是否曾困惑于 Javascript 中的new关键字呢?是否曾想理解关于 function 和 constructor 的区别是什么呢? 大多数 Javascript 的新开发者不太想要使用new关 ...

最新文章

  1. httpwatch的timechart 解析
  2. Altium designer原理图库中元件变动后在原理图中同步更新方法
  3. dataguard如何实现切换_深度干货 | 如何借助云原生搞定Oracle备份快速恢复?
  4. mysql使用过程中的几个细节注意点
  5. mongodb 入门 启动mongodb 无法启动 问题 非正常关闭
  6. 最大矩阵和 2015-05-13 21:23 8人阅读 评论(0) 收藏...
  7. Windows10部署Kubenetes详细步骤
  8. android之数组排序
  9. 网站性能优化 - 数据库及服务器架构篇
  10. 1、CSS height 属性,2、Flex 布局教程:实例篇,3、CSS 链接,4、CSS display 属性,5、表格,
  11. 一文读懂YUV的采样与格式
  12. 多种电压转换的电路设计方案
  13. Mongodb极简实践
  14. 【MFC】CTabSheet类之再改造
  15. 阴阳师手游服务器维护,12月30日阴阳师服务器维护更新公告
  16. 幼儿园早期阅读活动的现状及问题研究
  17. IE8——focus函数不好用
  18. 苹果iOS证书制作教程
  19. AIO,BIO,NIO详解
  20. 如何入侵网页游戏服务器,游戏服务器被攻击了应该怎么办?如何保护游戏网站安全?...

热门文章

  1. Flutter获取焦点
  2. ac6005直连ap 如何配置_【无线】 AC直连AP配置
  3. win10系统安装到服务器失败,win10安装失败怎么办?
  4. 史上最强HashMap面试教程
  5. 微信赌场——H5棋牌游戏渗透之旅
  6. css实现3d正方体旋转
  7. mac能不能做ppt?
  8. LEAM(生活/生命体验算法模型)
  9. 简单搜索引擎使用技巧
  10. 英语谚语精选(English Proverb Collection)