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()方法返回一个数组,该数组中包含从原始数组中删除的项。注意:会影响原数组。

转载于:https://www.cnblogs.com/wengxuesong/p/5551962.html

[Effective JavaScript 笔记]第23条:永远不要修改arguments对象相关推荐

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

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

  2. Effective JavaScript Item 23 永远不要修改arguments对象

    本系列作为Effective JavaScript的读书笔记. arguments对象只是一个类似数组的对象,但是它并没有数组对象提供的方法,比如shift,push等.因此调用诸如:argument ...

  3. [Effective JavaScript 笔记]第59条:避免过度的强制转换

    js是弱类型语言.许多标准的操作符和代码库会把输入参数强制转换为期望的类型而不是抛出错误.如果未提供额外的逻辑,使用内置操作符的程序会继承这样的强制转换行为. functin square(x){re ...

  4. [Effective JavaScript 笔记]第29条:避免使用非标准的栈检查属性

    许多js环境都提供检查调用栈的功能.调用栈是指当前正在执行的活动函数链.在某些旧的宿主环境中,每个arguments对象含有两个额外的属性:arguments.callee和arguments.cal ...

  5. [Effective JavaScript 笔记]第61条:不要阻塞I/O事件队列

    js程序是构建在事件之上的.输入可能来自不同的外部源.在一些语言中,我们习惯地编写代码来等待某个特定的输入. var text=downloadSync('http://example.com/fil ...

  6. [Effective JavaScript 笔记]第6章:库和API设计--个人总结

    前言 又到了一章的总结,这章里的内容.是把我从一个代码的使用者,如何换位成一个代码的编写者.如何让别人用自己的代码更容易,不用去注意太多的无用细节,不用记住冗长的函数名.在使用API时怎样避免使用者会 ...

  7. Effective Java 泛型 第23条:请不要在新代码中使用原生态类型

    java1.5发行版本中增加了泛型.在没有泛型之前,从集合中读取到的每一个对象都必须进行装换.如果有人不小心插入了类型错误的对象,在运行时的装换处理器就会出错.有了泛型之后,可以告诉编译器每个集合中接 ...

  8. Effective Java学习笔记之第6条 消除过期的引用对象

    第6条 消除过期的引用对象 所谓过期引用是指永远也不会再被解除的引用. 一旦对象引用已经过期,只需清空这些引用即可. public Object pop() { if (size == 0) { th ...

  9. JavaScript 笔记

    学习笔记 部分转载自网络 #目录 [TOC] JavaScript 笔记 JavaScript负责页面中的的行为. 它是一门运行在浏览器端的脚本语言. JS的编写的位置 1.可以编写到标签的指定属性中 ...

最新文章

  1. 使用事件委托降低重复的事件绑定,从而降低dom操作的对性能的消耗
  2. Bootstrap 3之美02-Grid简介和应用
  3. Mint-UI 移动首页开发 - header导航、banner轮播图
  4. es 创建索引_es的基本原理和操作文档
  5. 6.Nginx 的 Rewrite 规则
  6. 【JAVA】学生信息管理系统
  7. Eclipse菜单栏翻译
  8. nods中mysql时间相差8小时
  9. 【视觉运控一体机小课堂】三分钟实现图像颜色通道切换和RGB图转灰度图的功能
  10. 通配符 或 怎么浓_浓咖啡的咖啡渣新鲜度
  11. Google AI逆天了!
  12. POI 操作word
  13. itunes计算机无法启动,解决:Apple移动设备服务无法启动
  14. jvm系列 (四) ---强、软、弱、虚引用
  15. 分离数字的python编码_把数字拆分成2的幂的和
  16. 数字通信系统仿真的MATLAB实现(QPSK)
  17. H5自建企微应用,企业微信获取用户信息(网页如何拿到code,后端如何根据code获得UserId)
  18. 为什么我推荐你一定要学Python?
  19. 根证书的有效期与服务器SSL证书一样长吗?
  20. 学计算机要重修一门语言嘛,大学挂科率“最高”的4门科目,不仅要补考还要重修,你中招了吗...

热门文章

  1. java8泛型包括_JAVA8 获取泛型类型问题
  2. 使用tensorboard可视化tensorflow模型
  3. Monte Carlo方法
  4. android 让应用恢复前台显示_Android程序由后台切换到前台
  5. cublas matlab,Check failed: status == CUBLAS_STATUS_SUCCESS (11 vs. 0) CUBLAS_STATUS_MAPPING_ERROR
  6. mysql drop 几十g的表_MySQL Drop 大表的解决方案
  7. c语言 格式转换函数,C语言中的格式转换函数.doc
  8. linux保存编辑信息,linux系统编辑神器 -vim用法大全
  9. java以32位运行,强制java applet以32位而不是64位JRE运行
  10. Spring AOP之注解配置篇