JavaScript中的call 和apply的用途以及区别

luoy 7年前
   <p>apply 接受两个参数,第一个参数指定了函数体内this 对象的指向,第二个参数为一个带下</p>    <p>标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传</p>    <p>递给被调用的函数:</p>    <pre>  <code class="language-javascript">var func = function( a, b, c ){      alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]  };  func.apply( null, [ 1, 2, 3 ] );  </code></pre>    <p>在这段代码中,参数 1、2、3 被放在数组中一起传入func 函数,它们分别对应func 参数列</p>    <p>表中的a、b、c。</p>    <p>call 传入的参数数量不固定,跟apply 相同的是,第一个参数也是代表函数体内的this 指向,</p>    <p>从第二个参数开始往后,每个参数被依次传入函数:</p>    <pre>  <code class="language-javascript">var func = function( a, b, c ){      alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]  };  func.call( null, 1, 2, 3 );  </code></pre>    <p>当调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的</p>    <p>区别,JavaScript 的参数在内部就是用一个数组来表示的。从这个意义上说,apply 比call 的使用</p>    <p>率更高,我们不必关心具体有多少参数被传入函数,只要用apply 一股脑地推过去就可以了。</p>    <p>call 是包装在apply 上面的一颗语法糖,如果我们明确地知道函数接受多少个参数,而且想</p>    <p>一目了然地表达形参和实参的对应关系,那么也可以用call 来传送参数。</p>    <p>call和apply的用途</p>    <h2>1. 改变this 指向</h2>    <p>call 和apply 最常见的用途是改变函数内部的this 指向,我们来看个例子:</p>    <pre>  <code class="language-javascript">var obj1 = {      name: 'sven'  };  var obj2 = {      name: 'anne'  };  window.name = 'window';  var getName = function(){      alert ( this.name );  };  getName(); // 输出: window  getName.call( obj1 ); // 输出: sven  getName.call( obj2 ); // 输出: anne  </code></pre>    <p>当执行getName.call( obj1 )这句代码时,getName 函数体内的this 就指向obj1 对象,所以</p>    <p>此处的</p>    <p>var getName = function(){</p>    <p>alert ( this.name );</p>    <p>};</p>    <p>实际上相当于:</p>    <p>var getName = function(){</p>    <p>alert ( obj1.name ); // 输出: sven</p>    <p>};</p>    <p>在实际开发中,经常会遇到this 指向被不经意改变的场景,比如有一个div 节点,div 节点</p>    <p>的onclick 事件中的this 本来是指向这个div 的:</p>    <pre>  <code class="language-javascript">document.getElementById( 'div1' ).onclick = function(){      alert( this.id ); // 输出:div1  };  </code></pre>    <p>假如该事件函数中有一个内部函数func,在事件内部调用func 函数时,func 函数体内的this</p>    <p>就指向了window,而不是我们预期的div,见如下代码:</p>    <pre>  <code class="language-javascript">document.getElementById( 'div1' ).onclick = function(){      alert( this.id ); // 输出:div1      var func = function(){          alert ( this.id ); // 输出:undefined      }      func();  };  </code></pre>    <p>这时候我们用call 来修正func 函数内的this,使其依然指向div:</p>    <pre>  <code class="language-javascript">document.getElementById( 'div1' ).onclick = function(){      var func = function(){         alert ( this.id ); // 输出:div1      }      func.call( this );  };  </code></pre>    <h2>2. Function.prototype.bind</h2>    <p>大部分高级浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this 指向,</p>    <p>即使没有原生的Function.prototype.bind 实现,我们来模拟一个也不是难事,代码如下:</p>    <pre>  <code class="language-javascript">Function.prototype.bind = function( context ){  var self = this; // 保存原函数  return function(){ // 返回一个新的函数          return self.apply( context, arguments ); // 执行新的函数的时候,会     把之前传入的context      // 当作新函数体内的this      }  };  var obj = {      name: 'sven'  };  var func = function(){      alert ( this.name ); // 输出:sven  }.bind( obj);  func();  </code></pre>    <p>我们通过Function.prototype.bind 来“包装”func 函数,并且传入一个对象context 当作参</p>    <p>数,这个context 对象就是我们想修正的this 对象。</p>    <p>在Function.prototype.bind 的内部实现中,我们先把func 函数的引用保存起来,然后返回一</p>    <p>个新的函数。当我们在将来执行func 函数时,实际上先执行的是这个刚刚返回的新函数。在新</p>    <p>函数内部,self.apply( context, arguments )这句代码才是执行原来的func 函数,并且指定context</p>    <p>对象为func 函数体内的this。</p>    <p>这是一个简化版的Function.prototype.bind 实现,通常我们还会把它实现得稍微复杂一点,</p>    <p>使得可以往func 函数中预先填入一些参数:</p>    <pre>  <code class="language-javascript">Function.prototype.bind = function(){      var self = this, // 保存原函数      context = [].shift.call( arguments ), // 需要绑定的this 上下文      args = [].slice.call( arguments ); // 剩余的参数转成数组      return function(){ // 返回一个新的函数          return self.apply( context, [].concat.call( args, [].slice.call(    arguments ) ) );          // 执行新的函数的时候,会把之前传入的context 当作新函数体内的this         // 并且组合两次分别传入的参数,作为新函数的参数      }  };  var obj = {      name: 'sven'  };  var func = function( a, b, c, d ){      alert ( this.name ); // 输出:sven      alert ( [ a, b, c, d ] ) // 输出:[ 1, 2, 3, 4 ]  }.bind( obj, 1, 2 );  func( 3, 4 );  </code></pre>    <p> </p>    <p>来自:http://www.cnblogs.com/wswq/p/6273165.html</p>    <p> </p>