No.21、使用apply方法通过不同数量的参数调用函数
Tips:
//示例:计算给定数据的最大值 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()方法功能基本类似,差别在于参数写法不一样。
Tips:
每一个函数内部都有一个arguments对象包含所有传递的参数
function fun1(){ console.log(arguments); } fun1('1'); fun1(1,'2','str');
Tips:
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对象是更为安全的。
Tips:
首先,先来看一段代码的输出:
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());
Tips:
老规矩,看代码:(代码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方法是安全的,即使是一个可能在程序的其他部分被共享的函数。