Giải thích về [] .slice.call trong javascript?


197

Tôi tình cờ tìm thấy lối tắt gọn gàng này để chuyển đổi DOM NodeList thành một mảng thông thường, nhưng tôi phải thừa nhận, tôi hoàn toàn không hiểu cách thức hoạt động của nó:

[].slice.call(document.querySelectorAll('a'), 0)

Vì vậy, nó bắt đầu với một mảng trống [], sau đó sliceđược sử dụng để chuyển đổi kết quả của callmột mảng mới yeah?

Một chút tôi không hiểu là call. Làm thế nào để chuyển đổi document.querySelectorAll('a')từ một NodeList thành một mảng thông thường?


5
Array.prototype.slice.call(document.querySelectorAll('a'));là một cách thích hợp để viết đoạn mã bạn đã viết.
vdegenne

4
BTW, phương pháp ES6 hiện đại (và trực quan dễ hiểu) cho cùng là Array.from. Vì vậy, ví dụ, điều này sẽ làm tương tự: Array.from (document.querySelector ALL ('a'));
rugk

Câu trả lời:


158

Điều đang xảy ra ở đây là bạn gọi slice()như thể đó là một chức năng NodeListsử dụng call(). Điều gì slice()trong trường hợp này là tạo ra một mảng trống, sau đó lặp qua đối tượng mà nó đang chạy (ban đầu là một mảng, bây giờ là a NodeList) và tiếp tục nối các phần tử của đối tượng đó vào mảng trống mà nó tạo ra, cuối cùng được trả về. Đây là một bài viết về điều này .

BIÊN TẬP:

Vì vậy, nó bắt đầu với một mảng trống [], sau đó lát được sử dụng để chuyển đổi kết quả của cuộc gọi sang một mảng mới yeah?

Điều đó không đúng. [].slicetrả về một đối tượng hàm. Một đối tượng hàm có chức năng call()đó gọi hàm gán tham số đầu tiên của call()để this; nói cách khác, làm cho hàm nghĩ rằng nó được gọi từ tham số (được NodeListtrả về bởi document.querySelectorAll('a')) chứ không phải từ một mảng.


59
Cũng lưu ý ở đây rằng mặc dù điều này tương đương về mặt ngữ nghĩa để nói Array.prototype.slice.call(...), nhưng thực tế nó chỉ khởi tạo một đối tượng mảng ( []) để truy cập phương thức lát nguyên mẫu của nó. Đó là một sự lãng phí ngay lập tức. Nói Array.prototype.slice.call(...)thay vào đó là sạch hơn, mặc dù bạn thêm một vài ký tự vào JS nếu bạn đang đếm ...
Ben Zotto

Lưu ý rằng công trình này trong IE 8 và dưới đây chỉ trên các đối tượng Array, vì vậy bạn sẽ không thể clone NodeLists
Livingston Samuel

5
@quixoto []đáng tin cậy hơn vì Arraycó thể bị ghi đè lên thứ khác. Nếu bạn cần sử dụng lại Array#slice, đó là một ý tưởng tốt để lưu trữ nó.
Mathias Bynens

2
Trong trường hợp bất kỳ ai khác đang tìm cách để làm điều này trong IE8, hãy xem câu hỏi này stackoverflow.com/questions/3199588/
gợi

1
Tôi thực sự đã thấy mẫu này xuất hiện trong mã nguồn backbone.js: var array = []; var push = array.push; var slice = array.slice; var splice = array.splice;Anh ta có làm điều này cho vấn đề an toàn @MathiasBynens đề cập không?
owensmartin

125

Trong JavaScript, các phương thức của một đối tượng có thể được liên kết với một đối tượng khác trong thời gian chạy. Nói tóm lại, javascript cho phép một đối tượng "mượn" phương thức của đối tượng khác:

object1 = {
    name: 'Frank',
    greet() {
        alert(`Hello ${this.name}`);
    }
};

object2 = {
    name: 'Andy'
};

// Note that object2 has no greet method,
// but we may "borrow" from object1:

object1.greet.call(object2); // Will show an alert with 'Hello Andy'

Các phương thức callapplyphương thức của các đối tượng hàm (trong JavaScript, các hàm cũng là các đối tượng) cho phép bạn thực hiện điều này. Vì vậy, trong mã của bạn, bạn có thể nói rằng NodeList đang mượn phương thức lát của một mảng. .slice()trả về một mảng khác như kết quả của nó, nó sẽ trở thành mảng "được chuyển đổi" mà sau đó bạn có thể sử dụng.


