arguments对象并不是标准的Array类型的实例。arguments对象不能直接调用Array方法。

arguments对象的救星call方法

使得arguments可以品尝到数组方法的美味,知道可以吃,下面就是怎么吃的问题了。不管怎么吃,先吃一口试试。

function callMethod(obj,method){
var shift=[].shift;
shift.call(arguments);
shift.call(arguments);
return obj[method].apply(obj,argumetns);
}

感觉很棒的样子,色香都具备了,拿筷子尝一下吧。

var obj={
add:function(x,y){return x+y;}
};
callMethod(obj,'add',17,25);//这里应该是42,不运行我都知道

放到chrome控制台运行一下吧,哇,好鲜艳,有红色。
//Uncaught TypeError: Cannot read property 'apply' of undefined(…)
为什么会出错呢,回去找找菜谱(高3),看哪里不对。原来arguments对象并不是函数参数的副本。所有命名参数都是arguments对象中对应索引的别名。上节好像也有,回去看一下,下面列出一个arguments对象索引对应命名参数的列表

arguments对象:
索引 形参 实参
0 obj obj
1 method 'add'
2 17
3 25

看着这个可以想一下,这个材料(arguments对象),经过加工(两次shift)。
arguments对象变成下面这个样子了

arguments对象:
索引 形参 实参
0 obj 17
1 method 25

此时我们吃到得已经是变了味的东西了,唉,这个味还能和预期一样嘛。不出错才怪,这个糟糕的过程在JS内部这样执行。这时的obj['add']变成了17[25]。
17被转化为Number对象,然后查找属性"25",很明显不存在,得到undefined值,然后查找undefined的'apply'属性并将它当方法调用,最后我们的色香被味扼杀了。

这次的失败,我们可以总结出,arguments对象和命名参数的关系是脆弱的。修改arguments对象也要小心命名参数的改变。ES5下情况更复杂。严格模式,函数参数不支持对其arguments对象取别名。到底如何,写代码看一下就知道了。代码如下

function strict(x){
"use strict";
arguments[0]='我说还不行嘛';
return x===arguments[0];
}
function nostrict(x){
arguments[0]='我说还不行嘛';
return x===arguments[0];
}
strict('我就不说');//false
nostrict('我就不说');//true

所以,不管说不说,我们还是不要动arguments对象了。惹不起我还躲不起嘛,我自己复制一份参数中的元素到新数组中,这样可以吧。

var args=[].slice.call(arguments);

这是什么,很牛的样子。小朋友,我给你讲讲哈。当不使用额外的参数调用数组的slice方法时,它会复制整个数组,结果是一个真正的男人(数组)。要不我们来试试复制个数组试试。

var a=[1,2,3,4,5,6,7];
var b=a;
var c=a.slice();
a.shift();
b;//[2, 3, 4, 5, 6, 7]
c;//[1, 2, 3, 4, 5, 6, 7]

看到没要真正独立还是要复制一下的,要不会被影响的。
扯远了,回到最初的那个问题,动手修正callMethod函数,把"味"补上。

function callMethod(obj,method){
var args=[].slice.call(arguments,2);
return obj[method].apply(obj,args);
}

运行一下

var obj={
add:function(x,y){return x+y;}
};
callMethod(obj,'add',17,25);//42

对就是这个味(结果)。回味一下,这个过程要注意哪些。下面是书里给的提示。

提示

  • 永远不要修改arguments对象

  • 使用[].slice.call(arguments)将arguments对象复制到一个真正的数组中再进行修改

附录一:Array队列和操作方法

队列方法

shift()方法

移除数组中的第一个项并返回该项,同时将数组长度减1。

var a=[1,2,3];
var b=a.shift();
b;//1
a;//[2,3]

push()方法

接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。

var a=[1,2,3];
var b=a.push(0);
var c=a.push(4,5,7);
var m=a.push([9,0])
a;//[1, 2, 3, 0, 4, 5, 7, [9,0]]
b;//4
c;//7
m;//8

操作方法

concat()方法

基于当前数组中的所有项创建一个新数组。具体来说,先创建一个数组的副本,然后将接收的参数添加到这个副本的尾部,最后返回新构建的数组。
不传参可以当复制数组来使用

var a=[2,3,4];
var b=a.concat();
a.push(2,1);
a;//[2,3,4,2,1]
b;//[2,3,4]

传参是一个或多个数组,将这些数组的第一项复制到新数组后,如果不是数组则简单添加。

var a=[1,2,3,4,6];
var b=[1,[2,3],4];
var c="good";
var d=a.concat(b,c);
d;//[1, 2, 3, 4, 6, 1, [2,3], 4, "good"]

