Array.prototype.slice.call () nasıl çalışır?


474

Argümanları gerçek bir dizi yapmak için kullanıldığını biliyorum, ama kullanırken ne olduğunu anlamıyorum Array.prototype.slice.call(arguments)


2
^ bu bağlantı DOM düğümleri soruyor ve argümanlar değil, biraz farklı. Ve bence buradaki cevap, 'bunun' dahili olarak ne olduğunu açıklayarak çok daha iyi.
sqram

Yanıtlar:


870

Kaputun altında olan şey, .slice()normal olarak adlandırıldığında thisbir Array olmasıdır ve daha sonra sadece bu Array üzerinde yinelenir ve işini yapar.

Nasıl thisiçinde .slice()Dizi fonksiyonu? Çünkü bunu yaptığınızda:

object.method();

... objectotomatik değeri olur thisiçinde method(). Böylece:

[1,2,3].slice()

... [1,2,3]Dizi değeri olarak ayarlanır thisiçinde .slice().


Peki ya thisdeğer olarak başka bir şeyi değiştirebilseydiniz ? .lengthYerine koyduğunuz her şey bir sayısal özelliğe ve sayısal dizinler olan bir grup özelliğe sahip olduğu sürece, çalışmalıdır. Bu tür nesnelere genellikle dizi benzeri nesne denir .

.call()Ve .apply()yöntemleri izin elle değerini ayarlamak thisbir işlevde. Biz değerini ayarlamak Yani eğer thisiçinde .slice()bir dizi benzeri nesne , .slice()sadece edecek varsaymak bir Array birlik olduğuna ve bunun şeyi yapacaktır.

Bu düz nesneyi örnek olarak alalım.

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

Bu açıkça bir Array değildir, ancak thisdeğerini olarak ayarlayabilirseniz .slice(), o zaman işe yarayacaktır, çünkü .slice()düzgün çalışmak için bir Array gibi görünüyor .

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

Örnek: http://jsfiddle.net/wSvkv/

Konsolda görebileceğiniz gibi, sonuç beklediğimiz şeydir:

['three','four'];

Dolayısıyla, bir argumentsnesneyi thisdeğeri olarak ayarladığınızda bu olur .slice(). Çünkü argumentsbir .lengthözelliği ve bir dizi sayısal endeksi vardır, .slice()sadece gerçek bir Array üzerinde çalışıyormuş gibi işine devam eder.


7
Mükemmel cevap! Ancak maalesef, nesne anahtarlarınız gerçek kelimelerdeki gibi dize değerleriyse, herhangi bir nesneyi bu şekilde dönüştüremezsiniz. Bu başarısız olur, bu yüzden nesnelerinizin içeriğini 'stringName' gibi değil '0': 'değer' olarak tutun : 'değer'.
joopmicroop

6
@Michael: Açık kaynak JS uygulamalarının kaynak kodunu okumak mümkündür, ancak "ECMAScript" dil spesifikasyonuna başvurmak daha kolaydır. İşte bir bağlantı için Array.prototype.sliceyöntem tanımı.

1
nesne anahtarlarının sırası olmadığından, bu özel gösterim diğer tarayıcılarda başarısız olabilir. tüm satıcılar nesnelerinin anahtarlarını oluşturma sırasına göre sıralamaz.
vsync

1
@vsync: for-inSiparişi garanti etmeyen açıklama. Tarafından kullanılan algoritma , verilen nesnenin (veya Dizi veya herhangi biriyle) .slice()başlayan 0ve onunla biten (içermeyen) sayısal bir sıra tanımlar .length. Böylece, siparişin tüm uygulamalarda tutarlı olması garanti edilir.
çerez canavarı

7
@vsync: Bu bir varsayım değil. Zorlarsanız herhangi bir nesneden sipariş alabilirsiniz. Diyelim ki var var obj = {2:"two", 0:"zero", 1: "one"}. for-inNesneyi numaralandırmak için kullanırsak , düzen garantisi yoktur. Kullandığımız Ama eğer for, elle zorlayabilir: for (var i = 0; i < 3; i++) { console.log(obj[i]); }. Artık nesnenin özelliklerine, fordöngümüz tarafından tanımladığımız artan sayısal sırayla ulaşılacağını biliyoruz . İşte .slice()böyle. Gerçek bir Dizisi olup olmadığı önemli değildir. Sadece başlar 0ve yükselen bir döngüdeki özelliklere erişir.
çerez canavarı

88

argumentsNesne aslında bir Array örneğidir değildir ve Dizi yöntemlerden herhangi yoktur. Bu nedenle, arguments.slice(...)arguments nesnesinin dilim yöntemi olmadığından çalışmaz.

Diziler bu yönteme sahiptir ve argumentsnesne bir diziye çok benzediğinden ikisi uyumludur. Bu, arguments nesnesiyle dizi yöntemlerini kullanabileceğimiz anlamına gelir. Dizi yöntemleri diziler düşünülerek oluşturulduğundan, diğer argüman nesneleri yerine diziler döndürürler.

Peki neden kullanıyorsunuz Array.prototype? ArrayBiz (yeni diziler oluşturmak amacı new Array()) ve bu yeni diziler dilim gibi, yöntemleri ve özellikleri geçirilir. Bu yöntemler [Class].prototypenesnede saklanır . Yani, verimlilik uğruna, (new Array()).slice.call()veya tarafından dilim yöntemine erişmek yerine, [].slice.call()doğrudan prototipten alıyoruz. Bu yüzden yeni bir dizi başlatmak zorunda değiliz.

