菜单

深远浅出妙用 Javascript 中 apply、call、bind

2018年12月17日 - JavaScript

深切浅出妙用 Javascript 中 apply、call、bind

2015/09/24 · JavaScript
· 4 评论 ·
apply,
bind,
call

正文作者: 伯乐在线
chokcoco
。未经作者许可,禁止转载!
欢迎出席伯乐在线 专辑作者

立即首著作实在是死麻烦开,因为网上有关著作不胜枚举。

巧合的是前数天看阮老师的等同篇稿子的等同词话:

“对自我吧,博客首先是均等栽文化管理工具,其次才是传播工具。我的技能著作,重要为此来整治自还无领会的学问。我只写这个自还无完全控制的物,这么些自己了解的东西,往往无重力写。炫耀没有是自身的心情,好奇才是。”

对此当下句话,不可能帮忙更多,也叫自家下决心好好写就首,网上小说就是多,大多复制粘贴,且涩难知晓,我望能透过就首著作,可以清楚的晋级对apply、call、bind的认识,并且列有部分它们的妙用加深记忆。

   apply、call

当 javascript 中,call 和 apply
都是为着转移有函数运行时的上下文(context)而在的,换句话说,就是以改变函数体内部
this 的对准。

JavaScript
的同样可怜特征是,函数存在「定义时上下文」和「运行时达到下文」以及「上下文是好转之」那样的定义。

优先来一个板栗:

JavaScript

function fruits() {} fruits.prototype = { color: “red”, say: function()
{ console.log(“My color is ” + this.color); } } var apple = new fruits;
apple.say(); //My color is red

1
2
3
4
5
6
7
8
9
10
11
function fruits() {}
 
fruits.prototype = {
    color: "red",
    say: function() {
        console.log("My color is " + this.color);
    }
}
 
var apple = new fruits;
apple.say();    //My color is red

可是若我们出一个对象banana= {color : “yellow”} ,大家不缅想对她再也定义
say 方法,那么大家得经 call 或 apply 用 apple 的 say 方法:

JavaScript

banana = { color: “yellow” } apple.say.call(banana); //My color is
yellow apple.say.apply(banana); //My color is yellow

1
2
3
4
5
banana = {
    color: "yellow"
}
apple.say.call(banana);     //My color is yellow
apple.say.apply(banana);    //My color is yellow

故而,可以看来 call 和 apply 是为动态改变 this 而出现的,当一个 object
没有之一方法(本栗子中banana没有say方法),可是任何的发出(本栗子中apple有say方法),我们得以因call或apply用别样对象的方来操作。

apply、call 的区别

对此 apply、call
二者而言,效能了一样,只是接受参数的措施不绝雷同。例如,有一个函数定义如下:

JavaScript

var func = function(arg1, arg2) { };

1
2
3
var func = function(arg1, arg2) {
 
};

就是可通过如下模式来调用:

JavaScript

func.call(this, arg1, arg2); func.apply(this, [arg1, arg2])

1
2
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])

里头 this 是公想指定的上下文,他不过另外一个 JavaScript
对象(JavaScript 中整都对象),call 需要把参数按梯次传递进入,而 apply
则是把参数放在数组里。

JavaScript
中,某个函数的参数数量是无固定的,因而若说适用规则的言语,当你的参数是精通知晓数码时用
call 。

倘若未确定的下用 apply,然后将参数 push
进数组传递进入。当参数数量不确定时,函数内部也得经过 arguments
这些数组来遍历所有的参数。

为巩固深化记念,上边列举部分常用用法:

1、数组之间多

JavaScript

var array1 = [12 , “foo” , {name “Joe”} , -2458]; var array2 = [“Doe”
, 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1
值为 [12 , “foo” , {name “Joe”} , -2458 , “Doe” , 555 , 100] */

1
2
3
4
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

2、获取数组中的至极老价值与极小值

JavaScript

var numbers = [5, 458 , 120 , -215 ]; var maxInNumbers =
Math.max.apply(Math, numbers), //458 maxInNumbers =
Math.max.call(Math,5, 458 , 120 , -215); //458

1
2
3
var  numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers),   //458
    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

