禁止转载澳门太陽城集团登录网址

深入显出妙用 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 则会马上施行函数。

再总计一下:

  • apply 、 call 、bind 三者都是用来改动函数的this对象的对准的;
  • apply 、 call 、bind
    三者第二个参数都是this要对准的指标,也正是想钦定的上下文;
  • apply 、 call 、bind 三者都得以应用一而再参数字传送参;
  • bind 是再次来到对应函数,便于稍后调用;apply 、call 则是当下调用 。

正文实例出现的兼具代码,在自家的github上能够下载。

打赏帮助自个儿写出更加的多好小说,谢谢!

打赏我

打赏援救本身写出更加的多好作品,谢谢!

任选一种支付情势

澳门太陽城集团登录网址 1
澳门太陽城集团登录网址 2

2 赞 16 收藏 4
评论

有关笔者:chokcoco

澳门太陽城集团登录网址 3

经不住小运似水,逃但是此间少年。

个人主页 ·
小编的文章 ·
63 ·
   

澳门太陽城集团登录网址 4

You may also like...

发表评论

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

网站地图xml地图