I am writing a script in which I need to clone arrays in many different places. For this reason, I would like to do the following to emulate a cloning function:
var clone = [].slice.call;
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = clone(arr1, 0);
Unfortunately, the above code results in: TypeError: object is not a function
. I realize there are many functions out there to do deep cloning and shallow copies but I just want to use the built in method. Interestingly enough, the following does work:
var clone = [].slice;
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = clone.call(arr1, 0);
Does anyone know why the first block doesn't work while the second doe开发者_运维百科s? Is there any way to reference a functions call and apply functions without throwing errors when calling the referenced function?
I have to definitely agree with both Felix King and pimvdb. I think the only drawback to using the Function.protoytpe.bind() function is the fact that this is not a function that is available in all browsers (IE6 for example). An alternative would be to use a JavaScript library that provides the curry() function. Another alternative would be to define a function which gives you the ability to retrieve the call function for any other function. Here is a definition that I posted on my blog for such a function which I called getCall():
Function.prototype.getCall = function() {
var realFn = this;
return function(objThis) {
return realFn.apply(objThis, Array.prototype.slice.call(arguments, 1));
};
};
Now, with this definition, you could do the following to get a reference to the call function of the slice function:
var slice = [].slice.getCall();
You can clone an array by calling slice
directly:
var arr2 = arr1.slice();
If you want a clone
function, you can do:
var clone = function(arr) { return arr.slice(); };
If you really want to prototype function (which is not necessary as long as the function is not overwritten):
var clone = function(arr) { return [].slice.call(arr); };
Why can't you reference call
or apply
directly?
It does not work for the same reason assigning a method of an object to a variable does not "work".
If you call func.call()
then this
inside call
will be a reference to func
, a function object.
If you assign call
to a variable then the context is lost. You have a reference to the generic call
function. Thus you'd have to pass the correct context (the method you want to apply call
to) as first parameter to call
again:
var clone = [].slice.call;
var arr2 = clone.call([].slice, arr1);
This is not really an improvement and quite confusing.
call
and apply
are methods that every functions inherits from Function.prototype
. Functions don't have their own version of them. [].slice.call === [].splice.call
yields true
.
The difference is the scope of the function, i.e. what "this" is. I'm not sure what the correct technical terms are, but the "this" is not the same when a function is called "stand alone" or as a property of an object.
var myObj = {};
myObj.foo = function () {
console.log(this.bar);
};
myObj.bar = 1234;
var fooRef = myObj.foo;
myObj.foo(); // 1234
fooRef(); // undefined
You can however create a function that wraps a call to the function and passes on all the arguments:
var fooEncapsulated = function () {
return myObj.foo.apply(myObj, arguments);
}
fooEncapsulated(); // 1234
For the record, the most common way of doing this is:
Array.prototype.slice.call(myArray, other, arguments, here);
The problem is that whatever_function.call
is equal to Function.prototype.call
. Thus, you effectively save a reference to Function.prototype.call
and the information that it is the slice function is lost.
Compare it with a custom function:
Function.prototype.custom = function() { console.log(this) };
[].slice.custom(); // logs slice function
var ref = [].slice.custom;
ref(); // logs window object
A method of keeping the this
value from being changed is using Function.prototype.bind
:
var ref = [].slice.call.bind([].slice);
Now,
ref([1,2,3], 1); // [2, 3]
because when calling the .call
function, the this
value is bound to the slice function and everything works as expected.
Sweet and simple:
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]
精彩评论