Array.prototype.slice.call()如何工作?

时间:2011-08-14 12:59:05

标签: javascript prototype-programming

我知道它用于使参数成为一个真正的数组,但我不明白使用Array.prototype.slice.call(arguments)时会发生什么

15 个答案:

答案 0 :(得分:826)

在幕后发生的事情是,当.slice()被正常调用时,this是一个数组,然后它只是迭代该数组,并完成其工作。

this函数中的.slice()如何成为一个数组?因为当你这样做:

object.method();

... object自动成为thismethod()的值。所以:

[1,2,3].slice()

... [1,2,3]数组设置为this.slice()的值。


但是如果你可以用其他东西代替this值呢?只要你替换的任何东西都有一个数字.length属性,以及一堆属性为数字索引,它应该可以工作。这种类型的对象通常称为类似数组的对象

.call().apply()方法可让您手动在函数中设置this的值。因此,如果我们将this.slice()的值设置为类似数组的对象,则.slice()只会假设它正在使用一个数组,并将做它的事情。

以此普通对象为例。

var my_object = {
    '0': 'zero',
    '1': 'one',
    '2': 'two',
    '3': 'three',
    '4': 'four',
    length: 5
};

这显然不是一个数组,但如果您可以将其设置为this的{​​{1}}值,那么它就可以正常工作,因为它看起来像.slice()的数组工作正常。

.slice()

示例: http://jsfiddle.net/wSvkv/

正如您在控制台中看到的那样,结果就是我们所期望的:

var sliced = Array.prototype.slice.call( my_object, 3 );

因此,当您将['three','four']; 对象设置为arguments this的值时,会发生这种情况。由于.slice()具有arguments属性和一堆数字索引,.length只是将其工作视为真正的数组。

答案 1 :(得分:79)

arguments对象实际上不是Array的实例,并且没有任何Array方法。因此,arguments.slice(...)将不起作用,因为arguments对象没有slice方法。

数组确实有这种方法,因为arguments对象与数组非常相似,所以两者是兼容的。这意味着我们可以使用带有arguments对象的数组方法。由于数组方法是在构建时考虑了数组,因此它们将返回数组而不是其他参数对象。

那么为什么要使用Array.prototypeArray是我们从(new Array())创建新数组的对象,这些新数组是传递方法和属性,如slice。这些方法存储在[Class].prototype对象中。因此,为了提高效率,我们不是通过(new Array()).slice.call()[].slice.call()访问切片方法,而是直接从原型中获取切片方法。这样我们就不必初始化一个新数组了。

但为什么我们必须首先做到这一点?好吧,正如你所说,它将一个arguments对象转换为一个Array实例。然而,我们使用切片的原因更多的是“黑客”而不是任何东西。切片方法将采用,您猜对了,切片的数组并将该切片作为新数组返回。传递没有参数(除了参数对象作为其上下文)导致slice方法获取传递的“数组”的完整块(在本例中为arguments对象)并将其作为新数组返回。

答案 2 :(得分:41)

通常,请致电

var b = a.slice();

会将数组a复制到b。但是,我们做不到

var a = arguments.slice();

因为arguments不是真正的数组,并且没有slice作为方法。 Array.prototype.slice是数组的slice函数,call运行函数,this设置为arguments

答案 3 :(得分:21)

// We can apply `slice` from  `Array.prototype`:
Array.prototype.slice.call([]); //-> []

// Since `slice` is available on an array's prototype chain,
'slice' in []; //-> true
[].slice === Array.prototype.slice; //-> true

// … we can just invoke it directly:
[].slice(); //-> []

// `arguments` has no `slice` method
'slice' in arguments; //-> false

// … but we can apply it the same way:
Array.prototype.slice.call(arguments); //-> […]

// In fact, though `slice` belongs to `Array.prototype`,
// it can operate on any array-like object:
Array.prototype.slice.call({0: 1, length: 1}); //-> [1]

答案 4 :(得分:15)

首先,您应该阅读how function invocation works in JavaScript。我怀疑只有这一点足以回答你的问题。但这里是对正在发生的事情的总结:

Array.prototype.sliceArray的{​​{3}}中提取slice method。但是直接调用它是行不通的,prototype因此需要一个上下文(一个调用对象,this),否则会抛出Uncaught TypeError: Array.prototype.slice called on null or undefined

as it's a method (not a function)方法允许您指定方法的上下文,基本上使这两个调用等效:

someObject.slice(1, 2);
slice.call(someObject, 1, 2);

除前者要求slice方法存在于someObject的原型链中(与Array一样),而后者允许上下文(someObject )手动传递给方法。