Giải thích khái niệm trừu tượng cho các chức năng của đối tượng javascript. Bây giờ, bạn có thể áp dụng nó cho callchức năng của Array.prototypeaka [].prototypechính mình.
Sourabh

29

Nó lấy slicehàm từ một Array. Sau đó, nó gọi hàm đó, nhưng sử dụng kết quả document.querySelectorAllthisđối tượng thay vì một mảng thực tế.


19

Đây là một kỹ thuật để chuyển đổi các đối tượng giống như mảng thành các mảng thực.

Một số đối tượng này bao gồm:

  • arguments trong các chức năng
  • NodeList (hãy nhớ nội dung của chúng có thể thay đổi sau khi được tìm nạp! Vì vậy, chuyển đổi chúng thành mảng là một cách để đóng băng chúng)
  • Các bộ sưu tập jQuery, còn gọi là các đối tượng jQuery (một số tài liệu: API , loại , tìm hiểu )

Điều này phục vụ nhiều mục đích, ví dụ các đối tượng được truyền bằng tham chiếu trong khi các mảng được truyền theo giá trị.

Ngoài ra, lưu ý đối số đầu tiên 0có thể được bỏ qua, giải thích kỹ lưỡng ở đây .

Và để hoàn thiện, cũng có jQuery.makeArray () .


15

Làm thế nào mà chuyển đổi document.querySelectorAll('a')từ một NodeList mảng thông thường?

Đây là mã mà chúng tôi có,

[].slice.call(document.querySelectorAll('a'), 0)

Hãy tháo dỡ nó trước,

  []    // Array object
.slice // Accessing the function 'slice' present in the prototype of Array
.call // Accessing the function 'call' present in the prototype of function object(slice)
(document.querySelectorAll('a'),0) 
    // 'call' can have arguments like, (thisArg, arg1,arg2...n). 
   // So here we are passing the 'thisArg' as an array like object,
  // that is a 'nodeList'. It will be served as 'this' object inside of slice function.
 // And finally setting 'start' argument of slice as '0' and leaving the 'end' 
// argument as 'undefined'

Bước: 1 Thực thi callchức năng

  • Bên trong call, ngoài thisArgphần còn lại của các đối số sẽ được thêm vào danh sách đối số.
  • Bây giờ hàm slicesẽ được gọi bằng cách ràng buộc thisgiá trị của nó là thisArg(mảng giống như đối tượng đến từ document.querySelector) và với danh sách đối số. tức là] đối số startcó chứa0

Bước: 2 Thực thi slicechức năng được gọi bên trongcall

  • startsẽ được gán cho một biến s0
  • kể từ khi endundefined, this.lengthsẽ được lưu trữ tronge
  • một mảng trống sẽ được lưu trữ trong một biến a
  • Sau khi thực hiện các cài đặt ở trên, việc lặp lại sau đây sẽ xảy ra

    while(s < e) {
      a.push(this[s]);
      s++;
    }
  • akết quả là mảng đầy sẽ được trả về.

PS Để hiểu rõ hơn về kịch bản của chúng tôi, một số bước cần thiết cho ngữ cảnh của chúng tôi đã bị bỏ qua khỏi thuật toán gốc của cuộc gọilát .


1
Rất tốt đẹp từng bước giải thích. Tuyệt vời! Cảm ơn bạn :)
kittu

1
Giải thích tốt đẹp.
NaveenDA

7
[].slice.call(document.querySelectorAll('.slide'));

1. The querySelectorAll() method returns all elements in the document that matches a specified selector(s). 

2. The call() method calls a function with a given this value and arguments provided individually.

3. The slice() method returns the selected elements in an array, as a new array object.

  so this line return the array of [object HTMLDivElement]. Here is the six div with classname "slide" so array length will be 6.

<div class="slideshow">

  <div class="slide">
    first slider1
  </div>
  <div class="slide">
    first slider2
  </div>
  <div class="slide">
    first slider3
  </div>
  <div class="slide">
    first slider4
  </div>
  <div class="slide">
    first slider5
  </div>
  <div class="slide">
    first slider6
  </div>

</div>

<script type="text/javascript">

  var arraylist = [].slice.call(document.querySelectorAll('.slide'));

  alert(arraylist);

</script>

4

Từ ES6: Đơn giản chỉ cần tạo mảng với Array.from (Element.children) hoặc Array.from ({length: 5})

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.