绑定规则
默认绑定
如下代码
var a = 2;
function foo() {
console.log(this.a);
}
foo() //2foo执行时,this.a 被解析成了全局对象a,函数调用时应用了this的默认绑定,因此this指向全局对象。
如果使用严格模式(strick mode),则不能将全局对象作为绑定默认,因此this会绑定到undefined
function foo(){
'use strict'
console.log(this.a)
}
var a = 2;
foo(); //TypeError: this is undefined其次,在严格模式下调用,默认绑定也会生效
function foo(){
console.log(this.a)
}
var a = 2;
(function(){
'use strict'
foo() //2
})()隐式绑定
函数的调用位置是否含有上下文对象,或者说是否被某个对象拥有或者包含,隐式绑定规则会把函数中的this绑定到这个上下文对象。
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
obj.foo()//2需要注意的是,对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
function foo(){
console.log(this.a)
}
var obj1={
a:2;
foo:foo
}
var obj2={
a:3,
obj1:obj1
}
obj2.obj1.foo() //2
一个常见的this绑定问题就是被隐式绑定的函数会丢失绑定函数,也就是说他会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式。
function foo(){
console.log(this.a)
}
var obj={
a:2,
foo:foo
}
var a = 'global';
var bar = obj.foo;
bar() //global虽然bar 是obj.foo的一个引用,但实际上,它引用的是foo函数本身,因此,此时的bar是一个不带任何修饰的函数调用,因此应用了默认绑定。同理,传入回调函数是也是如此,如下代码
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
}
var a='global'
function bar(fn){
fn();
}
bar(obj.foo) //global将函数传入内置函数也是同理,
function foo(){
console.log(this.a)
}
var a = 'global'
var obj={
a:2,
foo:foo
}
settimeout(function(){
obj.foo()
},1000) // global显式绑定
与隐式绑定不同的是,显示绑定无需在一个对象内部 包含一个指向函数的属性,并通过这个属性间接引用函数,而是通过使用函数的call和aplly方法。此两者的第一给参数是一个对象,在调用函数时将其绑定到this。如下代码
function foo(){
console.log(this.a)
}
var a='global';
var obj={
a:2,
}
foo.call(obj) //2需要注意的是,显式绑定依然无法解决丢失this绑定的问题,尽管call中的this指向了obj,但是,参数fn依然是函数别名,依然是没有任何修饰的函数调用。如下代码
function foo(){
console.log(this.a);
}
var a='global'
var obj={
a:2,
foo:foo
}
var bar = function(fn){
fn()
}
bar.call(obj,obj.foo) //global
但是我们可以使用显式绑定的变种来解决这个问题
function foo(){
console.log(this.a)
}
var a='global'
var obj={
a:2,
}
var bar=function(fn){
fn.call(obj)
}
bar(); //2
setTimeout(bar,1000); //2需要注意到是,硬绑定的函数不可能再修改它的this,因为我们在该函数的内部手动调用来foo.call(obj),因此强制把foo的this绑定到了obj。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。
创建一个可以复用的辅助函数
function bind(fn,obj) {
return function(){
return fn.apply(obj,arguments)
}
}
foo(something){
console.log(this.a,something)
return this.a + something
}
var obj={
a:2
}
var bar = bind(foo,obj);
var b = bar(3) //2 3
console.log(b) //5
由于硬绑定是一个非常常用的模式,所以ES5提供了内置的Function.prototype.bind方法,用法如下:
function foo(someting){
console.log(this.a,something)
return this.a+something
}
foo.bind()API调用的上下文
第三方库的许多函数,以及JavaScript语言和宿主环境中许多新的内置函数,都提供了一些可选参数,通常被称作上下文,其作用和bind()一样,确保你的回调函数使用指定的this,举例
function foo(el){
console.log(el,this.a)
}
var a='global'
var obj={
a:2
}
[1,2,3].forEack(foo,obj)new绑定
使用new来调用函数,会自动执行如下操作
- 创建一个全新的对象
- 这个对象会被执行[[Prototype]]连接
- 这个新对象会绑定到函数调用的this
- 如果这个函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
思考下面的代码
function foo(a){
this.a=a
}
var bar=new foo(2)
console.log(bar.a) //2绑定例外
- 被忽略的this
如果把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值会在调用时被忽略,实际应用的是默认绑定规则。一种非常常见的做法是使用apply(..)来‘展开’一个数组,并当作一个参数传入一个函数,类似的,bind(..)也可以对参数进行柯里化(预先设置一些参数)function foo(){ console.log(this.a) } var a=2 foo.call(null) //2function foo(a,b){ console.log('a:'+a+ ',b:'+b ) } foo.apply(null,[2,3]) a:2,b:3 var bar = foo.bind(null,2) bar(3) //a:2,b:3 - 间接引用
this词法
以上规则都无法作用于箭头函数,此函数根据外层作用域来决定this
function foo(){
return ()=>{
//this继承自foo
console.log(this.a)
}
}
var obj1={
a:2
}
var obj2={
a:3
}
var bar=foo.call(obj1)
bar.call(obj2)//2
foo函数内部的箭头函数会捕捉调用时的foo()的this,由于foo()的this绑定到obj1,bar的this也会绑定到obj1,箭头函数的this无法被修改。new也不行。