此外,后者简称:

var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);

与以下内容相同:

Array.prototype.slice.call(someObject, 1, 2);

答案 5 :(得分:10)

Array.prototype.slice.call(arguments)是将参数转换为数组的老式方法。

在ECMAScript 2015中,您可以使用Array.from或spread运算符:

let args = Array.from(arguments);

let args = [...arguments];

答案 6 :(得分:9)

因为MDN notes

  

arguments对象不是数组。它类似于数组,但是   除了length之外没有任何数组属性。例如,确实如此   没有pop方法。但是它可以转换为真实数组:

这里我们在本地对象slice上调用Array而不在其实现上调用,这就是额外.prototype

的原因
var args = Array.prototype.slice.call(arguments);

答案 7 :(得分:4)

不要忘记,这种行为的低级基础是完全集成在JS引擎中的类型转换。

Slice只接受对象(感谢现有的arguments.length属性)并在对其执行所有操作后返回已铸造的数组对象。

如果尝试使用INT值处理String-method,则可以测试相同的逻辑:

String.prototype.bold.call(11);  // returns "<b>11</b>"

这解释了上述陈述。

答案 8 :(得分:1)

它使用slice方法数组,并使用this作为arguments对象来调用它。这意味着它会将其称为arguments.slice(),假设arguments有这样的方法。

创建一个没有任何参数的切片只会占用所有元素 - 因此它只是将元素从arguments复制到一个数组。

答案 9 :(得分:1)

我们假设你有:function.apply(thisArg, argArray )

  

apply方法调用一个函数,传入将绑定到该对象的对象   和一个可选的参数数组。

slice()方法选择数组的一部分,并返回新数组。

因此,当您调用Array.prototype.slice.apply(arguments, [0])时,会在参数上调用(绑定)数组切片方法。

答案 10 :(得分:1)

也许有点晚了,但所有这些混乱的答案是在JS中使用call()进行继承。 例如,如果我们将它与Python或PHP进行比较,则call分别用作super()。 init ()或parent :: _ construct()。

这是一个使用它的例子:

function Teacher(first, last, age, gender, interests, subject) {
  Person.call(this, first, last, age, gender, interests);

  this.subject = subject;
}

参考:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance

答案 11 :(得分:1)

Array.prototype.slice=function(start,end){
    let res=[];
    start=start||0;
    end=end||this.length
    for(let i=start;i<end;i++){
        res.push(this[i])
    }
    return res;
}

当你这样做时:

Array.prototype.slice.call(arguments) 

arguments 成为 thisslice 的值,然后 slice 返回一个数组

答案 12 :(得分:0)

当.slice()被正常调用时,这是一个数组,然后它只是迭代该数组,并完成其工作。

 //ARGUMENTS
function func(){
  console.log(arguments);//[1, 2, 3, 4]

  //var arrArguments = arguments.slice();//Uncaught TypeError: undefined is not a function
  var arrArguments = [].slice.call(arguments);//cp array with explicity THIS  
  arrArguments.push('new');
  console.log(arrArguments)
}
func(1,2,3,4)//[1, 2, 3, 4, "new"]

答案 13 :(得分:0)

/*
    arguments: get all args data include Length .
    slice : clone Array
    call: Convert Object which include Length to Array
    Array.prototype.slice.call(arguments): 
        1. Convert arguments to Array
        2. Clone Array arguments
*/
//normal
function abc1(a,b,c){
    console.log(a);
} 
//argument
function: function abc2(){
    console.log(Array.prototype.slice.call(arguments,0,1))
}

abc1('a','b','c');
//a
abc2('a','b','c');
//a

答案 14 :(得分:-1)

我只是写这个来提醒自己......

    Array.prototype.slice.call(arguments);
==  Array.prototype.slice(arguments[1], arguments[2], arguments[3], ...)
==  [ arguments[1], arguments[2], arguments[3], ... ]

或者只是使用这个方便的功能 $ A 将大部分内容转换为数组。

function hasArrayNature(a) {
    return !!a && (typeof a == "object" || typeof a == "function") && "length" in a && !("setInterval" in a) && (Object.prototype.toString.call(a) === "[object Array]" || "callee" in a || "item" in a);
}

function $A(b) {
    if (!hasArrayNature(b)) return [ b ];
    if (b.item) {
        var a = b.length, c = new Array(a);
        while (a--) c[a] = b[a];
        return c;
    }
    return Array.prototype.slice.call(b);
}

示例用法...

function test() {
    $A( arguments ).forEach( function(arg) {
        console.log("Argument: " + arg);
    });
}