Ama neden ilk etapta bunu yapmalıyız? Dediğiniz gibi, bir argüman nesnesini Array örneğine dönüştürür. Ancak dilimi kullanmamızın sebebi her şeyden çok "hack'tir". Dilim yöntemi, bir dizinin dilimini a tahmin edersiniz ve bu dilimi yeni bir dizi olarak döndürür. Hiçbir argümanın iletilmemesi (bağlam olarak arguments nesnesinin yanı sıra) dilim yönteminin iletilen "dizi" nin (bu durumda arguments nesnesi) tam bir parçasını almasına ve yeni bir dizi olarak döndürmesine neden olur.


Burada açıklanan nedenlerden dolayı bir dilim kullanmak isteyebilirsiniz: jspatterns.com/arguments-considered-harmful
KooiInc

44

Normalde

var b = a.slice();

diziyi aiçine kopyalar b. Ancak yapamayız

var a = arguments.slice();

çünkü argumentsgerçek bir dizi değildir ve sliceyöntem olarak yoktur. Array.prototype.slice, slicedizilerin callişlevidir ve işlevi olarak thisayarlı olarak çalıştırır arguments.


2
teşekkürler ama neden kullanmalıyım prototype? sliceyerel bir Arrayyöntem değil mi?
ilyo

2
Not Arraybir yapıcı işlevi olduğu ve karşılık gelen "sınıfı" dir Array.prototype. Ayrıca kullanabilirsiniz[].slice
user123444555621

4
İlyaD, sliceher Arrayörneğin bir yöntemidir , ancak Arrayyapıcı işlevi değildir . Sen kullanmak prototypebir şantiye teorik örneklerinin erişim yöntemlerine ilişkindir.
Delan Azabani

23

İlk olarak, işlev çağırma işleminin JavaScript'te nasıl çalıştığını okumalısınız . Sorunuzu cevaplamak için tek başına yeterli olduğundan şüpheleniyorum. Ama işte neler olduğuna dair bir özet:

Array.prototype.sliceözler yöntemi ile ilgili 'in prototip . Ancak doğrudan çağırmak işe yaramaz, çünkü bu bir yöntemdir (işlev değil) ve bu nedenle bir bağlam (çağıran bir nesne ) gerektirir, aksi takdirde fırlar .slice ArraythisUncaught TypeError: Array.prototype.slice called on null or undefined

call()Yöntem temelde bu iki çağrıları eşdeğer hale bir yöntemin bağlamı belirlemenizi sağlar:

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

Birincisi dışında sliceyöntemin someObjectprototip zincirinde (olduğu gibi Array) var olmasını gerektirirken, ikincisi bağlamın ( someObject) yönteme manuel olarak geçirilmesine izin verir .

Ayrıca, ikincisi kısadır:

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

Hangisi ile aynı:

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 (bağımsız değişkenler), bağımsız değişkenleri bir diziye dönüştürmenin eski yoludur.

ECMAScript 2015'te Array.from veya forma operatörünü kullanabilirsiniz:

let args = Array.from(arguments);

let args = [...arguments];

9

Çünkü MDN notları

Arguments nesnesi bir dizi değildir. Bir diziye benzer, ancak uzunluk dışında herhangi bir dizi özelliği yoktur. Örneğin, pop yöntemi yoktur. Ancak, gerçek bir diziye dönüştürülebilir:

Burada sliceyerel nesneyi çağırıyoruz Array, uygulanmasını değil ve bu yüzden ekstra.prototype

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

4

Unutmayın, bu davranışın düşük seviyeli temelleri tamamen JS motoruna entegre edilen tür dökümdür.

Dilim yalnızca nesneyi alır (varolan arguments.length özelliği sayesinde) ve bu işlemin ardından yapılan dizi nesnesini döndürür.

String yöntemini bir INT değeriyle tedavi etmeye çalışırsanız test edebileceğiniz aynı mantık:

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

Bu da yukarıdaki ifadeyi açıklıyor.


1

sliceDizilerin sahip olduğu yöntemi kullanır ve onu nesne thisolarak çağırır arguments. Bu , böyle bir yöntem olduğunu arguments.slice()varsaymış argumentsgibi adlandırdığınız anlamına gelir .

Herhangi bir argüman olmadan bir dilim oluşturmak tüm öğeleri alacaktır - böylece elemanları argumentsbir diziye kopyalar .


1

Varsayalım: function.apply(thisArg, argArray )

Apply yöntemi, buna bağlanacak nesneyi ve isteğe bağlı bir argüman dizisini ileten bir işlevi çağırır.

Slice () yöntemi bir dizinin bir bölümünü seçer ve yeni diziyi döndürür.

Yani Array.prototype.slice.apply(arguments, [0])dizi çağırdığınızda argümanlarda dilim yöntemi çağrılır (bağlanır).


1

Belki biraz geç, ama tüm bu karışıklığın cevabı, JS'de miras için call () yönteminin kullanılmasıdır. Örneğin bunu Python veya PHP ile karşılaştırırsak, çağrı sırasıyla super () olarak kullanılır. içinde () veya parent :: _ construct ().

Bu, kullanımını netleştiren kullanımına bir örnektir:

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

  this.subject = subject;
}

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


0

.slice () normal olarak çağrıldığında, bu bir Dizi'dir ve daha sonra sadece bu Dizi üzerinde yinelenir ve çalışmasını yapar.

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

Bunu kendime hatırlatmak için yazıyorum ...

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

Ya da çoğu şeyi bir diziye dönüştürmek için bu kullanışlı $ A işlevini kullanın .

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);
}

örnek kullanım ...

function test() {
    $A( arguments ).forEach( function(arg) {
        console.log("Argument: " + arg);
    });
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.