资讯专栏INFORMATION COLUMN

由ES规范学JavaScript(三):深入理解this

wthee / 3343人阅读

摘要:有三个重要组成部分和。参考小结函数中的是由调用函数的方式决定的。构造函数中的当构造函数通过操作符来调用时,表示正在创建的对象。情况没有明确作用对象的情况下,通常为全局对象例如函数的回调函数,它的就是全局对象。

一. this的来源

this是JavaScript的关键字,它最初应该是从Java、C++等面向对象的语言中借鉴来的。

比如,在Java中没有函数只有方法,this只能用在类的成员方法或构造方法中,表示当前实例对象。所以在Java中this的含义很明确,在其他语言中也类似。

然而到了JavaScript中,this变得复杂了起来:不仅函数内可以用,在所有函数外(全局上下文中)也可以用;函数中的this的含义在函数声明时无法确定,要到运行期才能确定,而且与调用函数的方式有关;代码是否是严格模式也会影响this的取值。

二. this到底是什么

在ES5.1中,有一个所谓执行上下文(Execution Context,EC)的概念,简单的说就是JS引擎的执行进入到某块代码区域时,为该代码区域建立的上下文对象,主要用来记录该区域中声明的变量、函数等。

EC有三个重要组成部分:VELEThisBinding。前两个是词法环境,暂且不管。第三个 ThisBinding就是指在该代码区域的this的值。

可见,this是跟某块代码区域关联的。而在JS中,代码区域有三种:

global代码

function代码

eval代码

此文中主要讨论前两种。

三. 全局代码区域中的this

全局代码区域是所有函数之外的区域。在此区域中的this就是指全局对象window(在Node.js中是global)。

示例:

var num = 123;
console.log(this.num); // 输出123

参考:http://es5.github.io/#x10.4.1.1

四. 函数代码区域中的this

函数代码区域是指某个函数内的代码,但是不包括它所嵌套的函数内的代码。从我们可以看出:

this是与包裹它的且离它最近的函数相关的,this既不能穿透到外部的函数,也不能穿透进内部的函数。

示例:

btn.addListener("click", function() {
    var that = this;
    dosth(function() {
        console.log(that.name);
    });
});

通常每个函数中的this是不同的,内部函数可以引用外部函数的局部变量,但是不能直接引用外部函数的this。通过将外部函数的this赋值给一个局部变量可以解决这个问题。

函数内的this的具体函数比较复杂,主要与调用这个函数的方式有关。主要包括以下情况:

1. 直接调用时

示例:

var num = 123;

function fn() {
    console.log(this.num);
}

function fn2() {
    "use strict"
    console.log(this.num);
}

fn(); // 输出123
fn2(); // 报错

直接调用函数时,如果是在严格模式下,this会被设为undefined;如果是在非严格模式下,this会被设为全局对象window

2. 作为方法调用时

示例:

var student = {
    name: "Tom",
    sayName: function() {
        console.log(this.name);
    };
};

student.sayName(); // 输出Tom

作为方法调用时,this指方法所属的对象。

参考:http://es5.github.io/#x10.4.3和 http://es5.github.io/#x11.2.3

3. call和apply方法:调用时指定this

除了上述两种固定的情况外,Javascript提供了一种可以随心所欲地根据需要更改函数中this方法。即使用函数对象的callapply方法来调用函数,显然这种方式给编程带来了极大的灵活性。

示例:

function fn() {
    var args = Array. prototype. slice.call(arguments, 1);
    console.log(args);
}

fn(1, 2, 3); // 输出[2, 3]

这种方法常用的场景就是:把一个对象的方法"借"给另一个具有类似结构的对象使用。

4. bind方法:重新绑定函数的this

与call和apply不同,bind方法是在调用前就把函数内的this绑定了,而且一旦绑定就不能再改变。实际上bind方法返回了一个原函数的新版本。

示例:

function fn() {
    console.log(this.age);
}

var fn2 = fn.bind({age: 18});
fn2() // 输出18
fn2.call({age: 25}) // 输出18

通过bind得到的函数,不论用哪种方式调用,它的this都是相同的。

参考:http://es5.github.io/#x15.3.4.5