上面的代码说明数组复制,不进行递归。

slice()方法

可以基于当前数组中的一或多个项创建一个新数组。(不影响原始数组)

不传参可以用来复制数组

var a=[2,3,4];
var b=a.slice();
a.push(2,1);
a;//[2,3,4,2,1]
b;//[2,3,4]

传一个或两个参数时

var a=[2,3,4];
var b=a.slice(1);
var c=a.slice(1,2);
a;//[2,3,4]
b;//[3,4]
c;//[3]

注意:

  • 参数为索引位置,范围为[arg1,arg2),产生的新数组包括arg1位置项,不包括arg2位置的项。

  • 参数可以为负数,当为负数时,对应的索引位置为数组长度加上该数来确定。

splice()方法

最强大的数组方法,主要用途是向数组中部插入项,有3种使用方式:

  • 删除:可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数。
    splice(0,2);删除数组中的前两项。范围为[arg1,arg1+arg2)

  • 插入:可以指定位置插入任意数量的项,只需要提供3个参数:起始位置,0,和要插入的项。
    splice(2,0,'good','morning');会从当前数组的位置2开始插入"godd","morning";

  • 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需要指定3个参数:起始位置,要删除的项数和要插入的任意数量的项。splice(2,1,'good','morning');删除位置2项并添加"godd","morning"

splice()方法返回一个数组,该数组中包含从原始数组中删除的项。注意:会影响原数组。

[Effective JavaScript 笔记]第23条:永远不要修改arguments对象的更多相关文章

  1. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  2. [Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法

    前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数argument ...

  3. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  4. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  5. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  6. 永远不要修改arguments对象

    案例复现 var obj = { plus: function(arg0, arg1) { return arg0 + arg1; } }; function callMethod(context, ...

  7. [Effective JavaScript 笔记]第22条:使用arguments创建可变参数的函数

    第21条讲述使用可变参数的函数average.该函数可处理任意数量的参数并返回这些参数的平均值. 如何创建可变参数的函数 1.实现固定元数的函数 书上的版本 function averageOfArr ...

  8. [Effective JavaScript 笔记]第54条:将undefined看做“没有值”

    undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined ...

  9. [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染

    之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...

随机推荐

  1. Use Windows Azure AD to create SSO projects

    Keywords Windows Azure AD, SSO Summary Use Windows Azure AD to create SSO projects Detailed Scenario ...

  2. 第四十六课:MVC和MVVM的开发区别

    实现MVC的目的就是为了让M和V相分离.前端的MVC无法做到View和Model的相分离,而MVVM可以. 我们先来看一个用MVC模式开发的经典例子:(一定要深入了解这种开发的思想,而不是看懂代码) ...

  3. 使用Web Deploy进行远程部署

    Web Deploy支持直接从本地Visual Studio的工程文件部署网站到远程服务器,部署的过程中可以对比哪些文件变化了需要拷贝,而不是一股脑的全部拷贝,效率和准确性会更好. 部署的过程主要要注 ...

  4. Qt无边框,可移动窗口

    QPoint dragPosition; void MainWindow::mousePressEvent(QMouseEvent *event) { if(event->button()==Q ...

  5. jvm classLoader architecture :

    jvm classLoader architecture : a.Bootstrap ClassLoader/启动类加载器 主要负责jdk_home/lib目录下的核心         api 或 - ...

  6. 2014ACMICPC西安网赛1006

    题意:给你一个骰子的初始状态和可以进行的四种操作,求从初始状态到目标状态的最少操作次数 题目本身很简单,bfs即可.但是因为骰子有六个面,搜索判重和记录状态比较麻烦.这时候就需要神器STL了. #in ...

  7. jQuery的查找

    children([expr])概述 :取得一个包含匹配的元素集合中每一个元素的所有子元素的元素集合.可以通过可选的表达式来过滤所匹配的子元素.注意:parents()将查找所有祖辈元素,而child ...

  8. laravel框架中的session问题

    这两天一直在鼓捣服务器,配置环境,在搭建laravel的过程之中,发现了laravel中的session的一些问题,这里总结一下: (1):我在服务器上搭建了多个sever,为了测试学习,分别使用不同 ...

  9. Laravel教程 二:路由,视图,控制器工作流程

    Laravel教程 二:路由,视图,控制器工作流程 此文章为原创文章,未经同意,禁止转载. View Controller 上一篇教程我们走了那么长的路,终于把Laravel安装好了,这一篇教程我们就 ...

  10. Visual Studio IIS Express 不支持.json后缀的mime类型

    vs默认web调试工具中并不支持.json后缀的mime类型. 如何解决,两个方案: 局部单应用:web.config: <system.webServer> .... <stati ...