菜单

JS主旨序列:浅谈 call apply 与 bind

2018年12月16日 - JavaScript

JS主旨体系:浅谈 call apply 与 bind

2016/03/01 · JavaScript
· apply,
bind,
call

原文出处: 一像素   

以JavaScript中,call、apply和bind
是Function对象从带的老六只章程,那两只章程的重点意图是改函数中之this指向,从而得以直达接花移木的效用。本文将本着这三单办法举办详细的上课,并列出七只经应用场景。

 

call(thisArgs [,args…])


拖欠法可传递一个thisArgs参数和一个参数列表,thisArgs指定了函数在运行期的调用者,也不怕是函数中之this对象,而参数列表会吃传调用函数中。thisArgs的取值有以下4种植处境:

(1) 不传染,或者传null,undefined, 函数着之this指为window对象

(2) 传递另一个函数的函数叫做,函数中的this指于这函数的援

(3)
传递字符串、数值或布尔路等基础项目,函数中之this指于这相应的包装对象,如
String、Number、Boolean

(4) 传递一个目的,函数中的this指于者目标

JavaScript

function a(){ console.log(this); //输出函数a中的this对象 } function
b(){} //定义函数b var obj = {name:’onepixel’}; //定义对象obj a.call();
//window a.call(null); //window a.call(undefined);//window a.call(1);
//Number a.call(”); //String a.call(true); //Boolean a.call(b);//
function b(){} a.call(obj); //Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function a(){
    console.log(this); //输出函数a中的this对象
}
function b(){} //定义函数b
 
var obj = {name:’onepixel’}; //定义对象obj
 
a.call(); //window
a.call(null); //window
a.call(undefined);//window
a.call(1); //Number
a.call(”); //String
a.call(true); //Boolean
a.call(b);// function b(){}
a.call(obj); //Object

眼看是call的核心成效,它同意而于一个目的上调用该对象没定义的办法,并且这多少个点子可以看该对象被的性能,至于那样做来啊利益,我要会再一次张嘴,我们先看一个大概的例子:

JavaScript

var a = { name:’onepixel’, //定义a的属性 say:function(){ //定义a的方法
console.log(“Hi,I’m function a!”); } }; function b(name){
console.log(“Post params: “+ name); console.log(“I’m “+ this.name);
this.say(); } b.call(a,’test’); >> Post params: test I’m onepixel
I’m function a!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var a = {
 
    name:’onepixel’, //定义a的属性
 
    say:function(){ //定义a的方法
        console.log("Hi,I’m function a!");
    }
};
 
function b(name){
    console.log("Post params: "+ name);
    console.log("I’m "+ this.name);
    this.say();
}
 
b.call(a,’test’);
>>
Post params: test
I’m onepixel
I’m function a!

当执行b.call时,字符串test作参数传递给了函数b,由于call的企图,函数b中的this指为了对象a,
因而一定给调用了靶a上的函数b,而实际上a中从未概念b 。

 

apply(thisArgs[,args[]])


apply和call的唯一区别是亚单参数的传递情势各异,apply的次独参数必须是一个再三组,而call允许传递一个参数列表。值得您注意的凡,即便apply接收的是一个参数数组,但在传递让调用函数时,却是为参数列表的花样传递,我们看个简易的例子:

JavaScript

function b(x,y,z){ console.log(x,y,z); } b.apply(null,[1,2,3]); // 1 2
3

1
2
3
4
5
function b(x,y,z){
    console.log(x,y,z);
}
 
b.apply(null,[1,2,3]); // 1 2 3

apply的这特性很要紧,我们碰面在脚的行使场景中涉嫌这么些特性。

 

bind(thisArgs [,args…])


bind是ES5新加的一个方,它的传参和call类似,但同时与call/apply有着显明的两样,即调用call或apply都相会自动执行相应的函数,而bind不汇合履行相应的函数,只是重返了针对性函数的援。粗略同看,bind似乎比call/apply要走下坡路一些,这ES5胡还要引入bind呢?

其实,ES5引入bind的着实目标是为着弥补call/apply的不足,由于call/apply会对目标函数自动执行,从而致使它不能在波绑定函数中动用,因为事件绑定函数不需我们手动执行,它是当事变于触发时是因为JS内部自行执行之。而bind在实现转移函数this的同时又非会合活动执行对象函数,因而好圆的缓解上述问题,看一个例证就是可知懂:

JavaScript

var obj = {name:’onepixel’}; /** *
给document添加click事件监听,并绑定onClick函数 *
通过bind方法设置onClick的this为obj,并传递参数p1,p2 */
document.add伊夫ntListener(‘click’,onClick.bind(obj,’p1′,’p2′),false);
//当点击网页时点并尽 function onClick(a,b){ console.log( this.name,
//onepixel a, //p1 b //p2 ) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {name:’onepixel’};
 
/**
* 给document添加click事件监听,并绑定onClick函数
* 通过bind方法设置onClick的this为obj,并传递参数p1,p2
*/
document.addEventListener(‘click’,onClick.bind(obj,’p1′,’p2′),false);
 
//当点击网页时触发并执行
function onClick(a,b){
    console.log(
            this.name, //onepixel
            a, //p1
            b  //p2
    )
}

当点击网页时,onClick给硌执行,输出onepixel p1 p2,
表明onClick中的this被bind改变成了obj对象,为了对bind举办深切的喻,我们来拘禁一下bind底polyfill实现:

JavaScript

if (!Function.prototype.bind) { Function.prototype.bind = function
(oThis) { var aArgs = Array.prototype.slice.call(arguments, 1), fToBind
= this, //this在此地针对的凡目的函数 fBound = function () { return
fToBind.apply( //假设外部执行var obj = new
fBound(),则拿obj作为最终之this,吐弃选择oThis this instanceof fToBind ?
this //此时的this就是new出之obj : oThis || this,
//即使传递的oThis无效,就拿fBound的调用者作为this
//将通过bind传递的参数与调用时传递的参数举行统一,并当最终之参数传递
aArgs.concat(Array.prototype.slice.call(arguments))); };
//将目的函数的原型对象拷贝到新函数中,因为目的函数有或让看作构造函数使用
fBound.prototype = this.prototype; //重回fBond的援,由外部按需要调用
return fBound; }; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (!Function.prototype.bind) {
    Function.prototype.bind = function (oThis) {
        var aArgs = Array.prototype.slice.call(arguments, 1),
            fToBind = this, //this在这里指向的是目标函数
            fBound = function () {
                return fToBind.apply(
                    //如果外部执行var obj = new fBound(),则将obj作为最终的this,放弃使用oThis
                    this instanceof fToBind
                            ? this  //此时的this就是new出的obj
                            : oThis || this, //如果传递的oThis无效,就将fBound的调用者作为this
 
                    //将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
                    aArgs.concat(Array.prototype.slice.call(arguments)));
            };
 
        //将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
        fBound.prototype = this.prototype;
 
        //返回fBond的引用,由外部按需调用
        return fBound;
    };
}

拔取场景一样:继承


世家知道,JavaScript中没有诸如Java、C#等高等语言中的extend
关键字,由此JS中从未继承的定义,假诺一定假使延续的话,call和apply可以兑现此效用:

JavaScript

function Animal(name,weight){ this.name = name; this.weight = weight; }
function Cat(){ Animal.call(this,’cat’,’50’);
//Animal.apply(this,[‘cat’,’50’]); this.say = function(){
console.log(“I am ” + this.name+”,my weight is ” + this.weight); } } var
cat = new Cat(); cat.say();//I am cat,my weight is 50

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Animal(name,weight){
   this.name = name;
   this.weight = weight;
}
 
function Cat(){
    Animal.call(this,’cat’,’50’);
  //Animal.apply(this,[‘cat’,’50’]);
 
   this.say = function(){
      console.log("I am " + this.name+",my weight is " + this.weight);
   }
}
 
var cat = new Cat();
cat.say();//I am cat,my weight is 50

