编写高质量JS代码的68个有效方法(五)
2014-12-06 08:50
323 查看
No.21、使用apply方法通过不同数量的参数调用函数
Tips:
使用apply方法自定一个可计算的参数数组来调用可变参数的函数
使用apply方法的第一个参数给可变参数的方法提供一个接收者
该方法和call()方法功能基本类似,差别在于参数写法不一样。
使用隐式的arguments对象实现可变参数的函数
考虑对可变参数的函数提供一个额外的固定元数的版本,从而使用者无需借助apply方法。
每一个函数内部都有一个arguments对象包含所有传递的参数
永远不要修改arguments的值
使用[].slice.call(arguments)将arguments对象赋值到一个真正的数组中再进行修改
arguments看起来像是数组,但是它并不是标准的数组,所以不支持数组的原型方法
正确的做法是,将arguments转换为真正的数组,再进行操作,代码如下:
注意:永远不要修改arguments对象是更为安全的。
当引用arguments时当心函数嵌套层级
绑定一个明确作用域的引用到arguments变量,从而可以再嵌套的函数中引用它
首先,先来看一段代码的输出:
arguments是函数中的隐式变量,每个函数都会有这样的一个隐式对象。所以最后一个console的结果可想而知。所以遇到这种场景,是建议用变量保存arguments的引用,也能让嵌套函数正确的进行对象引用,正确代码如下:
要注意,提取一个方法不会将方法的接收者绑定到该方法的对象上
当给高阶函数传递对象方法时,使用匿名函数在适当的接收者上调用该方法
使用bind方法创建绑定到适当接收者的函数
老规矩,看代码:(代码1)
该代码在直接使用时是没有问题的,思考下,由于高阶函数将函数/方法作为变量传递,那么可以有如下用法:(代码2)
以上代码在arr.forEach处已经报错,Cannot read property 'push' of undefined。因为这个时候的涉及到this的指向问题。我们可以改造下buffer代码,输出this让我们看看:(代码3)
从输出结果我们可以看到这个this,在(代码2)的执行环境中,指向的是window对象,所以导致了报错,那么如何避免这样的问题呢?针对forEach,我们有三个方法:(代码4)
针对这样的问题,ES5标准库中提供了一个bind()函数来实现这样的方法。只需要如下代码:
该bind()函数,利用buffer.add.bind(buffer)创建了一个新函数而不是修改了buffer.add函数。新函数行为就像原来函数的行为,但它的接收者被重新指定了。所以调用bind方法是安全的,即使是一个可能在程序的其他部分被共享的函数。
Tips:
使用apply方法自定一个可计算的参数数组来调用可变参数的函数
使用apply方法的第一个参数给可变参数的方法提供一个接收者
//示例:计算给定数据的最大值 function getMaxNum(){ var max = arguments[0]; for(var i = 1, len = arguments.length;i < len; i++){ if(max < arguments[i]){ max = arguments[i]; } } return max; } getMaxNum.apply(null,[1,3,4]);
该方法和call()方法功能基本类似,差别在于参数写法不一样。
No.22、使用arguments创建可变参数的函数
Tips:使用隐式的arguments对象实现可变参数的函数
考虑对可变参数的函数提供一个额外的固定元数的版本,从而使用者无需借助apply方法。
每一个函数内部都有一个arguments对象包含所有传递的参数
function fun1(){ console.log(arguments); } fun1('1'); fun1(1,'2','str');
No.23、永远不要修改arguments的值
Tips:永远不要修改arguments的值
使用[].slice.call(arguments)将arguments对象赋值到一个真正的数组中再进行修改
arguments看起来像是数组,但是它并不是标准的数组,所以不支持数组的原型方法
function fun1(nums){ var lastParam = arguments.pop(); //报错,undefined is not a function。 console.log(arguments); } fun1([1, 2, 3]);
正确的做法是,将arguments转换为真正的数组,再进行操作,代码如下:
function fun1(nums){ var argArr = [].slice.call(arguments); var lastParam = argArr.pop(); console.log(arguments); } fun1([1, 2, 3]);
注意:永远不要修改arguments对象是更为安全的。
No.24、使用变量保存arguments的引用
Tips:当引用arguments时当心函数嵌套层级
绑定一个明确作用域的引用到arguments变量,从而可以再嵌套的函数中引用它
首先,先来看一段代码的输出:
function fun1(){ var i = 0; console.log(arguments); return { next:function(){ return arguments[i++]; } } } var f = fun1(1,2,3,4); console.log(f.next()); //猜猜是啥?
arguments是函数中的隐式变量,每个函数都会有这样的一个隐式对象。所以最后一个console的结果可想而知。所以遇到这种场景,是建议用变量保存arguments的引用,也能让嵌套函数正确的进行对象引用,正确代码如下:
function fun1(){ var i = 0; var args = arguments; return { next:function(){ return args[i++]; } } } var f = fun1(1,2,3,4); console.log(f.next());
No.25、使用bind方法提取具有确定接收者的方法
Tips:要注意,提取一个方法不会将方法的接收者绑定到该方法的对象上
当给高阶函数传递对象方法时,使用匿名函数在适当的接收者上调用该方法
使用bind方法创建绑定到适当接收者的函数
老规矩,看代码:(代码1)
var buffer = { entries: [], add: function(value){ this.entries.push(value); }, concat: function(){ return this.entries.join(''); } };
该代码在直接使用时是没有问题的,思考下,由于高阶函数将函数/方法作为变量传递,那么可以有如下用法:(代码2)
var arr = ['Jay', '.M', '.Hu']; arr.forEach(buffer.add); console.log(buffer.concat()); //思考下这个结果是什么?
以上代码在arr.forEach处已经报错,Cannot read property 'push' of undefined。因为这个时候的涉及到this的指向问题。我们可以改造下buffer代码,输出this让我们看看:(代码3)
var buffer = { entries: [], add: function(value){ console.log(this); this.entries.push(value); }, concat: function(){ return this.entries.join(''); } };
从输出结果我们可以看到这个this,在(代码2)的执行环境中,指向的是window对象,所以导致了报错,那么如何避免这样的问题呢?针对forEach,我们有三个方法:(代码4)
//方式一,去掉this,直接用buffer对象引用 var buffer = { entries: [], add: function(value){ buffer.entries.push(value); }, concat: function(){ return buffer.entries.join(''); } }; //方式二,指定接收者,forEach方法提供,其他方法不一定提供 var arr = ['Jay', '.M', '.Hu']; arr.forEach(buffer.add, buffer); console.log(buffer.concat()); //方式三,通过用函数包装调用,来实现指定接收者 var arr = ['Jay', '.M', '.Hu']; arr.forEach(function(s){ buffer.add(s); }); console.log(buffer.concat());
针对这样的问题,ES5标准库中提供了一个bind()函数来实现这样的方法。只需要如下代码:
var arr = ['Jay', '.M', '.Hu']; arr.forEach(buffer.add.bind(buffer)); console.log(buffer.concat());
该bind()函数,利用buffer.add.bind(buffer)创建了一个新函数而不是修改了buffer.add函数。新函数行为就像原来函数的行为,但它的接收者被重新指定了。所以调用bind方法是安全的,即使是一个可能在程序的其他部分被共享的函数。
相关文章推荐
- 编写高质量JS代码的68个有效方法(四)
- 编写高质量JS代码的68个有效方法(十)
- 编写高质量JS代码的68个有效方法(六)
- 编写高质量JS代码的68个有效方法(十三)
- 编写高质量JS代码的68个有效方法(八)
- 编写高质量JS代码的68个有效方法(十二)
- 编写高质量JS代码的68个有效方法(三)
- 编写高质量JS代码的68个有效方法(九)
- JavaScript手札:《编写高质量JS代码的68个有效方法》(一)(1~5)
- 编写高质量JS代码的68个有效方法(十一)
- 编写高质量JS代码的68个有效方法(七)
- 编写高质量JS代码的68个有效方法(二)
- isNaN不一定准(Effective JavaScript 编写高质量JavaScript代码的68个有效方法)
- Effective Javascript编写高质量JavaScript代码的68个有效方法
- 编写高质量JavaScript代码的68个有效方法
- Effective JavaScript:编写高质量JavaScript代码的68个有效方法(1-2章)
- 《Effective Object-C 2.0 编写高质量IOS与OS X代码的52个有效方法》笔记
- 编写高质量iOS和OS X代码的52个有效方法(笔记一)
- Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法(一)
- 编写高质量iOS与OS X代码的52个有效的方法 之协议与分类