菜单

JS apply、call、bind区别

2018年12月17日 - 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伊芙(Eve)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地图