JS主旨种类

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

JS主旨种类。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运算符的教师,请参见:),而继续的重若是介于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

在JavaScript中,call、apply和bind
是Function对象自带的七个方法,那多少个法子的主要意义是改变函数中的this指向,从而能够达到`接花移木`的作用。本文将对那多个艺术开展详尽的教学,并列出多少个经典应用场景。 

浅谈javascript中的call、apply、bind,applybind

在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指向那一个目标

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的主干功用,它同意你在一个对象上调用该目的没有概念的主意,并且那么些主意可以访问该对象中的属性,至于那样做有啥样好处,我待会再讲,大家先看一个简单的例子:

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接收的是一个参数数组,但在传递给调用函数时,却是以参数列表的样式传递,大家看个简易的例子:

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的同时又不会自动执行对象函数,由此得以健全的化解上述难题,看一个例证就能领略:

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已毕:

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可以已毕这些意义:

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运算符的任课,请参考:’)
那句话,在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:

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,看代码就能看清:

var arr = [2,3,1,5,4];

Math.max.apply(null,arr); // 5

上述便是call和apply相比经典的多少个使用场景,熟稔精晓那么些技能,并把这几个特点应用到您的实际项目中,会使你的代码看起来更为有意思!

四个函数都是Function对象自带的多个方法,主要功效是改变函数中this的指向。

call(thisArgs [,args…])

您可能感兴趣的篇章:

  • js apply/call/caller/callee/bind使用方式与差异分析
  • javascript中call,apply,bind的用法比较分析
  • 浅谈javascript中call()、apply()、bind()的用法
  • 开启Javascript中apply、call、bind的用法之旅形式
  • 跟自身就学javascript的call(),apply(),bind()与回调
  • 浅谈javascript的call()、apply()、bind()的用法

在JavaScript中,call、apply和bind
是Function对象自带的多少个方式,那两个方式的基本点成效是改变函数中…

call()

该形式可以传递一个thisArgs参数和一个参数列表,thisArgs指定了函数在运行期的调用者,也就是函数中的this对象,而参数列表会被传到调用函数中。thisArgs的取值有以下4种情景:

语法 fun.call(thisArg[, arg1[, arg2[, …]]])

(1) 不传,或者传null,undefined, 函数中的this指向window对象

该办法可以传递一个thisArgs参数和一个参数列表,thisArgs指定了函数在运行期的调用者,也就是函数中的this对象,而参数列表会被传到调用函数中。

(2) 传递另一个函数的函数名,函数中的this指向那个函数的引用

透过 call
方法,你可以在一个目标上借用另一个目的上的主意,比如Object.prototype.toString.call([]),就是一个Array对象借用了Object对象上的措施。

(3)
传递字符串、数值或布尔类型等基础项目,函数中的this指向其对应的包装对象,如
String、Number、Boolean

thisArgs的取值有以下4种处境:

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

(1) 不传,或者传null,undefined, 函数中的this指向window对象
(2) 传递另一个函数的函数名,函数中的this指向那一个函数的引用
(3)
传递字符串、数值或布尔类型等基础项目,函数中的this指向其相应的卷入对象,如
String、Number、Boolean
(4) 传递一个目的,函数中的this指向这么些目的

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
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的中坚功用,它同意你在一个目的上调用该目标没有概念的措施,并且那么些方法可以访问该目的中的属性,至于那样做有啥样便宜,我待会再讲,大家先看一个粗略的事例:

那是call的中坚功效,它同意你在一个对象上调用该目的没有概念的法门,并且那几个办法能够访问该对象中的属性。

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 。

A、

apply(thisArgs[,args[]])

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');     //将b的this指向a,参数是b的

apply和call的绝无仅有不一致是第三个参数的传递形式分歧,apply的第四个参数必须是一个数组,而call允许传递一个参数列表。值得你注意的是,纵然apply接收的是一个参数数组,但在传递给调用函数时,却是以参数列表的样式传递,大家看个大概的例证:

输出:

function b(x,y,z){
 console.log(x,y,z);
}

b.apply(null,[1,2,3]); // 1 2 3
//Post params: test
//I'm onepixel
//I'm function a!

apply的那一个特点很要紧,大家会在下边的采取场景中提到那一个特性。

B、

bind(thisArgs [,args…])

行使call方法调用匿名函数

