JavaScript中有四条调用规则,我们可以根据这四条调用规则来判断当前this的指向。
第一条:默认绑定。
function foo(){ console.log(this.a); } var a = 2; foo(); // 2代码很简单,那要怎么给默认绑定一个定义呢?答案就是: 不带任何修饰的函数引用进行调用。
这个时候this指向window.(严格模式下指向undefined)
第二条:隐式绑定
隐式绑定,需要在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接绑定到这个对象上。
function foo(){ console.log(this.a); } var obj = { a:2, foo:foo } obj.foo(); // 2这里在调用的时候增加了一个上下文对象,隐式绑定会把函数调用中的this绑定到这个上下文对象。如果存在多层,按照最近的一层合账。a.b.c.foo(); 则实际指向的是c.
在这里呢,有一种特殊的情况,函数的赋值操作。
function foo(){ console.log(this.a); } function lee(func){ func(); } var obj = { a:2, foo:foo } var a = 1; doFoo(obj.foo); //1看似是隐式绑定,但是函数在传递参数的时候就执行了赋值操作,这个时候再次调用的绑定规则应该为默认绑定,所以指向window.
第三条:显式绑定
这里呢,关于 call和apply以及bind的知识,之前有说过,不清楚的可以先了解一下。这些函数都可以直接指定this的指向,所以是显式绑定。call和apply 仍然可以通过调用时传递的参数去修改当前this的绑定,现在想象一个场景:在一个函数内部要调用一个函数,需要将这个函数绑定在一个obj对象上,但是会有其他因素影响函数绑定到obj对象上,这样一来就会产生丢失绑定的问题。这个时候可以使用硬绑定:
function foo(){ console.log(this.a); } var obj = { a:2, } var bar = function(){ foo.call(obj); } bar(); //2 setTimeout(bar,100); //2 bar.call(window); //2这样一来,就无法修改函数的绑定对象了。
ES5提供了内置方法,Function.prototype.bind方法,每个function都自带bind方法,
var bar = foo.bind(obj)如此就可以使用了。
第四条:new绑定
也就是通过new去实例化一个对象。、function foo(a){ this.a = a; } var bar = new foo(2); console.log(bar.a); //2一言以蔽之: 构造一个新对象,并把它绑定到foo()调用中的this上。
多说一句:这里foo()是一个普通的函数,通过new关键字调用就是对函数的构造调用。由此可得出JS中实际上并不存在所谓的构造函数,只有对函数的构造调用。
优先级:
说了四种方式,既然存在那难免就会有个比较,到底谁更优先呢?function foo(value){ this.a = value; } 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 oo1 = {}; var bar = foo.bind(oo1); bar(2); //001.a = 2 var oo2 = new bar(3); // oo1.a = 2,oo2.a = 3; => new > 显式之前我们看过的硬绑定的实现方式,按道理来说应该是无法修改才对的,而实际使用的时候bind()有自己的实现机制,所以new调用仍然会修改当前指向对象。
这里是传统的四种绑定方式,有没有例外呢,有。感兴趣的可以看下我总结的箭头函数对this的影响,