改变this指向的三位选手,bind,call,apply。


bind,call,apply这三位选手都是原型Function下面的方法,函数原型对象是Function.prototype。比如使用了.push方法,它会去Array的prototype查找是否存在该方法,如果它会顺着原
型链_proto继续找,直到找到null,也就是原型链prototype
chain的最终链接。。。。。有点飞出地球表面。。。。,这三位选手中call、apply是立即调用函数,都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域
,bind则是返回一个绑定this的函数。

三位选手一般会这么用,例如有两个对象a和b

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let a={
name:'a',
getName:function(){
console.log(this.name)
}
}
let b={
name:'b',
getName:function(){
console.log(this.name)
}
}
let bFunc=a.getName.bind(b) //bind将a的this指向了b,所以a里面的getName打印的this.name就是 b
bFunc()
a.getName.call(b) //call将a的this指向b,所以getName打印出b
b.getName.apply(a) //apply将b的this指向a,所以getName打印出a

bind可以传入两次参数,可以let bFunc=a.getName.bind(b,arg1,arg2….)也可以bFunc(arg1,arg2…);call方法接收两个参数,第一个参数和apply的一样,然后传入的参数必须是列举
出来,可以将一个函数的对象上下文从初始的上下文改变为第一个参数指定的新对象,如果没有提供,那么this指向Global;apply接收两个参数,第一个参数是函数运行的作用域,第二个参数
是数组(一整个数组),如果第二个参数不是一个有效数组或者arguments对象,会导致TypeError,如果两个参没有传入的话,那么this指向Global;apply和call和bind就是传参不一样,app
ly和call都是会在调用的时候同时执行调用的函数,但是bind则会返回一个绑定了this的函数.

手动实现

bind

需求:返回一个可以new并且与被调函数具有相同函数体的新函数,调用bind的时候传入参数长度不定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Function.prototype.hBind=function(context){    //在Function的原型上增加一个hBind方法
if (typeof this!=='function') {
throw new TypeError('is not a Function')
return
}
let this_=this,
aArgs=[].slice.call(arguments,1), //arguments是类数组,[]=new Array,利用Array.prototype的slice从arguments里面拿到第一次传入的参数
F=function(){}, //为避免出现引用类型的原型对象出现相互影响创建一个用于中专的函数F
Fb=function(){ //返回的函数,也就是例如let func=obj.bind(obj)的func
return this_.apply(
this instanceof F && context?this:context||window,
aArgs.concat([].slice.call(arguments))
)
}
F.prototype=this.prototype //函数F的原型对象复制this的原型对象
Fb.prototype=new F() //Fb的原型对象再复制F的原型对象,,实现中专
return Fb
}

call

需求:改变this指向和传参不定长度,可以将目标函数作为这个对象的属性,不能增加对象的属性。

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.hCall=function(context,...args){
if (typeof this!=='function') {
throw new TypeError('is not a Function')
return
}
let cont=context||window
let arrs=args?args:[]
let func=Symbol()
cont[func]=this
cont[func](...arrs)
delete cont[func]
}

apply

与call一样,只是第二个参数是数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function.prototype.hApply=function(context,args){
if (this&&this.constructor!==Function) {
throw new TypeError('is not a function')
}
context=context||window
args=args?args:[]
let func=Symbol()
context[func]=this
if (args&&args.constructor===Array) {
context[func](...args)
}else if (!args) {
delete context[func]
}else{
throw new TypeError('CreateListFromArrayLike called on non-object')
}
elete context[func]
}