函数调用位置

与词法作用域相反的是,this的指向由函数运行时决定,它是动态的,随着函数调用位置变化而变化。

要理解 this,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(而

不是声明的位置)。只有仔细分析调用位置才能回答这个问题:这个this到底引用的是什么?

function baz() {
// 当前调用栈是:baz
// 因此,当前调用位置是全局作用域
console.log( "baz" );
bar(); // <-- bar的调用位置
}
function bar() {
// 当前调用栈是baz -> bar
// 因此,当前调用位置在baz中
console.log( "bar" );
foo(); // <-- foo的调用位置
}
function foo() {
// 当前调用栈是baz -> bar -> foo
// 因此,当前调用位置在bar中
console.log( "foo" );
}
baz(); // <-- baz的调用位置

多数现代桌面浏览器都内置了开发者工具,其中包含JavaScript调试器。你可以在工具中给函数的第一行代码设置一个断点,或者直接在第一行代码之前插入一条 debugger;语句。运行代码时,调试器会在那个位置暂停,同时会展示当前位置的函数调用列表,这就是你的调用栈。因此,如果你想要分析this的绑定,使用开发者工具得到调用栈,然后找到栈中第二个元素,这就是真正的调用位置。

this 绑定规则

函数的调用位置决定了this的绑定对象,当我们找到调用位置后,然后判断需要应用下面四条规则中的哪一条。

独立函数调用

独立函数调用,this 指向函数调用位置所在的包含环境对象。

function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2

作为对象的方法调用

当函数作为某个对象的方法被调用时,this 指向这个对象。

function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2

特别注意:虽然函数foo并不属于obj对象,但调用位置使用obj的上下文来调用函数。我一直在强调调用位置的重要性,因为你可能一不留神就会忽略掉它,看下面的列子:

function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名! 步骤1
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global" 步骤2

在步骤1中,变量bar是obj.foo 的一个引用,它实际指向的是函数foo。所以使用bar()与直接使用foo()并没有不同。

使用 .call/ .apply 绑定

每创建一个函数,这个函数就有了两个继承而来的方法:call和apply。

它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个 this 。因为你可以直接指定 this 的绑定对象,因此我们称之为显式绑定。

function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2

new绑定

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行[[原型]]连接,即指向构造函数的原型Foo.prototype。
  3. 这个新对象会绑定到函数调用的 this 。
  4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2

使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上。

优先级

如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call 或者 apply (或者 bind )调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined ,否则绑定到全局对象。

一定要注意,有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略 this 绑定,你可以使用一个DMZ对象,比如 ø = Object.create(null) ,以保护全局对象。

深入理解 js this 绑定机制的更多相关文章

  1. 深入理解 JS 引擎执行机制(同步执行、异步执行以及同步中的异步执行)

    首先明确两点: 1.JS 执行机制是单线程. 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行. 单线程执行带来什么问题? 在JS执行中都是单 ...

  2. 初步理解JS的事件机制

    一.事件流(捕获,冒泡)   事件流:指从页面中接收事件的顺序,有冒泡流和捕获流. 当页面中发生某种事件(比如鼠标点击,鼠标滑过等)时,毫无疑问子元素和父元素都会接收到该事件,可具体顺序是怎样的呢?冒 ...

  3. 27、理解js的继承机制(转载自阮一峰)

    Javascript继承机制的设计思想   作者: 阮一峰 日期: 2011年6月 5日 我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类&qu ...

  4. d3可视化实战04:事件绑定机制

    首先说明,d3支持所有的JS事件——甚至其他代码的自定义事件.这里有一个列表,The MDN Event Reference, 包含了几乎所有浏览器创建的事件类型.大家有需要可以去查看. D3的事件绑 ...

  5. 浅析JS异步执行机制

    前言 JS异步执行机制具有非常重要的地位,尤其体现在回调函数和事件等方面.本文将针对JS异步执行机制进行一个简单的分析. 从一份代码讲起 下面是两个经典的JS定时执行函数,这两个函数的区别相信对JS有 ...

  6. 从一道看似简单的面试题重新理解JS执行机制与定时器

     壹 ❀ 引 最近在看前端进阶的系列专栏,碰巧看到了几篇关于JS事件执行机制的面试文章,因为我在之前一篇 JS执行机制详解,定时器时间间隔的真正含义 博文中也有记录JS执行机制,所以正好用于作为测试自 ...

  7. 10分钟理解JS引擎的执行机制

    首先,请牢记2点: (1) JS是单线程语言 (2) JS的Event Loop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的event loop 1.灵魂三问 (1) JS为什么是单线 ...

  8. 理解JS引擎的执行机制

    首先,请牢记2点: (1) JS是单线程语言 (2) JS的Event Loop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的event loop 1.灵魂三问 : JS为什么是单线程的 ...

  9. 深入理解JS引擎的执行机制

    深入理解JS引擎的执行机制 1.灵魂三问 : JS为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4 ...

随机推荐

  1. 二叉树的遍历——Morris

    在之前的博客中,博主讨论过二叉树的经典遍历算法,包括递归和常规非递归算法,其时间复杂度和空间复杂度均为O(n).Morris算法巧妙地利用了二叉树的线索化思路,将二叉树的遍历算法的空间复杂度降低为O( ...

  2. PTA第五次作业

    #include<stdio.h> #include<math.h> int main () { int n,m,i,j,a; scanf("%d",&am ...

  3. Django入门与实践 17-26章总结

    Django入门与实践-第17章:保护视图 Django 有一个内置的视图装饰器 来避免它被未登录的用户访问: 现在如果用户没有登录,将被重定向到登录页面: 现在尝试登录,登录成功后,应用程序会跳转到 ...

  4. arduino uno r3 + SIM900 + USB打火机 实现电话触发点火

    需求来源 1.儿子过完年6岁,喜欢玩烟花,但是胆子小,于是我就负责点火,从年前26到大年初八,每天晚上要给儿子点鞭炮啊点鞭炮. 2.这边过年要打关门炮跟开门炮,大年初一凌晨还要起来帮老妈点鞭炮,说实在 ...

  5. js正则表达式汇集

    1.只允许中文.字母.数字.中划线.下划线.空格.中文的().英文的()和#,只能以数字.中文.字母.下划线开头,长度为2至40之间 validateTemplateName: function(va ...

  6. 顺序表[A+B->C]

    /*----代码段@映雪------*/ /*采用顺序表存储,改成数组也行*/ int MergeList(SeqList &A,SeqList &B,SeqList &C) ...

  7. gorename: easy refactoring tool for Golang[转]

    To inaugurate this attempt of blog, I’ll talk about gorename a small but incredibly useful tool I ju ...

  8. jbpm(流程管理)

    1.jbpm是什么 JBPM,全称是Java Business Process Management(业务流程管理),它是覆盖了业务流程管理.工作流.服务协作等领域的一个开源的.灵活的.易扩展的可执行 ...

  9. (KMP)Count the string -- hdu -- 3336

    http://acm.hdu.edu.cn/showproblem.php?pid=3336 Count the string Time Limit: 2000/1000 MS (Java/Other ...

  10. bolg迁移

    博客已迁移至:http://www.s0nnet.com 欢迎大家继续关注!!! 2015-7-4