number 本身并未 max 方法,不过 Math 有,我们固然得靠 call 或者 apply
使用该道。

3、验证是否是数组(前提是toString()方法没有被重复写过)

JavaScript

functionisArray(obj){ returnObject.prototype.toString.call(obj) ===
‘[object Array]’ ; }

1
2
3
functionisArray(obj){
    returnObject.prototype.toString.call(obj) === ‘[object Array]’ ;
}

4、类(伪)数组使用数组方法

JavaScript

var domNodes =
Array.prototype.slice.call(document.getElementsByTagName(“*”));

1
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

Javascript中存在同样种植叫做吧伪数组的目标协会。相比较特别之凡 arguments
对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们重回NodeList对象还属于伪数组。不可知用
Array下之 push , pop 等艺术。

唯独咱可以因而 Array.prototype.slice.call 转换为实在的数组的包含 length
属性的目的,这样 domNodes 就好行使 Array 下的保有办法了。

深入明使apply、call

下面就借一道面试题,来重新透之错过解下
apply 和 call 。

概念一个 log 方法,让其可代办 console.log 方法,常见的解决措施是:

JavaScript

function log(msg) { console.log(msg); } log(1); //1 log(1,2); //1

1
2
3
4
5
function log(msg) {
  console.log(msg);
}
log(1);    //1
log(1,2);    //1

地点方法好化解最要旨的要求,但是当传入参数的个数是未确定的时段,下面的计就是失效了,这个时段便足以设想选取apply 或者
call,注意这里传出几个参数是匪确定的,所以采纳apply是最好之,方法如下:

JavaScript

function log(){ console.log.apply(console, arguments); }; log(1); //1
log(1,2); //1 2

1
2
3
4
5
function log(){
  console.log.apply(console, arguments);
};
log(1);    //1
log(1,2);    //1 2

属下去的渴求是叫各级一个 log 信息添加一个”(app)”的前辍,比如:

JavaScript

log(“hello world”); //(app)hello world

1
log("hello world");    //(app)hello world

拖欠怎么开相比较优雅也?这多少个时候需要想到arguments参数是只伪数组,通过
Array.prototype.slice.call
转化为规范往往组,再用数组方法unshift,像这么:

JavaScript

function log(){ var args = Array.prototype.slice.call(arguments);
args.unshift(‘(app)’); console.log.apply(console, args); };

1
2
3
4
5
6
function log(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift(‘(app)’);
 
  console.log.apply(console, args);
};

bind

说罢了 apply 和 call ,再来说说bind。bind() 方法以及 apply 和 call
很一般,也是得更改函数体内 this 的针对性。

MDN的表达是:bind()方法会成立一个初函数,称为绑定函数,当调用这多少个绑定函数时,绑定函数会坐创办它时时传出 bind()方法的首先独参数作为 this,传入 bind() 方法的老二个跟今后的参数加上绑定函数运行时自我的参数按照顺序作本函数的参数来调用原函数。

一贯来探视具体什么以,在普遍的单体形式碰到,常常大家相会采取 _this , that
, self 等保存 this
,这样我们可以以改变了上下文之后持续引用到它。 像这样:

JavaScript

