返回
数据
分类

调用位置就是函数在代码中被调用的位置(而不是声明的位置),就需要找到这个函数的直接调用位置

日期: 2020-01-25 01:42 浏览次数 : 100

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

GitHub地址:

this全面解析
在理解this的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(而不是声明的位置)。通常来说,寻找调用位置就是寻找“函数被调用的位置”,但是做起来并没有这么简单,因为某些编程模式可能会隐藏真正的调用位置。最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数)。我们关心的调用位置就在当前正在执行的函数的前一个调用中。

隐式绑定

this的指向问题应该是让每一个前端er都头疼的问题,我也一样,曾经遇到甚至都是一顿乱猜。最近在研读一些书籍如《你不知道的JavaScript》和《JavaScript语言精粹与编程实践》,让我对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 的调用位置

必赢手机登录网址 ,关于this,一般来说,谁调用了方法,该方法的this就指向谁,如:

隐式绑定

关于this,一般来说,谁调用了方法,该方法的this就指向谁,如:

function foo(){
    console.log(this.a)
}

var a = 3;

var obj = {
    a: 2,
    foo: foo
};

obj.foo(); // 输出2,因为是obj调用的foo,所以foo的this指向了obj,而obj.a = 2

如果存在多次调用,对象属性引用链只有上一层或者说最后一层在调用位置中起作用,如:

function foo() {
    console.log( this.a )
}

var obj2 = { 
    a: 42,
    foo: foo
}

var obj1 = {
    a: 2,
    obj2: obj2
}

obj1.obj2.foo(); // 42

绑定规则

functionfoo(){console.log(this.a)}vara=3;varobj={a:2,foo:foo};obj.foo();//输出2,因为是obj调用的foo,所以foo的this指向了obj,而obj.a=2

隐式丢失

一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说他回应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式。

function foo() {
    console.log( this.a )
}

var obj1 = {
    a: 2,
    foo: foo
}

var bar = obj1.foo; // 函数别名!

var a = "oops, global"; // a是全局对象的属性

bar(); // "oops, global"

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定

一个更微妙、更常见并且更出乎意料的情况发生在传入回调函数时

function foo() {
    console.log( this.a )
}

function doFoo( fn ){
    // fn 其实引用的是 foo
    fn(); // <-- 调用位置!
}

var obj = {
    a: 2,
    foo: foo
}

var a = "oops, global"; // a是全局对象的属性

doFoo( obj.foo ); // "oops, global"

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果和上一个例子一样,如果把函数传入语言内置的函数而不是传入自己声明的函数(如setTimeout等),结果也是一样的

  • 默认绑定
    最常用的函数调用类型:独立函数调用:

如果存在多次调用,对象属性引用链只有上一层或者说最后一层在调用位置中起作用,如:

显式绑定

简单的说,就是指定this,如:call、apply、bind、new绑定等

functionfoo(){console.log(this.a)}varobj2={a:42,foo:foo}varobj1={a:2,obj2:obj2}obj1.obj2.foo();//42

硬绑定

function foo( something ) {
    console.log( this.a, something)
    return this.a + something
}

var obj = {
    a: 2
}

var bar = function() {
    return foo.apply( obj, arguments)
}

var b = bar(3); // 2 3
console.log(b); // 5

这里简单做一下解释: 在bar函数中,foo使用apply函数绑定了obj,也就是说foo中的this将指向obj,与此同时,使用arguments(不限制传入参数的数量)作为参数传入foo函数中;所以在运行bar(3)的时候,首先输出obj.a也就是2和传入的3,然后foo返回了两者的相加值,所以b的值为5

同样,本例也可以使用bind:

function foo( something ) {
    console.log( this.a, something)
    return this.a + something
}

var obj = {
    a: 2
}

var bar = foo.bind(obj)

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

隐式丢失

new绑定

在传统面向类的语言中,使用new初始化类的时候会调用类中的构造函数,但是JS中new的机制实际上和面向类和语言完全不同。

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

  • 创建(或者说构造)一个全新的对象
  • 这个新对象会被执行[[Prototype]]连接
  • 这个新对象会绑定到函数调用的this
  • 如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个新对象 如:
function foo(a){
    this.a = a
}

var bar = new foo(2);
console.log(bar.a); // 2

使用new来调用foo(…)时,我们会构造一个新对象并把它绑定到foo(…)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定。

在代码中,foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。
如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此this会绑定到undefined:

一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说他回应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式。

this的优先级

毫无疑问,默认绑定的优先级是四条规则中最低的,所以我们可以先不考虑它。

隐式绑定和显式绑定哪个优先级更高?我们来测试一下:

function foo(a){
    console.log(this.a)
}

