最近在学习JavaScript中的闭包,涉及到其中一个案例,想着改写一下。案例挺简单,但是改bug过程有些曲折,在此分享一下在改bug过程中的自我怀疑自我否定直到曲径通幽的心路历程。
需求说明:我们知道arguments对象如果想调用Array的push方法,需要使用Array.prototype.push.apply(argument,[1,2])这样的方式,但每次都写这么一长串代码特别繁琐,想着提取一个push方法,方便以后直接调用。废话不多说,,直接贴代码。//提取的方法var push = (function(){ return function(){ var obj = Array.prototype.shift.apply(arguments); Array.prototype.push.apply(obj,arguments); return arguments; }})()
方法洋洋洒洒写完之后,进入自测阶段。为了偷懒,再次使用了自执行函数,代码如下:
//自测代码(function(){ push(arguments,4) console.log(arguments);})(1,2,3)
在node环境中执行,结果抛错如下:
Array.prototype.push.apply(obj,arguments); ^TypeError: Cannot assign to read only property 'length' of function 'function (){ push(arguments,4) console.log(arguments);}'
这个报错很明显,当给一个function调用Array.porotype.push方法时会抛出这样的错误,因为一个function的length属性是只读的,而push的源码中length属性是需要可以修改的,即一个对象具有可读写的length属性之后才可以调用Array.porototype.push方法。
回到我提取的push方法中分析,Array.prototype.push.apply(obj,arguments);此刻我的obj应该是一个类数组{ '0': 1, '1': 2, '2': 3 },所以报错提示无法直观解决问题。首先为了排除测试案例的原因,我修改了我的测试案例,避免function的介入,代码如下:
//测试案例var fn = function(){ push(arguments,4); console.log(arguments);}fn(1,2,3);
修改完成之后发现测试顺利通过,说明我提取的方法没有问题。到此,牵扯出了一个新问题,我使用自执行函数时为啥会导致出错。
首先来到我脑海的原因是自执行函数的执行顺序,是否有异步一说。因为我提取push方法也是用的自执行函数,测试案例也是自执行函数,有可能两自执行函数的先后执行顺序导致了问题,为了验证这个答案,我决定将自测案例延时执行,代码如下:
setTimeout(function(){ (function(){ push(arguments,4) console.log(arguments); })(1,2,3)},10);
测试发现顺利通过,这个测试结果更加怀疑了刚才的假设(虽然心里一万个不相信)。然后开始查js自执行函数的执行顺序的问题,未果,说明大家都没遇到这样的问题,那很有可能我这假设不对。没办法,只能继续修改测试,既然是因为调用push.apply时候出错,我先注释掉它试试。代码如下:
//提取方法代码var push = (function(){ return function(){ var obj = Array.prototype.shift.apply(arguments); // Array.prototype.push.apply(obj,arguments); return arguments; }})()//测试案例代码(function(){ // push(arguments,4) console.log(arguments);})(1,2,3)
运行结果,抛出了不一样的错误,不一样的烟花,错误如下
D:\github\jsStudy\heightFn.js:174 })(1,2,3) ^TypeError: (intermediate value)(...)(...) is not a function
这个错误提示很容易定位到了问题,自执行函数结尾未写分号导致。
这个问题其实是一个小问题导致的,无关乎技巧。分享这个主要是想表达:在遇到问题时,我们看到的问题可能只是表象,寻找解决方案的过程就是一个不断自我猜想,验证猜想,不断否定的过程,需要持续不断的挖掘。最后贴出最终代码:var push = (function(){ return function(){ var obj = Array.prototype.shift.apply(arguments); Array.prototype.push.apply(obj,arguments); return arguments; }})();//测试案例;(function(){ push(arguments,4) console.log(arguments);})(1,2,3);