bind是ES5新增的一个方法,它的传参和call类似,但又和call/apply有着明显的不相同,即调用call或apply都会自行执行相应的函数,而bind不会履行相应的函数,只是再次来到了对函数的引用。粗略一看,bind如同比call/apply要滞后一些,那ES5怎么还要引入bind呢?

在下例中的for循环体内,我们创设了一个匿名函数,然后经过调用该函数的call方法,将各种数组元素作为指定的this值执行了相当匿名函数。这几个匿名函数的严重性目标是给各种数组元素对象添加一个print方法,这么些print方法能够打印出各要素在数组中的正确索引号。当然,那里不是必须得让数组元素作为this值传入那多少个匿名函数(普通参数就可以),目的是为着演示call的用法。

实质上,ES5引入bind的真正目标是为着弥补call/apply的阙如,由于call/apply会对目的函数自动执行,从而导致它不可以在事件绑定函数中应用,因为事件绑定函数不须求大家手动执行,它是在事件被触发时由JS内部自行执行的。而bind在促成转移函数this的同时又不会活动执行对象函数,因而得以圆满的缓解上述难点,看一个事例就能了然:

var animals = [
  {species: 'Lion', name: 'King'},
  {species: 'Whale', name: 'Fail'}
];

for (var i = 0; i < animals.length; i++) {
  (function (i) { 
    this.print = function () { 
      console.log('#' + i  + ' ' + this.species + ': ' + this.name); 
    } 
    this.print();
  }).call(animals[i], i);
}
//#0 Lion: King
//#1 Whale: Fail
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达成:

澳门葡京 2

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;
 };
}

澳门葡京 3

使用场景一:**继承**

apply()

世家清楚,JavaScript中尚无诸如Java、C#等高等语言中的extend
关键字,由此JS中没有继承的定义,要是一定要三番五次的话,call和apply能够兑现那么些职能:

语法:fun.apply(thisArg[, argsArray])

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

apply和call的唯一不一样是第四个参数的传递格局各异,apply的第四个参数必须是一个数组,而call允许传递一个参数列表。值得您放在心上的是,纵然apply接收的是一个参数数组,但在传递给调用函数时,却是以参数列表的花样传递。

当通过new运算符发生了cat时,Cat中的this就本着了cat对象,而三番五次的显如若在于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中定义的属性,从而完成了三番五次的目标。 

专注:那里的argsArray可以是一个数组或者类数组对象,假如该参数的值为null
或 undefined,则意味不须要传入任何参数。

利用场景二:**时过境迁**

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

在讲上面的始末前边,大家先是来认识一下JavaScript中的一个非标准专业术语:ArrayLike(类数组/伪数组)

利用apply和停放函数

ArrayLike
对象即拥有数组的一部分作为,在DOM中曾经展现出来,而jQuery的凸起让ArrayLike在JavaScript中大放异彩。ArrayLike对象的独具匠心在于它和JS原生的Array类似,可是它是随机构建的,它出自开发者对JavaScript对象的恢弘,也就是说:对于它的原型(prototype)我们得以随意定义,而不会污染到JS原生的Array。

聪慧的apply用法允许你在某些本来须要写成遍历数组变量的职务中运用内建的函数。在吸收里的例子中大家会使用Math.max/Math.min来找出一个数组中的最大/最小值。

ArrayLike对象在JS中被普遍利用,比如DOM中的NodeList,
函数中的arguments都是类数组对象,那个目的像数组一样存储着每一个要素,但它从未操作数组的不二法门,而我辈得以因此call将数组的少数方法`移接`到ArrayLike对象,从而完成操作其元素的目标。比如大家得以如此遍历函数中的arguments:

//里面有最大最小数字值的一个数组对象
var numbers = [5, 6, 2, 3, 7];

/* 使用 Math.min/Math.max 在 apply 中应用 */
var max = Math.max.apply(null, numbers);
// 一般情况是用 Math.max(5, 6, ..) 或者 Math.max(numbers[0], ...) 来找最大值
var min = Math.min.apply(null, numbers);

//通常情况我们会这样来找到数字的最大或者最小值
//比对上面的栗子,是不是下面的看起来没有上面的舒服呢?
max = -Infinity, min = +Infinity;
for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] > max)
    max = numbers[i];
  if (numbers[i] < min) 
    min = numbers[i];
}
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);

call()与apply()应该选哪一个可以吗?

除外,对于apply而言,我们地点提到了它独有的一个特点,即apply接收的是数组,在传递给调用函数的时候是以参数列表传递的。
那么些特点让apply看起来比call
后来居上,比如有这么一个气象:给定一个数组[1,3,4,7],然后求数组中的最大因素,而你理解,数组中并不曾获得最大值的办法,一般景况下,你须要通过编制代码来兑现。而我辈知道,Math对象中有一个获取最大值的法门,即Math.max(),
max方法要求传递一个参数列表,然后回到那个参数中的最大值。而apply不仅可以将Math对象的max方法运用到任何对象上,还可以够将一个数组转化为参数列表传递给max,看代码就能看清:

当参数是总之知晓数码时用call;不确定的时候用apply,然后把参数push进数组传递进入,也足以经过arguments那一个数组来遍历所有的参数。

var arr = [2,3,1,5,4];

Math.max.apply(null,arr); // 5

bind()

如上便是call和apply比较经典的多少个应用场景,熟稔精通那些技巧,并把这一个特征应用到你的其实项目中,会使您的代码看起来越发长远!

语法:fun.bind(thisArg[, arg1[, arg2[, …]]])

你或许感兴趣的篇章:

  • js
    apply/call/caller/callee/bind使用方法与差别分析
  • 浅谈javascript中call()、apply()、bind()的用法
  • JS中改变this指向的法子(call和apply、bind)
  • 跟我就学javascript的call(),apply(),bind()与回调
  • javascript中call,apply,bind的用法相比分析
  • 浅谈javascript的call()、apply()、bind()的用法
  • javascript中call apply 与
    bind方法详解
  • 拉开Javascript中apply、call、bind的用法之旅方式
  • 有关JS中的apply,call,bind的长远剖析
  • javascript中apply、call和bind的应用分别
  • javascript中call,apply,bind函数用法示例

bind是ES5新增的一个办法,它的传参和call类似,但又和call/apply有着明显的分裂,即调用call或apply都会自动执行相应的函数,而bind不会执行相应的函数,只是重临了对函数的引用。

澳门葡京 4

简不难单一看,bind似乎比call/apply要滞后一些,那ES5为何还要引入bind呢?

实质上,ES5引入bind的真正目标是为着弥补call/apply的不足,由于call/apply会对目的函数自动执行,从而导致它不能在事变绑定函数中应用,因为事件绑定函数不须求我们手动执行,它是在事变被触发时由JS内部自行执行的。而bind在达成转移函数this的还要又不会自行执行对象函数,由此可以周详的解决上述难题,看一个例证就能明了:

当点击网页时,伊夫ntClick被触发执行,输出JSLite.io p1 p2,
表明伊芙ntClick中的this被bind改变成了obj对象。假诺您将伊夫ntClick.bind(obj,’p1′,’p2′)
变成 伊芙ntClick.call(obj,’p1′,’p2′) 的话,页面会直接出口 JSLite.io p1 p2

var obj = {name:'JSLite.io'};
/**
 * 给document添加click事件监听,并绑定EventClick函数
 * 通过bind方法设置EventClick的this为obj,并传递参数p1,p2
 */
document.addEventListener('click',EventClick.bind(obj,'p1','p2'),false);
//当点击网页时触发并执行
function EventClick(a,b){
    console.log(
            this.name, //JSLite.io
            a, //p1
            b  //p2
    )
}
// JSLite.io p1 p2

兼容

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, // this在这里指向的是目标函数
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP
                 ? this //此时的this就是new出的obj
                 : oThis || this,//如果传递的oThis无效,就将fBound的调用者作为this

               //将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
               aArgs.concat(Array.prototype.slice.call(arguments)));
        };
    fNOP.prototype = this.prototype;
    //将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
    fBound.prototype = new fNOP();
    //返回fBond的引用,由外部按需调用
    return fBound;
  };
}

运用场景一:继承
世家精通,JavaScript中向来不诸如Java、C#等高级语言中的extend
关键字,因而JS中从不持续的概念,如果一定要继承的话,call和apply可以完毕这一个作用:

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运算符的任课,请参考:)
这句话,在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:

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,看代码就能看清:

var arr = [2,3,1,5,4];

Math.max.apply(null,arr); // 5

 

 

 

 

 

 

 

参考:

 

相关文章

发表评论

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

*
*
Website