var foo = { bar : 1, eventBind: function(){ var _this = this;
$(‘.someClass’).on(‘click’,function(event) { /* Act on the event */
console.log(_this.bar); //1 }); } }

1
2
3
4
5
6
7
8
9
10
var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this;
        $(‘.someClass’).on(‘click’,function(event) {
            /* Act on the event */
            console.log(_this.bar);     //1
        });
    }
}

由于 Javascript 特有的建制,上下文环境在 eventBind:function(){ }
过渡至 $(‘.someClass’).on(‘click’,function(event)
{ }) 暴发了转移,上述使用变量保存 this 这多少个主意都是行之有效的,也从不什么问题。当然使用
bind() 可以更进一步文雅的缓解此问题:

JavaScript

var foo = { bar : 1, eventBind: function(){
$(‘.someClass’).on(‘click’,function(event) { /* Act on the event */
console.log(this.bar); //1 }.bind(this)); } }

1
2
3
4
5
6
7
8
9
var foo = {
    bar : 1,
    eventBind: function(){
        $(‘.someClass’).on(‘click’,function(event) {
            /* Act on the event */
            console.log(this.bar);      //1
        }.bind(this));
    }
}

以上述代码里,bind()创造了一个函数,当这一个click事件绑定以被调用的时,它的 this
关键词会被装置成让传播的价值(这里指调用bind()时传出的参数)。因此,这里我们传入想假诺的内外文
this(其实尽管是 foo ),到 bind() 函数着。然后,当回调函数被实践之早晚,
this 便指向 foo 对象。再来一个简单的栗子:

JavaScript

var bar = function(){ console.log(this.x); } bar(); // undefined var
func = bar.bind(foo); func(); // 3

1
2
3
4
5
6
7
var bar = function(){
    console.log(this.x);
}
 
bar(); // undefined
var func = bar.bind(foo);
func(); // 3

此处大家创设了一个新的函数 func,当以 bind()创制一个绑定函数之后,它让执行之时节,它的 this 会被装成 foo ,
而无是诸如咱调用 bar() 时的大局效能域。

来只有意思之题目,假设总是 bind() 两浅,亦或者是连续 bind()三破那么输出的价是啊也?像这么:

JavaScript

var bar = function(){ console.log(this.x); } var foo = { x:3 } var sed =
{ x:4 } var func = bar.bind(foo).bind(sed); func(); //? var fiv = { x:5
} var func = bar.bind(foo).bind(sed).bind(fiv); func(); //?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func(); //?
 
var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //?

答案是,两不善还照将出口 3 ,而无要被的 4 和 5
。原因是,在Javascript中,多次 bind() 是废的。更老层次之缘由, bind()的落实,卓殊给以函数在里边包了一个 call / apply ,第二坏 bind()异常给再一次保住第一次 bind() ,故第二软以后的 bind 是无能为力生效之。

apply、call、bind比较

那么 apply、call、bind 三者相较,之间以出什么异同呢?何时使用
apply、call,啥时候使用 bind 呢。简单的一个板栗:

JavaScript

var obj = { x: 81, }; var foo = { getX: function() { return this.x; } }
console.log(foo.getX.bind(obj)()); //81 console.log(foo.getX.call(obj));
//81 console.log(foo.getX.apply(obj)); //81

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
    x: 81,
};
 
var foo = {
    getX: function() {
        return this.x;
    }
}
 
console.log(foo.getX.bind(obj)());  //81
console.log(foo.getX.call(obj));    //81
console.log(foo.getX.apply(obj));   //81

老三独出口的都是81,不过注意看使用 bind() 方法的,他后多矣针对括号。

也就是说,区别是,当您盼改上下文环境下不要就实施,而是回调执行之时光,使用
bind() 方法。而 apply/call 则会应声施行函数。

再一次总结一下:

正文实例出现的有所代码,在本人之github上可下载

打赏协理自己形容有重新多好文章,谢谢!


打赏作者

及时篇稿子实在是不行为难写,因为网上有关著作不胜枚举。

打赏帮忙我勾勒来双重多好章,谢谢!

任选一种出情势

图片 1
图片 2

2 赞 16 收藏 4
评论

巧合的是前数天看阮老师的均等篇稿子的均等句子话:

有关作者:chokcoco

图片 3

经不住大运似水,逃可是此少年。

个人主页
·
我之稿子
·
63
·
   

图片 4

“对本身的话,博客首先是一模一样种知识管理工具,其次才是传工具。我的技能著作,首要用来打点自还无明了的学识。我特写这些自己还平昔不了领悟的事物,这些自己了然的物,往往无重力写。炫耀没有是自身的念,好奇才是。”

对这句话,不能扶助更多,也叫我下决心好好写就首,网上随笔就是多,大多复制粘贴,且涩难精晓,我盼望能透过就首著作,可以清楚的提高对apply、call、bind的认识,并且列有部分它的妙用加深回忆。

 

apply、call

以 javascript 中,call 和 apply
都是为了改变有函数运行时之上下文(context)而留存的,换句话说,就是为改变函数体内部
this 的针对性。

JavaScript 的相同十分特征是,函数存在「定义时达成下文」和「运行时达下文」以及「上下文是可转移之」这样的定义。

优先来一个板栗:

1
2
3
4
5
6
7
8
9
10
11
function fruits() {}
 
fruits.prototype = {
    color: "red",
    say: function() {
        console.log("My color is " this.color);
    }
}
 
var apple = new fruits;
apple.say();    //My color is red

 不过只要大家发出一个对象banana= {color : “yellow”} ,我们无思对它们再一次定义
say 方法,那么我们好因而 call 或 apply 用 apple 的 say 方法:

1
2
3
4
5
banana = {
    color: "yellow"
}
apple.say.call(banana);     //My color is yellow
apple.say.apply(banana);    //My color is yellow

就此,可以见到 call 和 apply 是为了动态改变 this 而起的,当一个 object
没有有方法(本栗子中banana没有say方法),不过其他的生(本栗子中apple有say方法),大家好靠call或apply用任何对象的格局来操作。

 

apply、call 的区别

 对于 apply、call
二者而言,功能完全等同,只是接受参数的法门不绝一样。例如,有一个函数定义如下:

1
2
3
var func = function(arg1, arg2) {
     
};

尽管好透过如下模式来调用:

1
2
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])

其间 this 是你想指定的上下文,他好是外一个 JavaScript
对象(JavaScript 中全方位皆对象),call 需要把参数按梯次传递进入,而 apply
则是把参数放在数组里。  

JavaScript
中,某个函数的参数数量是无定点的,由此而说适用规则的语句,当你的参数是醒目知晓数据时用
call 。

即使非确定的上用 apply,然后把参数 push
进数组传递进入。当参数数量不确定时,函数内部也可因此 arguments
这些伪数组来遍历所有的参数。

 

为巩固深化回想,下边罗列部分常用用法:

1、数组之间加

1
2
3
4
var array1 = [12 , "foo" , {name "Joe"} , -2458]; 
var array2 = ["Doe" , 555 , 100]; 
Array.prototype.push.apply(array1, array2); 
/* array1 值为  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

2、获取数组中的无限老价值与无限小值

1
2
3
var  numbers = [5, 458 , 120 , -215 ]; 
var maxInNumbers = Math.max.apply(Math, numbers),   //458
    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

number 本身并未 max 方法,可是 Math 有,我们尽管可以靠 call 或者 apply
使用其法。

3、验证是否是数组(前提是toString()方法没有吃重复写过)

1
2
3
functionisArray(obj){ 
    return Object.prototype.toString.call(obj) === '[object Array]' ;
}

4、类(伪)数组使用数组方法

1
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

Javascript中在一样种名叫吧伪数组的靶子社团。比较特别之是 arguments
对象,还有诸如调用 getElementsByTagName , document.childNodes 之类的,它们再次来到NodeList对象都属伪数组。不可知运用
Array下的 push , pop 等情势。

但咱可以经过 Array.prototype.slice.call 转换为真正的数组的带有 length
属性的靶子,这样 domNodes 就可拔取 Array 下的有办法了。

 

深切明使apply、call

下面就借一道面试题,来再次透彻之失去精通下
apply 和 call 。

概念一个 log 方法,让它们可代办 console.log 方法,常见的缓解模式是:

1
2
3
4
5
function log(msg) {
  console.log(msg);
}
log(1);    //1
log(1,2);    //1

方方法能够化解最焦点的需求,不过当传入参数的个数是匪确定的时光,下面的法子就是失效了,这些上虽然得考虑用
apply 或者
call,注意那里流传多少只参数是勿确定的,所以利用apply是极其好的,方法如下:

1
2
3
4
5
function log(){
  console.log.apply(console, arguments);
};
log(1);    //1
log(1,2);    //1 2

连着下的求是深受各国一个 log 信息添加一个”(app)”的前辍,比如:

1
log("hello world");    //(app)hello world

欠怎么开比优雅也?这多少个上要想到arguments参数是只伪数组,通过
Array.prototype.slice.call
转化为正规往往组,再运数组方法unshift,像这么:

1
2
3
4
5
6
function log(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('(app)');
 
  console.log.apply(console, args);
};

 

bind

说罢了 apply 和 call ,再来说说bind。bind() 方法以及 apply 和 call
很一般,也是足以变更函数体内 this 的对。

MDN的讲是:bind()方法会创造一个初函数,称为绑定函数,当调用这么些绑定函数时,绑定函数会因创它常传出 bind()方法的第一单参数作为 this,传入 bind() 方法的亚独同随后的参数加上绑定函数运行时自的参数遵照顺序作本函数的参数来调用原函数。

直白来瞧实际哪利用,在科普的单体情势中,通常我们会动用 _this , that
, self 等保存 this
,这样我们得以于变更了上下文之后继续引用到其。 像这样:

1
2
3
4
5
6
7
8
9
10
var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this;
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(_this.bar);     //1
        });
    }
}

出于 Javascript 特有的体制,上下文环境在 eventBind:function(){ }
过渡至 $(‘.someClass’).on(‘click’,function(event)
{ }) 暴发了改观,上述使用变量保存 this 这一个方法都是实惠的,也从没啊问题。当然使用
bind() 可以更文雅的解决之题目:

1
2
3
4
5
6
7
8
9
var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(this.bar);      //1
        }.bind(this));
    }
}

每当上述代码里,bind()制造了一个函数,当这click事件绑定以为调用的上,它的 this
关键词会被安装成让盛传的值(这里指调用bind()时传出的参数)。由此,这里大家传入想只要的左右文
this(其实就是 foo ),到 bind() 函数吃。然后,当回调函数被实施的当儿,
this 便指向 foo 对象。再来一个简单的板栗:

1
2
3
4
5
6
7
8
9
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
bar(); // undefined
var func = bar.bind(foo);
func(); // 3

此间我们创立了一个新的函数 func,当用 bind()创设一个绑定函数之后,它叫执行的时刻,它的 this 会被装成 foo ,
而未是诸如大家调用 bar() 时底全局效率域。

发出个好玩的题材,假若总是 bind() 两不行,亦或是连接 bind()三不善那么输出的值是什么啊?像这么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func(); //?
 
var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //?

答案是,两回于还听从用出口 3 ,而无要中的 4 和 5
。原因是,在Javascript中,多次 bind() 是没用的。更可怜层次的来由, bind()的兑现,非凡给以函数在中包了一个 call / apply ,第二糟 bind()格外给更包住第一赖 bind() ,故第二潮之后的 bind 是力不从心生效的。

  

apply、call、bind比较

那么 apply、call、bind 三者相较,之间又出什么异同呢?什么日期使用
apply、call,啥时候使用 bind 呢。简单的一个板栗:

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
    x: 81,
};
 
var foo = {
    getX: function() {
        return this.x;
    }
}
 
console.log(foo.getX.bind(obj)());  //81
console.log(foo.getX.call(obj));    //81
console.log(foo.getX.apply(obj));   //81

老三独出口的仍然81,不过注意看使用 bind() 方法的,他背后多矣针对性括号。

也就是说,区别是,当您期望改上下文环境下不要就施行,而是回调执行的时刻,使用
bind() 方法。而 apply/call 则会登时执行函数。

 

重总括一下:

 

正文实例出现的备代码,在自的github上足下载

 

初稿地址:http://www.cnblogs.com/coco1s/p/4833199.html

 

相关文章

发表评论

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

网站地图xml地图