小结:函数中的this是由调用函数的方式决定的。同一个函数,调用它的方式不同,那么它内部的this就可能不同。换句话说,this是动态决定的。

5. 构造函数中的this

当构造函数通过new操作符来调用时,this表示正在创建的对象。

示例:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

var jerry = new Person("Jerry", 12);

console.log(jerry.age); // 输出12

正因为这个原因,我们可以在构造函数中通过this给实例添加属性。

参考:http://es5.github.io/#x11.2.2

6. 回调函数的this

回调函数也只不过是函数的一种,实际上这种情况已经包含在了前面提到的情况中。但是由于回调函数的调用者往往不是我们自己,而是回调函数的接收者,即某个库或框架、甚至是JS运行时环境。这样一来,回调函数在中的this是什么就与对方的调用方式有关了,因此变得比较复杂,所以多带带拿出来讨论一下。

情况1:没有明确作用对象的情况下,通常this为全局对象

例如setTimeout函数的回调函数,它的this就是全局对象。你如果希望自己指定this,可以通过bind函数等方法。

情况2:某个事件的监听器回调函数,通常this就是事件源对象

例如:

button.addEventListener("click", fn)

fn的中的this就是事件源button对象。

情况3:某些API会专门提供一个参数,用来指定回调函数中的this

例如,我们可以重新设计一个可以指定this的setTimeout:

function setTimeoutExt(cb, period, thisArg) {
    setTimeout(function() {
        cb.call(thisArg);
    }, period);
}

另外,在ExtJS中也大量使用了可以指定this的接口。

五. eval中的this

(代补充)

六. 重新审视

this,除了面向对象语言中通用的那两种情况(方法和构造函数)外,在JavaScript 中还提供了更多的使用方式,虽然这让JS中的this变得相对难以掌握,但是它使得JS更加丰富更加灵活。我们可以把this看成函数的一个特殊的隐含的参数,这个参数代表函数正在操作的主体。

注:时间比较仓促,有些地方没有太深入,代码实例也比较简单。有机会继续完善。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78485.html

相关文章

  • ES规范JavaScript(二):深入理解“连等赋值”问题

    摘要:有这样一个热门问题其实这个问题很好理解,关键要弄清下面两个知识点引擎对赋值表达式的处理过程赋值运算的右结合性一赋值表达式形如的表达式称为赋值表达式。赋值表达式是右结合的。 有这样一个热门问题: var a = {n: 1}; var b = a; a.x = a = {n: 2}; alert(a.x); // --> undefined alert(b.x); // --> {n: ...

    JasonZhang 评论0 收藏0
  • 零基础的前端开发初者应如何系统地习?

    摘要:在有了基础之后,进一步学习内容包括框架。前端学习交流群禁止闲聊,非喜勿进。代码提交前必须做的三个事情检查所有变更跑一边单元测试手动运行一遍所有 网站开发开发大致分为前端和后端,前端主要负责实现视觉和交互效果,以及与服务器通信,完成业务逻辑。其核心价值在于对用户体验的追求。可以按如下思路学习系统学习: 基础知识: html + css 这部分建议在 w3school 在线教程上学习,边...

    JouyPub 评论0 收藏0
  • 零基础的前端开发初者应如何系统地习?

    摘要:在有了基础之后,进一步学习内容包括框架。前端学习交流群禁止闲聊,非喜勿进。代码提交前必须做的三个事情检查所有变更跑一边单元测试手动运行一遍所有 网站开发开发大致分为前端和后端,前端主要负责实现视觉和交互效果,以及与服务器通信,完成业务逻辑。其核心价值在于对用户体验的追求。可以按如下思路学习系统学习: 基础知识: html + css 这部分建议在 w3school 在线教程上学习,边...

    funnyZhang 评论0 收藏0
  • H5

    摘要:为此决定自研一个富文本编辑器。本文,主要介绍如何实现富文本编辑器,和解决一些不同浏览器和设备之间的。 对ES6Generator函数的理解 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。 JavaScript 设计模式 ② 巧用工厂模式和创建者模式 我为什么把他们两个放在一起讲?我觉得这两个设计模式有相似之处,有时候会一个设计模式不能满...

    aristark 评论0 收藏0

发表评论

0条评论

wthee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<