繁体   English   中英

`Array.prototype.slice.call` 是如何工作的?

[英]How does `Array.prototype.slice.call` work?

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

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

this是如何在.slice() function 一个数组中的? 因为当你这样做时:

object.method();

... objectmethod()中自动成为this的值。 所以:

[1,2,3].slice()

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


但是如果你可以用别的东西代替this值呢? 只要您替换的任何内容都具有数字.length属性,以及一堆数字索引的属性,它就应该可以工作。 这种类型的 object 通常称为类数组 object

.call( .call().apply()方法允许您在 function 中手动设置this的值。 因此,如果我们在.slice() this值设置为类似数组的 object.slice()假设它正在使用数组,并且会做它的事情。

以这个普通的 object 为例。

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

这显然不是一个数组,但如果你可以将它设置为.slice()this值,那么它就可以工作,因为它看起来很像一个数组,可以让.slice()正常工作。

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

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

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

['three','four'];

因此,当您将arguments object 设置为.slice()this值时,就会发生这种情况。 因为arguments有一个.length属性和一堆数字索引, .slice()就像在处理一个真正的数组一样工作。

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

Arrays确实有这个方法,而且因为arguments object很像一个数组,所以两者是兼容的。 这意味着我们可以在 arguments object 中使用数组方法。 而且由于数组方法是在考虑 arrays 的情况下构建的,因此它们将返回 arrays 而不是其他参数对象。

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

但是为什么我们必须首先这样做呢? 好吧,正如您所说,它将 arguments object 转换为 Array 实例。 然而,我们使用 slice 的原因更多的是“hack”。 slice 方法将获取一个数组的切片,并将该切片作为新数组返回。 Passing no arguments to it (besides the arguments object as its context) causes the slice method to take a complete chunk of the passed "array" (in this case, the arguments object) and return it as a new array.

通常,调用

var b = a.slice();

将数组a复制到b中。 然而,我们做不到

var a = arguments.slice();

因为arguments没有slice作为方法(它不是真正的数组)。

Array.prototype.slice是 arrays 的slice function。 .call运行此slice function, this值设置为arguments

首先,您应该阅读function 调用如何在 JavaScript 中工作 我怀疑仅此一项就足以回答您的问题。 但这里是正在发生的事情的摘要:

Array.prototype.sliceArray原型中提取slice方法 但是直接调用它是行不通的, 因为它是一个方法(不是一个函数) ,因此需要一个上下文(一个调用 object, this ),否则它会抛出Uncaught TypeError: Array.prototype.slice called on null or undefined

call()方法允许您指定方法的上下文,基本上使这两个调用等效:

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);
// 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]

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

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

let args = Array.from(arguments);

let args = [...arguments];

这是因为,正如MDN 所指出

arguments object 不是数组。 它类似于数组,但除了长度之外没有任何数组属性。 例如,它没有 pop 方法。 但是它可以转换为一个真正的数组:

在这里,我们在本机 object Array上调用slice而不是在其实现上,这就是为什么额外的.prototype

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

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

Slice 只需要 object(感谢现有的 arguments.length 属性)并在对其执行所有操作后返回数组对象。

如果您尝试使用 INT 值处理字符串方法,则可以测试相同的逻辑:

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

这解释了上面的陈述。

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变成slicethis的值,然后slice返回一个数组

它使用 arrays 拥有的slice方法,并将this arguments object。 这意味着假设arguments有这样的方法,它就好像你做了arguments.slice()一样调用它。

创建没有任何 arguments 的切片将简单地获取所有元素 - 因此它只是将元素从arguments复制到数组。

假设您有: function.apply(thisArg, argArray )

apply 方法调用 function,传入将绑定到此的 object 和 arguments 的可选数组。

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

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

当.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"]

也许有点晚了,但所有这些混乱的答案是 call() 在 JS 中用于 inheritance。 例如,如果我们将其与 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

/*
    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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM