我正在编写一个脚本,我需要在许多不同的地方克隆数组。出于这个原因,我想执行以下操作来模拟克隆函数:
var clone = [].slice.call;
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = clone(arr1, 0);
不幸的是,上面的代码会导致:TypeError: object is not a function
。我意识到有许多功能可以做深度克隆和浅拷贝,但我只是想使用内置方法。有趣的是,以下内容确实有效:
var clone = [].slice;
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = clone.call(arr1, 0);
有人知道为什么第一个块不起作用而第二个块不起作用?有没有办法在调用引用函数时引用函数调用和应用函数而不抛出错误?
答案 0 :(得分:3)
您可以直接调用slice
来克隆数组:
var arr2 = arr1.slice();
如果您需要clone
功能,可以执行以下操作:
var clone = function(arr) { return arr.slice(); };
如果你真的想要原型功能(只要功能没有被覆盖就没有必要):
var clone = function(arr) { return [].slice.call(arr); };
为什么不能直接引用call
或apply
?
由于将对象的方法分配给变量而不“工作”的原因不同。
如果您致电func.call()
,则this
内的call
将引用func
,即功能对象。
如果将call
分配给变量,则上下文将丢失。您可以参考通用call
函数。因此,您必须再次将正确的上下文(要应用call
的方法)作为第一个参数传递给call
:
var clone = [].slice.call;
var arr2 = clone.call([].slice, arr1);
这实际上并不是一种改进而且令人困惑。
call
和apply
是每个函数都从Function.prototype
继承的方法。函数没有自己的版本。 [].slice.call === [].splice.call
会产生true
。
答案 1 :(得分:3)
我必须同意Felix King和pimvdb。我认为使用Function.protoytpe.bind()函数的唯一缺点是,这不是所有浏览器(例如IE6)中都可用的函数。另一种方法是使用提供curry()函数的JavaScript库。另一种方法是定义一个函数,使您能够检索任何其他函数的调用函数。这是一个我posted on my blog这个函数的定义,我称之为getCall():
Function.prototype.getCall = function() {
var realFn = this;
return function(objThis) {
return realFn.apply(objThis, Array.prototype.slice.call(arguments, 1));
};
};
现在,通过此定义,您可以执行以下操作来获取对切片函数的调用函数的引用:
var slice = [].slice.getCall();
答案 2 :(得分:2)
问题是whatever_function.call
等于Function.prototype.call
。因此,您有效地保存了对Function.prototype.call
的引用,并且丢失了切片函数的信息。
将其与自定义函数进行比较:
Function.prototype.custom = function() { console.log(this) };
[].slice.custom(); // logs slice function
var ref = [].slice.custom;
ref(); // logs window object
保持this
值不被更改的方法是使用Function.prototype.bind
:
var ref = [].slice.call.bind([].slice);
现在,
ref([1,2,3], 1); // [2, 3]
因为在调用.call
函数时,this
值绑定到切片函数,一切都按预期工作。
答案 3 :(得分:1)
区别在于函数的范围,即“this”是什么。我不确定正确的技术术语是什么,但当一个函数被称为“独立”或作为对象的属性时,“this”就不一样了。
var myObj = {};
myObj.foo = function () {
console.log(this.bar);
};
myObj.bar = 1234;
var fooRef = myObj.foo;
myObj.foo(); // 1234
fooRef(); // undefined
但是,您可以创建一个函数来包装对函数的调用并传递所有参数:
var fooEncapsulated = function () {
return myObj.foo.apply(myObj, arguments);
}
fooEncapsulated(); // 1234
为了记录,最常见的方法是:
Array.prototype.slice.call(myArray, other, arguments, here);
答案 4 :(得分:0)
甜蜜而简单:
slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1,2,3]); // [1,2,3]
slice({length:3,0:1,1:2,2:3}); // [1,2,3]