菜单

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

2018年12月18日 - 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地图