>

JS核心系列

- 编辑:至尊游戏网站 -

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

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.addEventListener('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

本文由软件综合发布,转载请注明来源:JS核心系列