Array.prototype.slice.call ()은 어떻게 작동합니까?


474

나는 그것이 인수를 실제 배열로 만드는 데 사용된다는 것을 알고 있지만 사용할 때 어떤 일이 발생하는지 이해하지 못한다 Array.prototype.slice.call(arguments)


2
^ 링크가 인수가 아닌 DOM 노드에 대해 묻기 때문에 약간 다릅니다. 그리고 나는 이것이 '이것'이 무엇인지 설명함으로써 여기에 대한 대답이 훨씬 낫다고 생각합니다.
sqram

답변:


870

후드 아래에서 발생하는 .slice()것은 정상적으로 호출 될 때 this배열이며 배열을 반복하고 작업을 수행한다는 것입니다.

함수 this에서 어떻게 .slice()배열입니까? 당신이 할 때 :

object.method();

...는 object자동의 값이된다 this하여 method(). 따라서 :

[1,2,3].slice()

... [1,2,3]배열은 thisin 의 값으로 설정됩니다 .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값을 로 설정할 수 있으면 제대로 작동 .slice()하기 .slice()에 충분히 배열처럼 보이기 때문에 작동합니다.

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

예 : http://jsfiddle.net/wSvkv/

콘솔에서 볼 수 있듯이 결과는 다음과 같습니다.

['three','four'];

따라서 arguments객체를 this값으로 설정하면 이런 일이 발생합니다 .slice(). 속성과 많은 숫자 인덱스 arguments가 있기 때문에 실제 배열에서 작업하는 것처럼 작업을 수행합니다..length.slice()


7
좋은 답변입니다! 그러나 슬프게도 실제 단어처럼 객체 키가 문자열 값인 경우 객체를 이런 식으로 변환 할 수 없습니다. 실패하면 객체 내용을 'stringName'이 아닌 '0': 'value'로 유지하십시오 :'값'.
joopmicroop

6
@Michael : 오픈 소스 JS 구현의 소스 코드를 읽는 것이 가능하지만 "ECMAScript"언어 사양을 참조하는 것이 더 간단합니다. 다음Array.prototype.slice메소드 설명에 대한 링크 입니다.

1
객체 키는 순서가 없으므로이 특정 데모는 다른 브라우저에서 실패 할 수 있습니다. 모든 공급 업체가 객체의 키를 생성 순서대로 정렬하지는 않습니다.
vsync

1
@vsync : for-in순서를 보장하지 않는 진술입니다. 에 의해 사용되는 알고리즘 은 주어진 객체 (또는 Array 등 )로 .slice()시작 0하고 끝나는 (비 포함) 숫자 순서 를 정의합니다 .length. 따라서 순서는 모든 구현에서 일관성이 보장됩니다.
쿠키 몬스터

7
@vsync : 가정이 아닙니다. 당신이 그것을 시행하는 경우 모든 개체에서 주문을 얻을 수 있습니다. 내가 있다고 가정 해 봅시다 var obj = {2:"two", 0:"zero", 1: "one"}. for-in객체를 열거하는 데 사용 하면 순서를 보장 할 수 없습니다. 그러나를 사용하면 for수동으로 주문을 시행 할 수 있습니다 for (var i = 0; i < 3; i++) { console.log(obj[i]); }. 이제 객체의 속성이 for루프에 의해 정의 된 오름차순으로 표시됩니다 . 그게 뭐야 .slice(). 실제 배열이 있는지는 중요하지 않습니다. 그냥 시작 0하고 오름차순 루프의 속성에 액세스합니다.
쿠키 몬스터

88

arguments객체는 실제로 배열의 인스턴스가 아닌, 및 배열 방법을 가지고 있지 않습니다. 따라서 arguments.slice(...)arguments 객체에 slice 메서드가 없으므로 작동하지 않습니다.

배열에는이 방법이 있으며 arguments객체는 배열과 매우 유사하기 때문에 두 개가 호환됩니다. 즉, arguments 객체와 함께 배열 메서드를 사용할 수 있습니다. 그리고 배열 메소드는 배열을 염두에두고 작성되었으므로 다른 인수 오브젝트가 아닌 배열을 리턴합니다.

왜 사용 Array.prototype합니까? 는 Array우리가 (의 새로운 배열을 생성하는 객체입니다 new Array()), 그리고이 새로운 배열은 조각처럼, 방법 및 속성을 전달됩니다. 이러한 메소드는 [Class].prototype객체에 저장됩니다 . 따라서 효율성 향상을 위해 (new Array()).slice.call()또는로 슬라이스 방법에 액세스하는 대신 [].slice.call()프로토 타입에서 바로 가져옵니다. 따라서 새로운 배열을 초기화 할 필요가 없습니다.