当通过new运算符发生了cat时,Cat中之this就对准了cat对象(关于new运算符的授课,请参考:http://www.cnblogs.com/onepixel/p/5043523.html),而后续的首如若在乎Cat中施行了Animal.call(this,’cat’,’50’)
那句话,在call中以this作为thisArgs参数传递,于是Animal方法吃之this就对准了Cat中的this,而cat中之this指向的凡cat对象,所以Animal中之this指向的便是cat对象,在Animal中定义了name和weight属性,就一定给在cat中定义了那个性,由此cat对象就享有了Animal中定义的性质,从而达成了继往开来的目标。

 

使场景二:移花接木


在叙下面的情前边,我们先是来认一下JavaScript中的一个非标准专业术语:ArrayLike(类数组/伪数组)

ArrayLike
对象就是拥有数组的一律有些作为,在DOM中就展现出,而jQuery的崛起让ArrayLike在JavaScript中大放异彩。ArrayLike对象的精密在于它和JS原生的Array类似,可是她是随机构建的,它出自开发者对JavaScript对象的扩大,也不怕是说:对于她的原型(prototype)大家可随意定义,而休汇合传及JS原生的Array。

ArrayLike对象在JS中于周边利用,比如DOM中的NodeList,
函数惨遭之arguments都是类数组对象,那些目的像数组一样存储方每一个要素,但她从不操作数组的道,而我们可因此call将反复组的某些方法移接暨ArrayLike对象,从而达到操作其元素的目的。比如大家可如此一切历函数中的arguments:

JavaScript

function test(){ //检测arguments是否为Array的实例 console.log( arguments
instanceof Array, //false Array.isArray(arguments) //false );
//判断arguments是否爆发forEach方法 console.log(arguments.forEach);
//undefined // 将数组中之forEach应用至arguments上
Array.prototype.forEach.call(arguments,function(item){
console.log(item); // 1 2 3 4 }); } test(1,2,3,4);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function test(){
    //检测arguments是否为Array的实例
    console.log(
            arguments instanceof Array, //false
            Array.isArray(arguments)  //false
    );
    //判断arguments是否有forEach方法
    console.log(arguments.forEach); //undefined
 
    // 将数组中的forEach应用到arguments上
    Array.prototype.forEach.call(arguments,function(item){
        console.log(item); // 1 2 3 4
    });
 
}
test(1,2,3,4);

除此之外,对于apply而言,大家地点提到了其独有的一个特色,即apply接收的是屡组,在传递让调用函数的上是以参数列表传递的。
这个特点让apply看起比call
略胜一筹,比如来这般一个现象:给得一个数组[1,3,4,7],然后求数组中的极深因素,而你精晓,数组中连不曾博得最特别价值的情势,一般景观下,你需要通过编制代码来实现。而我辈明白,Math对象中起一个赢得最深价值的点子,即Math.max(),
max方法需要传递一个参数列表,然后回到那多少个参数中之无比充裕价值。而apply不仅可以用Math对象的max方法以及外对象上,还可将一个数组转化为参数列表传递让max,看代码就可以一目了解:

JavaScript

var arr = [2,3,1,5,4]; Math.max.apply(null,arr); // 5

1
2
3
var arr = [2,3,1,5,4];
 
Math.max.apply(null,arr); // 5

上述就是是call和apply相比经典的几独应用场景,熟习领悟这一个技能,并拿这个特色应用及公的其实项目受到,会如你的代码看起更深!

2 赞 12 收藏
评论

图片 1

obj.call(thisObj, arg1,arg2...)
obj.apply(thisObj, [arg1,arg2,arg3...])
obj.bind(thisObj, arg1,arg2...)

function add(j, k){
    return j+k;
}

function sub(j, k){
    return j-k;
}

以支配高运行:

add(5,3); //8
add.call(sub, 5, 3); //8
add.apply(sub, [5, 3]); //8

sub(5, 3); //2
sub.call(add, 5, 3); //2
sub.apply(add, [5, 3]); //2

同样是add()和sub():

add.bind(sub, 5, 3); //不再返回8
add.bind(sub, 5, 3)(); //8

以call调用原生方法

var a = {0:1, 1:"zohar", length: 2}; 
a.slice(); //TypeError: a.slice is not a function
Array.prototype.slice.call(a);//[1, "zohar"]

使call实现连续

var parent = function () {
  this.url= 'zohar.com.cn',
  this.name= 'zohar'
}
var child = {}
console.log(child); // {}
parent.call(child);
console.log(child); // {url: "zohar.com.cn", name: "zohar"}

原作者:飞鸿影~
出处:http://52fhy.cnblogs.com/

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图