[Effective JavaScript 笔记]第23条:永远不要修改arguments对象
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对象的更多相关文章
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法
前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数argument ...
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- 永远不要修改arguments对象
案例复现 var obj = { plus: function(arg0, arg1) { return arg0 + arg1; } }; function callMethod(context, ...
- [Effective JavaScript 笔记]第22条:使用arguments创建可变参数的函数
第21条讲述使用可变参数的函数average.该函数可处理任意数量的参数并返回这些参数的平均值. 如何创建可变参数的函数 1.实现固定元数的函数 书上的版本 function averageOfArr ...
- [Effective JavaScript 笔记]第54条:将undefined看做“没有值”
undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined ...
- [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...
随机推荐
- Mongodb使用基本之——安装
版本是:Mongodb 3.2.1 本来是想用python做个爬虫然后爬取一些数据放到数据库上的,想着想着以为NoSQL会很流行,就用了Mongodb,结果,一折腾真是不容易. 遇到的第一个问题:官网 ...
- 怎样提高Windows Azure Cloud Service中的WebRole的文件访问权限
关键字:WebRole 1. 背景 Web应用程序需要读取和写入该项目下的文件的权限. 在默认情况下,W3wp.exe 和WaIISHost.exe的运行账号是Network Service,而Net ...
- cookie的操作
比如这样如果一个网站上有两个域名的时候,我们需要考虑,两个域名下的cookie. 我们所说的跨域只的就是lesport.com和le.com js的cookie是不能跨域的,为了防止过大. js增加删 ...
- [AHOI2013]立方体(三维bit)
[Ahoi2013]立方体 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 130 Solved: 55[Submit][Status] Descrip ...
- 01.C#数据类型、排序、过滤(一章1.1-1.2)
随着看的文章及探讨越多,越发现自己实在太不定性了,看下<C#深入理解>吧,做一下读书笔记,从中发现自己的不足.闲话不说,进入正题吧. 在C#1中定下一个简单的数据类型 public cla ...
- Linq之Lambda表达式初步认识
目录 写在前面 匿名方法 一个例子 Lambda 定义 一个例子 总结 参考文章 写在前面 元旦三天在家闲着无事,就看了看Linq的相关内容,也准备系统的学习一下,作为学习Linq的前奏,还是先得说说 ...
- 【转】div居中代码 DIV水平居中显示CSS代码
原文地址:http://www.divcss5.com/rumen/r622.shtml 如何使用CSS让DIV居中显示,让div水平居中有哪些CSS样式呢? 需要的主要css代码有两个,一个为tex ...
- OC基础--ARC的基本使用
一.ARC的判断准则:只要没有强指针指向对象,就会释放对象 二.ARC特点: 1>不允许使用release.retain.retainCount 2>允许重写dealloc,但是不允许调用 ...
- java 关键字 transient
一个对象实现了Serilizable 接口,该对象就可以被序列化. 然而在实际开发工程中,我们会遇到,这个类的有些属性不需要序列化,比如包含用户的敏感信息(如密码),为了安全起见,不希望在网络操作(主 ...
- 【kAri OJ605】陈队的树
时间限制 1000 ms 内存限制 65536 KB 题目描述 陈队有N棵树,有一天他突然想修剪一下这N棵树,他有M个修剪器,对于每个修剪器给出一个高度h,表示这个修剪器可以把某一棵高度超过h的树修剪 ...