原文地址:http://www.moye.me/2014/11/21/也说this/

引子

Any sufficiently advanced technology is indistinguishable from magic.

— Arthur C.Clarke

老爷子所言不虚,各种技术里都有黑魔法,比如JavaScript,就有着像 this这样的奇葩存在。

What's this?

this是什么?大概能得到的答案有:

  • 当前方法引用的对象实例
  • 当前调用函数本身
  • 全局上下文(顶层对象)
  • 函数运行的context环境

哪种解释是对的呢?来看段代码:

function foo() {
console.log(this.a);
}
var a = 2;
var obj = {a: 1};
foo(); // 2 (在Node下是undefined)
foo.call(obj); // 1
obj.foo = foo;
obj.foo(); // 1

在这段代码中,this 呈现了三种可能:

  1. 全局上下文:foo执行时,this 开放式的指向了全局上下文(在node下为undefined,原因在于Global的作用域与浏览器不同
  2. 向方法传递的上下文:Function对象固有的applycall方法,第一个参数均为 thisArg,它将被调用函数做为执行的上下文(在非strict模式,如果这个值为null或undefined,将有污染全局上下文的风险
  3. 方法引用的对象:可以将这个对象看成new创建的对象,函数的this将指向它

看起来是要有无限可能的节奏……好在我提前找到了这本书:

You Don't Know JS: this & Object Prototypes

To learn this, you first have to learn what this is not, despite any assumptions or misconceptions that may lead you down those paths. this is neither a reference to the function itself, nor is it a reference to the function’s lexical scope.

this is actually a binding that is made when a function is invoked, and what it references is determined entirely by the call-site where thefunction is called.

 

this 绑定上下文

this 实质上是一种绑定(binding)机制,只关乎函数被调用时所处的 上下文(context),而不关心它在哪里被定义。举个粟子:

function foo(something) {
this.a = something;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo( 2 );
console.log( obj1.a ); // 2
obj1.foo.call( obj2, 3 );
console.log( obj2.a ); // 3
var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4

this做为一个被绑定的context,甚至与函数依附的对象无关,通过obj1.foo.call( obj2, 3 )一句我们能看清:这个context被引入,函数既可以读也可以修改它,但这里涉及到一个优先级问题,即:为什么 obj1绑定的this.a 没有被foo()改写,却是 obj2的 this.a?

this 绑定优先级

绑定 this 的形式大概有以下几种,它们的优先级如下排列:

  1. 被new过的函数实例,那么函数里的this绑定的就是这个实例对象本身,如 var bar = new foo();
  2. 函数是被 call 或者 apply 调用,那么 this 绑定的是第一个参数: var bar = foo.call( obj2 );
  3. 函数是被某个对象引用的方法,那么它的this 绑定的是这个对象:var bar = obj1.foo();
  4. 最后,就是隐式绑定了,这种情况的this 绑定的是全局上下文,在 strict mode(Node)下是 undefined:var bar = foo();

建议

对于闭包惯用的self = this技俩,You Don't Know JS 一书是不提倡的,它的建议是:

Embrace  this-style mechanisms completely, including using bind(..) where necessary, and try to avoid  self = this ...

Function.prototype.bind,它的实现看起来是这样的:

if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError(
"Function.prototype.bind - what is trying " +
"to be bound is not callable"
);
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {
},
fBound = function () {
return fToBind.apply(
(this instanceof fNOP && oThis ? this : oThis),
aArgs.concat(Array.prototype.slice.call(arguments))
)
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}

更多文章请移步我的blog新地址: http://www.moye.me/

[Node.js] 也说this的更多相关文章

  1. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  2. 利用Node.js的Net模块实现一个命令行多人聊天室

    1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...

  3. Node.js:进程、子进程与cluster多核处理模块

    1.process对象 process对象就是处理与进程相关信息的全局对象,不需要require引用,且是EventEmitter的实例. 获取进程信息 process对象提供了很多的API来获取当前 ...

  4. Node.js:理解stream

    Stream在node.js中是一个抽象的接口,基于EventEmitter,也是一种Buffer的高级封装,用来处理流数据.流模块便是提供各种API让我们可以很简单的使用Stream. 流分为四种类 ...

  5. Node.js:Buffer浅谈

    Javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意.Node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该 ...

  6. node.js学习(二)--Node.js控制台(REPL)&&Node.js的基础和语法

    1.1.2 Node.js控制台(REPL) Node.js也有自己的虚拟的运行环境:REPL. 我们可以使用它来执行任何的Node.js或者javascript代码.还可以引入模块和使用文件系统. ...

  7. Node.js npm 详解

    一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...

  8. Node.js入门(一)

    一.Node.js本质上是js的运行环境. 二.可以解析js代码(没有浏览器安全级的限制): 提供系统级的API:1.文件的读写 2.进程的管理 3.网络通信 三.可以关注的四个网站: 1.https ...

  9. Node.js学习笔记——Node.js开发Web后台服务

    一.简介 Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效.Node.j ...

  10. Node.js入门

    开始之前,安利一本正在看的书<站在两个世界的边缘>,作者程浩,上帝丢给他太多理想,却忘了给他完成理想的时间.OK,有兴趣的可以看一看. node.js如标题一样,我也是刚开始接触,大家一起 ...

随机推荐

  1. 使用Facebook的SDK判斷來訪者是否已經按讃并成為本站粉絲團的成員

    今天公司裡要做活動,其中有一項活動內容是要求來訪者按一下facebook粉絲團的讃,按了讃之後贈送現金.Facebook被墻大家眾所周知,在百度搜了一下發現因為被墻的原因導致國內涉及到Facebook ...

  2. c# 压缩文件

    递归实现压缩文件夹和子文件夹. using System; using System.Collections.Generic; using System.Linq; using System.Text ...

  3. Lotus开发之Lotus Notes中域的验证

    一:介绍       Lotus中的域主要有以下的类型:文本,日期/时间,对话框列表,复选框,单选按钮,RTF等等.Lotus中域的验证方式有很多种公式,lotusscript,javascript等 ...

  4. Linux Red hat修改主机名

    步骤一:修改etc/hosts文件内容为 [root@kypt01 /]# cd etc[root@kypt01 etc]# cat hosts127.0.0.1 localhost kypt01 l ...

  5. Oracle Essbase入门系列(四)

    成员存储类型 除了大纲计算,维度成员的另一项重要属性是存储类型,存储类型决定维度成员相关单元格的物理存储方式.在维库中编辑成员的[Data Storage]属性,下拉菜单中可选的5种,再加上Share ...

  6. NSBundle 的理解和 mainBundle

    http://www.360doc.com/content/15/0629/10/20918780_481405304.shtml

  7. Windows Azure 入门系列课程Windows Azure 入门系列课程

    Windows Azure 入门系列课程 https://www.microsoft.com/china/msdn/events/webcasts/shared/webcast/NewSeries/A ...

  8. 转:Android开发之旅:环境搭建及HelloWorld

    http://www.cnblogs.com/skynet/archive/2010/04/12/1709892.html 引言 本系列适合0基础的人员,因为我就是从0开始的,此系列记录我步入Andr ...

  9. [原] JsTree.js

    写自用软件系统时查找到的树列表控件过于庸余,样式难调,故自写一套完整的简易js_TreeTable控件,使用时简单的添加自定义的样式效果即可,特此发布第一个版本. 源码如下: /* * Huashan ...

  10. [原创]实现android知乎、一览等的开场动画图片放大效果

    代码下载地址: https://github.com/Carbs0126/AutoZoomInImageView 知乎等app的开场动画为:一张图片被显示到屏幕的正中央,并充满整个屏幕,过一小段时间后 ...