그러나 왜 우리는 처음에 이것을해야합니까? 글쎄, 당신이 말했듯이, 그것은 arguments 객체를 Array 인스턴스로 변환합니다. 그러나 슬라이스를 사용하는 이유는 무엇보다 "핵"입니다. slice 메소드는 배열의 슬라이스를 가져 와서 그 슬라이스를 새로운 배열로 반환합니다. arguments 객체를 인수로 컨텍스트에 전달하지 않고 전달하면 slice 메서드는 전달 된 "배열"(이 경우 arguments 객체)의 전체 청크를 가져 와서 새 배열로 반환합니다.


여기에 설명 된 이유로 슬라이스를 사용할 수 있습니다. jspatterns.com/arguments-considered-harmful
KooiInc

44

일반적으로 전화

var b = a.slice();

배열 a을에 복사합니다 b. 그러나 우리는 할 수 없습니다

var a = arguments.slice();

arguments실제 배열이 아니며 slice메소드 가 없기 때문 입니다 . Array.prototype.slice는 IS slice배열 기능과 call함께 기능을 실행 this에 집합 arguments.


2
고맙지 만 왜 사용 prototype합니까? 되지는 slice기본 Array방법은?
ilyo

2
참고 Array생성자 함수이며, 해당 "클래스"입니다 Array.prototype. 다음을 사용할 수도 있습니다[].slice
user123444555621

4
IlyaD slice는 각 Array인스턴스 의 메서드 이지만 Array생성자 함수는 아닙니다 . prototype생성자의 이론적 인스턴스 메소드에 액세스 하는 데 사용 합니다.
Delan Azabani

23

먼저 JavaScript에서 함수 호출이 작동하는 방식 을 읽어야 합니다 . 혼자서도 귀하의 질문에 대답하기에 충분하다고 생각합니다. 그러나 여기에 무슨 일이 일어나고 있는지 요약되어 있습니다.

Array.prototype.slice추출물 방법 에서 의 프로토 타입 . 그러나 메서드 (함수가 아님) 이므로 컨텍스트 (호출 객체 ) 가 필요하므로 직접 호출하면 작동 하지 않습니다 . 그렇지 않으면 throw 됩니다.slice ArraythisUncaught 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);

22
// 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]

16

Array.prototype.slice.call (arguments)는 인수를 배열로 변환하는 구식 방법입니다.

ECMAScript 2015에서는 Array.from 또는 스프레드 연산자를 사용할 수 있습니다.

let args = Array.from(arguments);

let args = [...arguments];

9

그 때문에, 같은 MDN 노트

arguments 객체는 배열이 아닙니다. 배열과 비슷하지만 길이를 제외하고 배열 속성이 없습니다. 예를 들어, pop 메소드가 없습니다. 그러나 실제 배열로 변환 할 수 있습니다.

여기에서 우리는 호출하는 slice기본 객체 Array가 아닌 자사에 구현 하고 그게 왜 추가.prototype

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

4

이 동작의 저수준 기본 사항은 JS 엔진에 완전히 통합 된 유형 캐스팅이라는 것을 잊지 마십시오.

슬라이스는 단지 기존의 arguments.length 속성으로 인해 객체를 가져 와서 모든 작업을 수행 한 후에 캐스팅 된 배열 객체를 반환합니다.

String-method를 INT 값으로 처리하려고 시도 할 때 테스트 할 수있는 동일한 논리 :

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

그리고 그것은 위의 진술을 설명합니다.


1

그것은 slice배열을 가지고 메소드를 사용하고 그것을 객체 this로 호출 arguments합니다. 즉, 그러한 메소드 가 arguments.slice()있다고 가정 한 것처럼 호출합니다 arguments.

인수없이 슬라이스를 만들면 모든 요소가 사용되므로 요소를 arguments배열로 복사하기 만하면 됩니다.


1

당신이 가지고 있다고 가정 해 봅시다. function.apply(thisArg, argArray )

apply 메소드는 함수를 호출하여 이것에 바인딩 될 객체와 선택적 인수 배열을 전달합니다.

slice () 메서드는 배열의 일부를 선택하고 새 배열을 반환합니다.

따라서 Array.prototype.slice.apply(arguments, [0])배열 슬라이스 메소드를 호출하면 인수에 대해 호출 (바인드)됩니다.


1

어쩌면 조금 늦었지만이 혼란에 대한 대답은 call ()이 JS에서 상속을 위해 사용된다는 것입니다. 예를 들어 이것을 파이썬이나 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


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

-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);
    });
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.