[JavaScript 随笔] 关于 this 的点滴

jopen 9年前

TL;DR: this 指向调用该方法的对象,只有函数执行时,this 才有定义。

关于 JavaScript 中 this 的坑大家都踩过。像本文开头的这句话,道理你都懂,但是……所以这里就总结了几个 this 最常用的使用场景。

全局环境

在浏览器的全局环境中,this === window 。
但是,当使用了 ‘use strict'; 进入严格模式时,this === undefined 。

如果是在 nodejs 环境中,全局对象会更复杂一些,因为它有两种执行方式。一个是命令行方式,即输入

shell$ node

进入类似于浏览器的控制台一样的界面,可以逐行执行代码。那这里的 this 和 global 是严格相等的。但如果是

shell$ node program.js

这样执行一个文件的话,nodejs 会为每个文件创建一个自执行匿名函数的块,这里面的 this 并是全局对象 global 。但是,如果声明变量时没有加 var 的话,这些变量还是会加到 global 上去。

函数调用

函数中的 this 可能更常见一点吧。

javascriptfunction foo() {    console.log(this.name);  }

如果直接判断这里的 this 是全局对象的话,就太冲动了(还记得最开始这句话吗?只有当函数调用时才能判断 this 真正引用的对象是什么)。

如果它作为一个全局函数【foo()】,或者闭包【return foo;】,又或者是回调函数【other(foo)】的话,那么它在执行时就是全局对象了。

还有三个我们经常遇到的方法可以改变 this 的引用,就是 call、apply 和 bind 。

javascriptfoo.call(thisArg);

那么这里的 this 就指向了 thisArg,但当它为 null 或者 undefined,this 会指向全局对象。

还有一种函数调用是使用 new 关键字。

javascriptnew foo();

这里的 this 指向的是构造出来的新对象。

那么其他情况就必然是 x.foo() 类似的调用方式了,看到这条语句的时候再去看 foo 的定义,它里面 this 引用的就是对象 x 。
数学学得好的话,可以反应过来 x 可以表示任何对象,比如 a,a.b,或者其他更复杂的表达式。

好,来测试一下,有如下代码

var o = {    foo: function() { return this; }  };    function bar() {    return o.foo;  }    console.log(bar()());

结果是什么?反正就两个选择,一是 this 指向全局对象,二是指向 o 。

原型

在上一篇文章 《继承的实现方式及原型概述》 中其实有提到过一部分原型方面的知识,那这里就稍微深入一点。

这里介绍的是通过 new 创建对象时的 this 。每个函数对象(用 function 关键字修饰的变量)自带 prototype 属性,在 prototype 上定义的属性都会继承给 this 。这些属性被每个实例共享,实例中会创建所有 prototype 上的属性,值为这些属性的引用。

文字一多心里难免烦烦的……

[JavaScript 随笔] 关于 this 的点滴

首先,这里通过 new 创建了两个实例,它们有相同的 prototype 。原型中的数据是创建在堆上的,所以继承下来的属性和方法都会指向同一个引用(左边的箭头)。

然后,在 this 上定义的属性和方法是创建在栈上的,所以这些属性和方法会有各自独立的内存区域(右边的箭头)。

假如在 this 上定义的变量与 prototype 上冲突了,那么 prototype 中的那个变量会“隐藏”起来。

如果有 child.foo = ‘tom'; 那么思考一下怎样才能让 child.foo 重新获得 prototype 中的 foo 值?

一种是可以直接 Parent.prototype.foo 就完事了。另外一种可以通过

javascriptdelete child.foo;   console.log(child.foo); // 重新获得 prototype 中的 foo 值

前者的缺点在于,假设 foo 是个函数的话,那么 Parent.prototype.foo() 时,其中的 this 指向的是 Parent.prototype,而不是 child 实例。

很多道理都很简单,那么再来考一下……

javascriptvar slice = ___?    slice({'0': 'a', '1': 'b', 'length': 2}) => ['a', 'b']

简单来说就是用一个表达式定义一个变量(函数),它可以将类数组对象(比如说 arguments)转化成数组。提示一下,数组对象的 slice 函数本身就是有这个功能的,也就是说 Array.prototype.slice.call(arguments) 可以达到要求。

DOM 事件

听说长篇大论不会受欢迎,但是我仔细想想还是得把这部分写下来。

事件有两种记法,一个是

javascriptel.addEventListener('event', handler);

attachEvent 也是类似,那么在 handler 中出现的 this 表示触发该事件的元素,也就是 el 。

另一种是

javascriptel.onevent = handler;

同样,handler 中的 this 还是 el 。

最后一个题……

javascriptvar o = {    foo: function() { return this; }  }    document.onload = o.foo;

请问,当 load 事件触发时,这里的 this 是什么?三个选择:o, window, document.

小结

从这里开始是“广告”时间了……

其实 this 的讨论可以展开很多,可能上面记的内容中有很多欠缺的地方,这个希望大家可以指正。

那我们的知识库总是会随着学习和努力慢慢扩大的,个人能力是一方面,花的精力是另一方面。只要肯静下心去琢磨,很多“网上各种人说这个难那个难”的知识(比如说闭包、原型,或者和知识面广度有关的,比如说数组中 slice 的高级用法等),花点时间总会搞懂的。

所以学习没有捷径,也没有培训班,有心就可以了。

来自:http://hao.jser.com/archive/8118/