JavaScript深入之类数组对象与arguments

smtg8813 7年前
   <p>JavaScript深入系列第十三篇,讲解类数组对象与对象之间的相似与差异以及arguments的注意要点</p>    <h2>类数组对象</h2>    <p>所谓的类数组对象:</p>    <p>拥有一个 length 属性和若干索引属性的对象</p>    <p>举个例子:</p>    <pre>  <code class="language-javascript">var array = ['name', 'age', 'sex'];    var arrayLike = {      0: 'name',      1: 'age',      2: 'sex',      length: 3  }</code></pre>    <p>即便如此,为什么叫做类数组对象呢?</p>    <p>那让我们从读写、获取长度、遍历三个方面看看这两个对象</p>    <h2>读写</h2>    <pre>  <code class="language-javascript">console.log(array[0]); // name  console.log(arrayLike[0]); // name    array[0] = 'new name';  arrayLike[0] = 'new name';</code></pre>    <h2>长度</h2>    <pre>  <code class="language-javascript">console.log(array.length); // 3  console.log(arrayLike.length); // 3</code></pre>    <h2>遍历</h2>    <pre>  <code class="language-javascript">for(var i = 0, len = array.length; i < len; i++) {     ……  }  for(var i = 0, len = arrayLike.length; i < len; i++) {      ……  }</code></pre>    <p>是不是很像?</p>    <p>那类数组对象可以使用数组的方法吗?比如:</p>    <pre>  <code class="language-javascript">arrayLike.push('4');</code></pre>    <p>然而上述代码会报错: arrayLike.push is not a function</p>    <p>所以终归还是类数组呐……</p>    <h2>调用数组方法</h2>    <p>如果类数组就是任性的想用数组的方法怎么办呢?</p>    <p>既然无法直接调用,我们可以用Function.call间接调用:</p>    <pre>  <code class="language-javascript">var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }    Array.prototype.join.call(arrayLike, '&'); // name&age&sex    Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"]   // slice可以做到类数组转数组    Array.prototype.map.call(arrayLike, function(item){      return item.toUpperCase();  });   // ["NAME", "AGE", "SEX"]</code></pre>    <h2>类数组转对象</h2>    <p>在上面的例子中已经提到了一种类数组转数组的方法,再补充三个:</p>    <pre>  <code class="language-javascript">var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }  // 1. slice  Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"]   // 2. splice  Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"]   // 3. ES6 Array.from  Array.from(arrayLike); // ["name", "age", "sex"]   // 4. apply  Array.prototype.concat.apply([], arguments)</code></pre>    <p>那么为什么会讲到类数组对象呢?以及类数组有什么应用吗?</p>    <p>要说到类数组对象,Arguments对象就是一个类数组对象。在客户端JavaScript中,一些DOM方法(document.getElementsByTagName())也返回类数组对象。</p>    <h2>Arguments对象</h2>    <p>接下来重点讲讲Arguments对象。</p>    <p>Arguments对象只定义在函数体内,包括了函数的参数和其他属性。</p>    <p>举个例子:</p>    <pre>  <code class="language-javascript">function foo(name, age, sex) {      console.log(arguments);  }    foo('name', 'age', 'sex')</code></pre>    <p>打印结果如下:</p>    <p><img src="https://simg.open-open.com/show/012cb7f8d68c4c45f495ef824d571725.png"></p>    <p>我们可以看到除了类数组的索引属性和length属性之外,还有一个callee属性,接下来我们一个一个介绍。</p>    <h2>length属性</h2>    <p>Arguments对象的length属性,表示实参的长度,举个例子:</p>    <pre>  <code class="language-javascript">function foo(b, c, d){      console.log("实参的长度为:" + arguments.length)  }    console.log("形参的长度为:" + foo.length)    foo(1)    // 形参的长度为:3  // 实参的长度为:1</code></pre>    <h2>callee属性</h2>    <p>Arguments对象的callee属性,通过它可以调用函数自身。</p>    <p>讲个闭包经典面试题使用callee的解决方法:</p>    <pre>  <code class="language-javascript">var data = [];    for (var i = 0; i < 3; i++) {      (data[i] = function () {         console.log(arguments.callee.i)       }).i = i;  }    data[0]();  data[1]();  data[2]();    // 0  // 1  // 2</code></pre>    <p>接下来讲讲arguments对象的几个注意要点:</p>    <h2>arguments和对应参数的绑定</h2>    <pre>  <code class="language-javascript">function foo(name, age, sex, hobbit) {        console.log(name, arguments[0]); // name name        // 改变形参      name = 'new name';        console.log(name, arguments[0]); // new name new name        // 改变arguments      arguments[1] = 'new age';        console.log(age, arguments[1]); // new age new age        // 测试未传入的是否会绑定      console.log(sex); // undefined        sex = 'new sex';        console.log(sex, arguments[2]); // new sex undefined        arguments[3] = 'new hobbit';        console.log(hobbit, arguments[3]); // undefined new hobbit    }    foo('name', 'age')</code></pre>    <p>传入的参数,实参和arguments的值会共享,当没有传入时,实参与arguments值不会共享</p>    <p>除此之外,以上是在非严格模式下,如果是在严格模式下,实参和arguments是不会共享的。</p>    <h2>传递参数</h2>    <p>将参数从一个函数传递到另一个函数</p>    <pre>  <code class="language-javascript">function foo() {      bar.apply(this, arguments);  }  function bar(a, b, c) {      // logic  }</code></pre>    <h2>强大的ES6</h2>    <p>使用ES6的...运算符,我们可以轻松转成数组。</p>    <pre>  <code class="language-javascript">function func(...arguments) {      console.log(arguments); // [1, 2, 3]  }    func(1, 2, 3);</code></pre>    <h2>应用</h2>    <p>arguments的应用其实很多,在下个系列,也就是JavaScript专题系列中,我们会在jQuery的extend实现、函数柯里化、递归等场景看见arguments的身影。这篇文章就不具体展开了。</p>    <p>如果要总结这些场景的话,暂时能想到的包括:</p>    <ol>     <li> <p>参数不定长</p> </li>     <li> <p>函数柯里化</p> </li>     <li> <p>递归调用</p> </li>     <li> <p>函数重载</p> <p>...</p> </li>    </ol>    <p> </p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000009328344</p>    <p> </p>