var obj1 = {
    a: 2,
    foo: foo
}

var obj2 = {
    a: 3,
    foo: foo
}

obj1.foo(); // 2
obj2.foo(); // 3

obj1.foo.call(obj2); // 3
obj2.foo.call(obj1); // 2

可以看到,显式绑定优先级更高,也就是说在判断时应当先考虑是否可以存在显式绑定。

现在我们要搞清楚new绑定隐式绑定的优先级谁高谁低 :

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

可以看到new绑定隐式绑定优先级高。但是new绑定显式绑定谁的优先级更高呢?

function foo(something){
    this.a = something
}

var obj1 = {}

var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2

var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3

可以看到,new绑定修改了硬绑定中的this,所以new绑定的优先级比显式绑定更高。

之所以要在new中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new进行初始化时就可以只传入其余的参数。bind(…)的功能之一就是可以把除了第一个参数(第一个参数用于绑定this)之外的其他参数都传给下层的函数(这种技术称为“部分应用”,是“柯里化”的一种)。举例来说:

function foo(p1,p2){
    this.val = p1 + p2;
}

// 之所以使用null是因为在本例中我们并不关心硬绑定的this是什么
// 反正使用new时this会被修改
var bar = foo.bind(null,'p1');

var baz = new bar('p2');

baz.val; // p1p2
}

柯里化:在直觉上,柯里化声称“如果你固定某些参数,你将得到接受余下参数的一个函数”。所以对于有两个变量的函数yx,如果固定了 y = 2,则得到有一个变量的函数 2x

function foo() {
    "use strict";
    console.log(this.a);
}
var a = 2;
foo(); // TypeError: this is undefined
functionfoo(){console.log(this.a)}varobj1={a:2,foo:foo}varbar=obj1.foo;//函数别名!vara=oops,global;//a是全局对象的属性bar();//oops,global

This在箭头函数中的应用

箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。

我们来看一下箭头函数的词法作用域:

function foo() {
    // 返回一个箭头函数
    return (a) => {
        // this继承自foo()
        console.log(this.a)
    };
}

var obj1 = {
    a: 2
};

var obj2 = {
    a: 3
};

var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是3!

foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。(new也不行!)

这里有一个微妙但是非常重要的细节,虽然this的绑定规则完全取决于调用位置,但是只有foo()运行在非strict mode下时,默认绑定才能绑定到全局对象;严格模式下与foo()的调用位置无关:

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定

总结

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

  1. 由new调用?绑定到新创建的对象。
  2. 由call或者apply(或者bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到undefined,否则绑定到全局对象。
function foo() {
    console.log(this.a);
}
var a = 2;
(function () {
    "use strict";
    foo(); // 2
})();

一个更微妙、更常见并且更出乎意料的情况发生在传入回调函数时:

  • 隐式绑定
    另一条需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含,不过这种说法可能会造成一些误导。
functionfoo(){console.log(this.a)}functiondoFoo(fn){//fn其实引用的是foofn();//--调用位置!}varobj={a:2,foo:foo}vara=oops,global;//a是全局对象的属性doFoo(obj.foo);//oops,global

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果和上一个例子一样,如果把函数传入语言内置的函数而不是传入自己声明的函数(如setTimeout等),结果也是一样的

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

显式绑定

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因为调用foo()时this被绑定到obj,因此this.a和obj.a是一样的。
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。

简单的说,就是指定this,如:call、apply、bind、new绑定等

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

硬绑定

隐式丢失
一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式。

functionfoo(something){console.log(this.a,something)returnthis.a+something}varobj={a:2}varbar=function(){returnfoo.apply(obj,arguments)}varb=bar(3);//23console.log(b);//5
function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"

这里简单做一下解释: 在bar函数中,foo使用apply函数绑定了obj,也就是说foo中的this将指向obj,与此同时,使用arguments(不限制传入参数的数量)作为参数传入foo函数中;所以在运行bar(3)的时候,首先输出obj.a也就是2和传入的3,然后foo返回了两者的相加值,所以b的值为5

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
一种更微妙、更常见并且更出乎意料的情况发生在传入回调函数时:

同样,本例也可以使用bind:

function foo() {
    console.log(this.a);
}
function doFoo(fn) {
    // fn 其实引用的是foo
    fn(); // <-- 调用位置!
}
var obj = {
    a: 2,
    foo: foo
};
var a = "oops, global"; // a 是全局对象的属性
doFoo(obj.foo); // "oops, global"
functionfoo(something){console.log(this.a,something)returnthis.a+something}varobj={a:2}varbar=foo.bind(obj)varb=bar(3);//23console.log(b);//5

显示绑定
用call和apply

new绑定

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

在传统面向类的语言中,使用new初始化类的时候会调用类中的构造函数,但是JS中new的机制实际上和面向类和语言完全不同。

通过foo.call(..),我们可以在调用foo时强制把它的this绑定到obj上。
如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当做this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(..)、new Boolean(..)或者new Number(..))。这通常被称为“装箱”。
显示绑定仍然无法解决我们之前提出的丢失绑定问题。但是显示绑定的一个变种可以解决这个问题。

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

  • 硬绑定

创建(或者说构造)一个全新的对象

这个新对象会被执行[[Prototype]]连接

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2
};
var bar = function () {
    foo.call(obj);
};
bar(); // 2
setTimeout(bar, 100); // 2
// 硬绑定的bar 不可能再修改它的this
bar.call(window); // 2

这个新对象会绑定到函数调用的this

我们创建了函数bar(),并在它的内部手动调用了foo.call(obj),因此强制把foo的this绑定到了obj。无论之后如何调用函数bar,它总会手动在obj上调用foo。这种绑定是一种显示的强制绑定,因此我们称之为硬绑定。
硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接受到的所有值:

如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个新对象 如:

function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}
var obj = {
    a: 2
};
var bar = function () {
    return foo.apply(obj, arguments);
};
var b = bar(3); // 2 3
console.log(b); // 5
functionfoo(a){this.a=a}varbar=newfoo(2);console.log(bar.a);//2

另一种使用方法是创建一个可以重复使用的辅助函数:

使用new来调用foo(…)时,我们会构造一个新对象并把它绑定到foo(…)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定。

function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}
// 简单的辅助绑定函数
function bind(fn, obj) {
    return function () {
        return fn.apply(obj, arguments);
    };
}
var obj = {
    a: 2
};
var bar = bind(foo, obj);
var b = bar(3); // 2 3
console.log(b); // 5

this的优先级

由于硬绑定是一种非常常用的模式,所以在ES5中提供了内置的方法Function.prototype.bind,它的用法如下:

毫无疑问,默认绑定的优先级是四条规则中最低的,所以我们可以先不考虑它。

function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}
var obj = {
    a: 2
};
var bar = foo.bind(obj);
var b = bar(3); // 2 3
console.log(b); // 5

隐式绑定和显式绑定哪个优先级更高?我们来测试一下:

bind(..)会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。

functionfoo(a){console.log(this.a)}varobj1={a:2,foo:foo}varobj2={a:3,foo:foo}obj1.foo();//2obj2.foo();//3obj1.foo.call(obj2);//3obj2.foo.call(obj1);//2
  • API调用的“上下文”
    第三方库的许多函数,以及JavaScript语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为“上下文”(context),其作用和bind(..)一样,确保你的回调函数指定的this。

可以看到,显式绑定优先级更高,也就是说在判断时应当先考虑是否可以存在显式绑定。

现在我们要搞清楚new绑定和隐式绑定的优先级谁高谁低 :

function foo(el) {
    console.log(el, this.id);
}
var obj = {
    id: "awesome"
};
// 调用foo(..) 时把this 绑定到obj
[1, 2, 3].forEach(foo, obj);
    // 1 awesome 2 awesome 3 awesome
functionfoo(something){this.a=something}varobj1={foo:foo}varobj2={}obj1.foo(2);console.log(obj1.a);//2obj1.foo.call(obj2,3);console.log(obj2.a);//3varbar=newobj1.foo(4)console.log(obj1.a);//2console.log(bar.a);//4

这些函数实际上就是通过call(..)或者apply(..)实现了显式绑定,这样可以少写一些代码。

可以看到new绑定比隐式绑定优先级高。但是new绑定和显式绑定谁的优先级更高呢?

new绑定
包括内置对象函数(比如Number(..))在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。
使用new来调用函数,或者说发生构造函数低啊用时,会自动执行下面的操作:

functionfoo(something){this.a=something}varobj1={}varbar=foo.bind(obj1);bar(2);console.log(obj1.a);//2varbaz=newbar(3);console.log(obj1.a);//2console.log(baz.a);//3
  • 创建(构建)一个全新的对象。
  • 这个新对象会被执行[[原型]]连接。
  • 这个新对象会绑定到函数调用的this。
  • 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

可以看到,new绑定修改了硬绑定中的this,所以new绑定的优先级比显式绑定更高。

之所以要在new中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new进行初始化时就可以只传入其余的参数。bind(…)的功能之一就是可以把除了第一个参数(第一个参数用于绑定this)之外的其他参数都传给下层的函数(这种技术称为“部分应用”,是“柯里化”的一种)。举例来说:

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
functionfoo(p1,p2){this.val=p1+p2;}//之所以使用null是因为在本例中我们并不关心硬绑定的this是什么//反正使用new时this会被修改varbar=foo.bind(null,p1);varbaz=newbar(p2);baz.val;//p1p2}

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

