博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[译] Javascript 中多样的 this
阅读量:6122 次
发布时间:2019-06-21

本文共 4842 字,大约阅读时间需要 16 分钟。

  • 原文地址:
  • 原文作者:
  • 译文出自:
  • 本文永久链接:
  • 译者:
  • 校对者:,

Javascript 中多样的 this

本文将尽量解释清楚 JavaScript 中最基础的部分之一:执行上下文(execution context)。如果你经常使用 JS 框架,那理解 this 更是锦上添花。但如果你想更加认真地对待编程的话,理解上下文无疑是非常重要的。

我们可以像平常说话一样来使用 this。例如:我会说“我妈很不爽,这(this)太糟糕了”,而不会说“我妈很不爽,我妈很不爽这件事太糟糕了”。理解了 this 的上下文,才会理解我们为什么觉得很糟糕。

现在试着把这个例子与编程语言联系起来。在 Javascript 中,我们将 this 作为一个快捷方式,一个引用。它指向其所在上下文的某个对象或变量。

现在这么说可能会让人不解,不过很快你就能理解它们了。

全局上下文

如果你和某人聊天,在刚开始对话、没有做介绍、没有任何上下文时,他对你说:“这(this)太糟糕了”,你会怎么想?大多数情况人们会试图将“这(this)”与周围的事物、最近发生的事情联系起来。

对于浏览器来说也是如此。成千上万的开发者在没有上下文的情况下使用了 this。我们可怜的浏览器只能将 this 指向一个全局对象(大多数情况下是 window)。

var a = 15;console.log(this.a);// => 15console.log(window.a);// => 15复制代码

[以上代码需在浏览器中执行]

函数外部的任何地方都为全局上下文,this 始终指向全局上下文(window 对象)。

函数上下文

以真实世界来类比,函数上下文可以看成句子的上下文。“我妈很不爽,这(this)很不妙。”我们都知道这句话中的 this 是什么意思。其它句子中同样可以使用 this,但是由于其处于所处上下文不同因而意思全然不同。例如,“风暴来袭,这(this)太糟糕了。”

JavaScript 的上下文与对象有关,它取决于函数被执行时所在的对象。因此 this 会指向被执行函数所在的对象。

var a = 20;function gx () {    return this;}function fx () {    return this.a;}function fy () {    return window.a;}console.log(gx() === window);// => Trueconsole.log(fx());// => 20console.log(fy());// => 20复制代码

this 由函数被调用的方式决定。如你所见,上面的所有函数都是在全局上下文中被调用。

var o = {  prop: 37,  f: function() {    return this.prop;  }};console.log(o.f());// => 37复制代码

当一个函数是作为某个对象的方法被调用时,它的 this 指向的就是这个方法所在的对象。

function fx () {    return this;}var obj = {    method: function () {        return this;    }};var x_obj = {    y_obj: {        method: function () {            return this;        }    }};console.log(fx() === window);// => True — 我们仍处于全局上下文中。console.log(obj.method() === window);// => False — 函数作为一个对象的方法被调用。console.log(obj.method() === obj);// => True — 函数作为一个对象的方法被调用。console.log(x_obj.y_obj.method() === x_obj)// => False — 函数作为 y_obj 对象的方法被调用,因此 `this` 指向的是 y_obj 的上下文。复制代码

例 4

function f2 () {  'use strict';   return this;}console.log(f2() === undefined);// => True复制代码

在严格模式下,全局作用域的函数在全局作用域被调用时,thisundefined

例 5

function fx () {    return this;}var obj = {    method: fx};console.log(obj.method() === window);// => Falseconsole.log(obj.method() === obj);// => True复制代码

与前面的例子一样,无论函数是如何被定义的,在这儿它都是作为一个对象方法被调用。

例 6

var obj = {    method: function () {        return this;    }};var sec_obj = {    method: obj.method};console.log(sec_obj.method() === obj);// => Falseconsole.log(sec_obj.method() === sec_obj);// => True复制代码

this 是动态的,它可以由一个对象指向另一个对象。

例 7

var shop = {  fruit: "Apple",  sellMe: function() {    console.log("this ", this.fruit);// => this Apple    console.log("shop ", shop.fruit);// => shop Apple  }}shop.sellMe()复制代码

我们既能通过 shop 对象也能通过 this 来访问 fruit 属性。

例 8

var Foo = function () {    this.bar = "baz"; };var foo = new Foo();console.log(foo.bar); // => bazconsole.log(window.bar);// => undefined复制代码

现在情况不同了。new 操作符创建了一个对象的实例。因此函数的上下文设置为这个被创建的对象实例。

Call、apply、bind

依旧以真实世界举例:“这(this)太糟糕了,因为我妈开始不爽了。”

这三个方法可以让我们在任何期许的上下文中执行函数。让我们举几个例子看看它们的用法:

例 1

var bar = "xo xo";var foo = {    bar: "lorem ipsum"};function test () {    return this.bar;}console.log(test());// => xo xo — 我们在全局上下文中调用了 test 函数。console.log(test.call(foo)); // => lorem ipsum — 通过使用 `call`,我们在 foo 对象的上下文中调用了 test 函数。console.log(test.apply(foo));// => lorem ipsum — 通过使用 `apply`,我们在 foo 对象的上下文中调用了 test 函数。复制代码

这两种方法都能让你在任何需要的上下文中执行函数。

apply 可以让你在调用函数时将参数以不定长数组的形式传入,而 call 则需要你明确参数。

例 2

var a = 5;function test () {    return this.a;}var bound = test.bind(document);console.log(bound()); // => undefined — 在 document 对象中没有 a 这个变量。console.log(bound.call(window)); // => undefined — 在 document 对象中没有 a 这个变量。在这个情况中,call 不能改变上下文。var sec_bound = test.bind({a: 15})console.log(sec_bound())// => 15 — 我们创建了一个新对象 {a:15},并在此上下文中调用了 test 函数。复制代码

bind 方法返回的函数的下上文会被永久改变。

在使用 bind 之后,其上下文就固定了,无论你再使用 call、apply 或者 bind 都无法再改变其上下文。

箭头函数(ES6)

箭头函数是 ES6 中的一个新语法。它是一个非常方便的工具,不过你需要知道,在箭头函数中的上下文与普通函数中的上下文的定义是不同的。让我们举例看看。

例 1

var foo = (() => this);console.log(foo() === window); // => True复制代码

当我们使用箭头函数时,this 会保留其封闭范围的上下文。

例 2

var obj = {method: () => this};var sec_obj = {  method: function() {    return this;  }};console.log(obj.method() === obj);// => Falseconsole.log(obj.method() === window);// => Trueconsole.log(sec_obj.method() === sec_obj);// => True复制代码

请注意箭头函数与普通函数的不同点。在这个例子中使用箭头函数时,我们仍然处于 window 上下文中。

我们可以这么看:

x => this.y equals function (x) { return this.y }.bind(this)

可以将箭头函数看做其始终 bind 了函数外层上下文的 this,因此不能将它作为构造函数使用。下面的例子也说明了其不同之处。

例 3

var a = "global";var obj = { method: function () {   return {     a: "inside method",     normal: function() {       return this.a;     },     arrowFunction: () => this.a   }; }, a: "inside obj"};console.log(obj.method().normal());// => inside methodconsole.log(obj.method().arrowFunction());// => inside obj复制代码

当你了解了函数中动态(dynamic) this 与词法(lexical)this ,在定义新函数的时候请三思。如果函数将作为一个方法被调用,那么使用动态 this;如果它作为一个子程序(subroutine)被调用,则使用词法 this

译注:了解动态作用域与词法作用域可

相关阅读


是一个翻译优质互联网技术文章的社区,文章来源为 上的英文分享文章。内容覆盖 、、、、、、 等领域,想要查看更多优质译文请持续关注 、、。

转载地址:http://rjwua.baihongyu.com/

你可能感兴趣的文章
新安装的WAMP中phpmyadmin的密码问题
查看>>
20172303 2017-2018-2 《程序设计与数据结构》第5周学习总结
查看>>
eclipse中将一个项目作为library导入另一个项目中
查看>>
Go语言学习(五)----- 数组
查看>>
Android源码学习之观察者模式应用
查看>>
Content Provider的权限
查看>>
416. Partition Equal Subset Sum
查看>>
centos7.0 64位系统安装 nginx
查看>>
数据库运维平台~自动化上线审核需求
查看>>
注解开发
查看>>
如何用 Robotframework 来编写优秀的测试用例
查看>>
Django之FBV与CBV
查看>>
Vue之项目搭建
查看>>
app内部H5测试点总结
查看>>
Docker - 创建支持SSH服务的容器镜像
查看>>
[TC13761]Mutalisk
查看>>
三级菜单
查看>>
Data Wrangling文摘:Non-tidy-data
查看>>
加解密算法、消息摘要、消息认证技术、数字签名与公钥证书
查看>>
while()
查看>>