开发者

How do you reference Array.prototype.slice.call()?

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]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