柯里化:在直觉上,柯里化声称“如果你固定某些参数,你将得到接受余下参数的一个函数”。所以对于有两个变量的函数yx,如果固定了 y = 2,则得到有一个变量的函数 2x

优先级
显示绑定优先级高于隐式绑定
new绑定比隐式绑定优先级高
new绑定比硬绑定高
为什么要在new中使用硬绑定函数呢?直接使用普通函数不是更简单吗?
之所以要在new中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new进行初始化时就可以只传入其余的参数。bind(..)的功能之一就是可以把除了第一个参数(第一个参数用于绑定this)之外的其他参数都传给下层的函数(这种技术称为“部分应用”):

This在箭头函数中的应用

function foo(p1, p2) {
    this.val = p1 + p2;
}
// 之所以使用null 是因为在本例中我们并不关心硬绑定的this 是什么
// 反正使用new 时this 会被修改
var bar = foo.bind(null, "p1");
var baz = new bar("p2");
baz.val; // p1p2

箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。

判断this
根据优先级来判断函数在某个调用位置应用的是哪条规则:
1、函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
2、 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。var bar = foo.call(obj2)
3、函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。var bar = obj1.foo()
4、如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。var bar = foo()

我们来看一下箭头函数的词法作用域:

被忽略的this
如果你把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:

functionfoo(){//返回一个箭头函数return(a)={//this继承自foo()console.log(this.a)};}varobj1={a:2};varobj2={a:3};varbar=foo.call(obj1);bar.call(obj2);//2,不是3!
function foo() {
    console.log(this.a);
}
var a = 2;
foo.call(null); // 2

foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。(new也不行!)

什么情况下会传入null呢?一种非常常见的做法是使用apply(..)来“展开”一个数组,并当做参数传入一个函数。类似地,bind(..)可以对参数进行柯里化(预先设置一些参数),这种方法有时非常有用:

总结

function foo(a, b) {
    console.log("a:" + a + ", b:" + b);
}
// 把数组“展开”成参数
foo.apply(null, [2, 3]); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind(null, 2);
bar(3); // a:2, b:3

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

这两种方法都需要传入一个参数当做this的绑定对象。如果函数并不关心this的话,你仍然需要传入一个占位值,这时null可能是一个不错的选择,就像代码所示的那样。
在ES6中,可以用...操作符代替apply(..)来“展开”数组,foo(..[1,2])和foo(1,2)是一样的,这样可以避免不必要的this绑定。可惜,在ES6中没有柯里化的相关语法,因此还是需要使用bind(..)。

由new调用?绑定到新创建的对象。

更安全的this
使用null来忽略this绑定可能产生一些副作用。一种“更安全”的做法是传入一个特殊的对象,把this绑定到这个对象不会对你的程序产生任何副作用。在JavaScript中创建一个空对象最简单的方法都是Object.create(null)。Object.create(null)和{}很像,但是并不会创建Object.prototype这个委托,所以它比{}"更空":

由call或者apply(或者bind)调用?绑定到指定的对象。

function foo(a, b) {
    console.log("a:" + a + ", b:" + b);
}
// 我们的DMZ 空对象
var ø = Object.create(null);
// 把数组展开成参数
foo.apply(ø, [2, 3]); // a:2, b:3
// 使用bind(..) 进行柯里化
var bar = foo.bind(ø, 2);
bar(3); // a:2, b:3

由上下文对象调用?绑定到那个上下文对象。

间接引用
另一个需要注意的是,你有可能会创建一个函数的“间接引用”,在这种情况下,调用这个函数会应用默认绑定规则。

默认:在严格模式下绑定到undefined,否则绑定到全局对象。

function foo() {
    console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2

赋值表达式p.foo=0.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者0.foo()。根据我们之前说过的,这里会应用默认绑定。

this词法

function foo() {
    // 返回一个箭头函数
    return (a) => {
        //this 继承自foo()
        console.log(this.a);
    };
}
var obj1 = {
    a: 2
};
var obj2 = {
    a: 3
};
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是3 !

foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。(new也不行!)
箭头函数最常用于回调函数中,例如事件处理器或者定时器:

function foo() {
    setTimeout(() => {
        // 这里的this 在此法上继承自foo()
        console.log(this.a);
    }, 100);
}
var obj = {
    a: 2
};
foo.call(obj); // 2

必赢手机登录网址 1

雨下了走好路,这句话我记住