首页 > 代码库 > 编写高质量JS代码的68个有效方法(五)
编写高质量JS代码的68个有效方法(五)
No.21、使用apply方法通过不同数量的参数调用函数
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方法是安全的,即使是一个可能在程序的其他部分被共享的函数。
<style></style>
编写高质量JS代码的68个有效方法(五)