js call 和apply

   ECMAScript3  给Function原型上定义了两个方法,他们是Function.prototype.call和Function.prototype.apply。数组

 

  • call和apply的区别
 1 var func =function(a,b,c){
 2     alert([a,b,c]);
 3 };
 4 func.apply(null,[1,2,3]);//apply
 5 
 6 
 7 var func =function(a,b,c){
 8     alert([a,b,c]);
 9 };
10 func.call(null,1,2,3);//call

  apply::两个参数,第一个参数:指定了函数体内this对象的指向;浏览器

          第二个参数:一个带下标的集合,能够为数组,也能够为伪数组app

  call:多个参数:第一个参数:指定了函数体内this对象的指向;ide

          第二个参数开始日后,每个参数被依次传入函数;函数

 

  call和apply传入的第一个参数为null,函数体内this会指向默认的宿主对象,在浏览器中则是windowthis

  若是在严格模式下,函数体内的this仍是为null 1 var func = function(a,b,c){加密

 2     alert(this === window);//true  
 3 };
 4 
 5 func.apply(null,[1,2,3])
 6 
 7 var func = function(a,b,c){
 8     "use stract";//若是在严格模式下,函数体内的this仍是为null
 9     alert(this === null);//true  
10 };
11 func.apply(null,[1,2,3]);

  • call和apply的用途

    1.改变this的指向 spa

 1 var obj1 = {
 2     name:"seven"
 3 };
 4 
 5 var oj2 = {
 6     name:"anne"
 7 };
 8 
 9 window.name = "window";
10 
11 var getName = function(){
12     alert(this.name)
13 };
14 
15 getName();//window
16 getName.call(obj1);//seven
17 getName.apply(obj2);//anne
View Code

在实际开发中,常常会遇到this指向被不经意改变的场景,好比,有一个div节点,div节点的onclick事件中的this原本指向这个div的:prototype

 1 dcument.getElementById("div1").onclick=function(){
 2     alert(this.id);//div1
 3 }
 4 //加入改时间函数中有一个内部函数func,在实践内部调用func函数时,func函数体内的this就指向了window,而不是咱们预期的div
 5 
 6 document.getElementById("div1").oonclick = function(){
 7     alert(this.id);//div1
 8     var func = function(){
 9         alert(this.id)  //undefined
10     };
11     func();
12 };
13 //这时候咱们用call来修正func函数内的this,使其指向div
14 document.getElementById("div1").onclick=function(){
15     var func = function(){
16         alert(this.id);//div1
17     };
18     func.call(this);
19 }   

使用call来修正this的场景,咱们并非第一次遇到,code

1 document.getElementById = (function(){
2     return function(){
3         return func.apply(document,arguments);
4     }
5 })(document.getElementById);
6 
7 var getId =document.getElementById;
8 var div = getId("div1");
9 alert(div.id);  //div1

    2.Function.prototype.bind

大部分浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this指向,即便没有原生的Function.prototype.bind实现。

 1 Function.prototype.bind= function(context){
 2     var self = this;//保存原函数
 3     return function(){//返回一个新函数
 4         return self.apply(context,arguments);//执行新的函数的时候,会把以前的context当作新函数体内的this
 5     };
 6 }
 7 
 8 var obj = {
 9     name:"seve"
10 };
11 
12 var a = function(){
13     alert(this.name);//seve
14 }.bind(obj);
15 
16 a();

咱们经过Function.prototype.bind来“包装”func函数,而且传入一个对象context看成参数,这个context对象就是咱们想修正的this对象。

在Function.prototype.bind的内部实现中,咱们先把func函数的引用保存起来,而后返回一个新函数。当咱们在未来执行a函数时,实际上先执行的是这个刚刚返回的新函数。在新函数内部,self.apply(context,arguments)这句话代码是执行原来的a函数,而且指定context对象为a函数体内的this.

下面是复杂一点的实现:

 1 Function.prototype.bind = function(){
 2     var self = this,
 3         context = [].shift.call(arguments),
 4         args = [].slice.call(arguments),
 5     return function(){
 6         return self.apply(context,[].concat.call(args,[].slice.call(arguments)));
 7     } 
 8 };
 9 var obj = {
10     name:"seven"
11 };
12 
13 var func = function(){
14     alert(his.name);
15     alert([a,b,c,d])
16 }.bind(obj,1,2);
17 
18 func(3,4);

    3.借用其余对象的方法

借用方法的第一种场景是“借用构造函数”,经过这种技术,能够实现一些相似继承的效果;

 1 var A = function(name){
 2   this.name = name;
 3 };
 4 
 5 var B = function(){
 6     A.apply(this,arguments);
 7 };
 8 
 9 B.prototype.getName = function(){
10     return this.name;
11 };
12 
13 var b = new B("seven");
14 console.log(b.getName());

借用方法的第二种运用场景跟咱们的关系更加密切。

函数的参数列表arguments是一个类数组对象,虽然它也有“下标”,但它并不是真正的数组,因此也不像数组同样,进行排序操做或者往集合里添加一个新的元素。这种状况下,咱们经常会借用Array.prototype对象上的方法。好比想往arguments中添加一个新的元素,一般会借用Array.prototype.push:

(function(){
    Array.prototype.push.call(arguments,3);
    console.log(arguments);
})(1,2)//在操做arguments的时候,咱们常常很是频繁地找Array.prototype对象借用方法。

将数组转成真正的数组:Array.prototype.slice

截取arguments列表的头一个元素:Array.prototype.shift

v8的引擎源码:以Array.prototype.push为例

 1 function ArrayPush(){
 2     var n = TO_UINT32(this.length);//被push的对象的length
 3     var m = %_ArgumentsLength();//push的参数个数
 4     for(var i =0;i<m;i++){
 5         this[i+n] = %_Arguments(i);//复制元素 (1)
 6     }
 7     this.length =n+m;//修正length属性的值  (2)
 8     return this.length;
 9 };
10 //经过这段代码能够看到,Array.prototype.push其实是一个属性复制的过程,把参数按照下标一次添加到push的对象上面,顺便修改了这个对象的length属性。至于被修改的对象是谁,究竟是数组仍是类数组对象,这一点并不重要。
11 //由此能够推断出,咱们能够把“任意”对象传入Array.prototype.push:
12 var a = {};
13 Array.prototype.push.call(a,"first");
14 alert(a.length);//输出:1
15 alert(a[0]);//first
16 //这段代码在绝大部分浏览器里都能顺利执行,但因为引擎的内部实现存在差别,若是在低版本 的IE浏览器中执行,必须显示的给对象a设置length属性;
17 var  a = {
18     length:0
19 };

前面咱们之因此把“任意”两个字加上引号,是由于能够借用Array.prototype.push方法的对象还要知足一下两个条件,从ArrayPush函数的(1)和(2)处也能够猜到,这个对象至少还要知足:

  对象自己要能够存取属性;

 对象的length属性可读写;

相关文章
相关标签/搜索