Tạo phạm vi trong JavaScript - cú pháp lạ


129

Tôi đã chạy vào đoạn mã sau trong danh sách gửi thư thảo luận es:

Array.apply(null, { length: 5 }).map(Number.call, Number);

Điều này tạo ra

[0, 1, 2, 3, 4]

Tại sao đây là kết quả của mã? Chuyện gì đang xảy ra ở đây vậy?


2
IMO Array.apply(null, Array(30)).map(Number.call, Number)dễ đọc hơn vì nó tránh giả vờ rằng một đối tượng đơn giản là một Mảng.
fncomp

10
@fncomp Vui lòng không sử dụng hoặc để thực sự tạo một phạm vi. Không chỉ chậm hơn so với cách tiếp cận đơn giản - nó gần như không dễ hiểu. Thật khó để hiểu cú pháp (tốt, thực sự là API chứ không phải cú pháp) ở đây khiến cho đây là một câu hỏi thú vị nhưng mã sản xuất khủng khiếp IMO.
Benjamin Gruenbaum

Có, không đề xuất bất cứ ai sử dụng nó, nhưng nghĩ rằng nó vẫn dễ đọc hơn, liên quan đến phiên bản nghĩa đen của đối tượng.
fncomp

1
Tôi không chắc tại sao mọi người muốn làm điều này. Lượng thời gian cần thiết để tạo ra mảng theo cách này có thể đã được thực hiện theo cách hơi kém gợi cảm nhưng nhanh hơn nhiều: jsperf.com/basic-vs-ext tối cao
Eric Hodonsky

Câu trả lời:


263

Hiểu "hack" này đòi hỏi phải hiểu một số điều:

  1. Tại sao chúng ta không làm Array(5).map(...)
  2. Cách Function.prototype.applyxử lý đối số
  3. Cách Arrayxử lý nhiều đối số
  4. Cách Numberhàm xử lý các đối số
  5. Có gì Function.prototype.callkhông

Chúng là những chủ đề khá tiên tiến trong javascript, vì vậy điều này sẽ dài hơn là khá dài. Chúng ta sẽ bắt đầu từ đầu. Thắt dây an toàn!

1. Tại sao không chỉ Array(5).map?

Thật là một mảng, thực sự? Một đối tượng thông thường, chứa các khóa nguyên, ánh xạ tới các giá trị. Nó có các tính năng đặc biệt khác, ví dụ như lengthbiến ma thuật , nhưng ở cốt lõi, nó là một key => valuebản đồ thông thường , giống như bất kỳ đối tượng nào khác. Chúng ta hãy chơi với mảng một chút, phải không?

var arr = ['a', 'b', 'c'];
arr.hasOwnProperty(0); //true
arr[0]; //'a'
Object.keys(arr); //['0', '1', '2']
arr.length; //3, implies arr[3] === undefined

//we expand the array by 1 item
arr.length = 4;
arr[3]; //undefined
arr.hasOwnProperty(3); //false
Object.keys(arr); //['0', '1', '2']

Chúng ta có sự khác biệt vốn có giữa số lượng các mục trong mảng arr.lengthvà số lượng key=>valueánh xạ của mảng có thể khác nhau arr.length.

Mở rộng mảng thông qua arr.length không tạo ra bất kỳ key=>valueánh xạ mới nào , do đó, không phải là mảng có các giá trị không xác định, nó không có các khóa này . Và điều gì xảy ra khi bạn cố gắng truy cập vào một tài sản không tồn tại? Bạn lấyundefined .

Bây giờ chúng ta có thể ngẩng đầu lên một chút và xem tại sao các chức năng như arr.mapkhông đi qua các tính chất này. Nếu arr[3]chỉ đơn thuần là không xác định và khóa tồn tại, tất cả các hàm mảng này sẽ vượt qua nó giống như bất kỳ giá trị nào khác:

//just to remind you
arr; //['a', 'b', 'c', undefined];
arr.length; //4
arr[4] = 'e';

arr; //['a', 'b', 'c', undefined, 'e'];
arr.length; //5
Object.keys(arr); //['0', '1', '2', '4']

arr.map(function (item) { return item.toUpperCase() });
//["A", "B", "C", undefined, "E"]

Tôi cố tình sử dụng một cuộc gọi phương thức để chứng minh thêm rằng điểm chính của khóa không bao giờ có: Gọi undefined.toUpperCasesẽ gây ra lỗi, nhưng không được. Để chứng minh rằng :

arr[5] = undefined;
arr; //["a", "b", "c", undefined, "e", undefined]
arr.hasOwnProperty(5); //true
arr.map(function (item) { return item.toUpperCase() });
//TypeError: Cannot call method 'toUpperCase' of undefined

Và bây giờ chúng ta đến quan điểm của tôi: Làm thế nào Array(N)để mọi thứ. Mục 15.4.2.2 mô tả quy trình. Có một loạt mumbo jumbo mà chúng tôi không quan tâm, nhưng nếu bạn có thể đọc được giữa các dòng (hoặc bạn có thể tin tưởng tôi về điều này, nhưng không), về cơ bản nó sẽ hiểu rõ điều này:

function Array(len) {
    var ret = [];
    ret.length = len;
    return ret;
}

(hoạt động theo giả định (được kiểm tra trong thông số thực tế) lenlà một uint32 hợp lệ và không chỉ là bất kỳ số lượng giá trị nào)

Vì vậy, bây giờ bạn có thể thấy lý do tại sao hoạt động Array(5).map(...)không hiệu quả - chúng tôi không xác định lencác mục trên mảng, chúng tôi không tạo key => valueánh xạ, chúng tôi chỉ đơn giản là thay đổi thuộc lengthtính.

Bây giờ chúng ta đã có được điều đó, hãy nhìn vào điều kỳ diệu thứ hai:

2. Cách Function.prototype.applylàm việc

Những gì applyvề cơ bản là lấy một mảng và hủy bỏ nó như là một đối số của hàm gọi. Điều đó có nghĩa là những điều sau đây khá giống nhau:

function foo (a, b, c) {
    return a + b + c;
}
foo(0, 1, 2); //3
foo.apply(null, [0, 1, 2]); //3

Bây giờ, chúng ta có thể dễ dàng quá trình xem cách applyhoạt động bằng cách đăng nhập argumentsbiến đặc biệt:

function log () {
    console.log(arguments);
}

log.apply(null, ['mary', 'had', 'a', 'little', 'lamb']);
 //["mary", "had", "a", "little", "lamb"]

//arguments is a pseudo-array itself, so we can use it as well
(function () {
    log.apply(null, arguments);
})('mary', 'had', 'a', 'little', 'lamb');
 //["mary", "had", "a", "little", "lamb"]

//a NodeList, like the one returned from DOM methods, is also a pseudo-array
log.apply(null, document.getElementsByTagName('script'));
 //[script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script]

//carefully look at the following two
log.apply(null, Array(5));
//[undefined, undefined, undefined, undefined, undefined]
//note that the above are not undefined keys - but the value undefined itself!

log.apply(null, {length : 5});
//[undefined, undefined, undefined, undefined, undefined]

Thật dễ dàng để chứng minh yêu cầu của tôi trong ví dụ thứ hai đến cuối cùng:

function ahaExclamationMark () {
    console.log(arguments.length);
    console.log(arguments.hasOwnProperty(0));
}

ahaExclamationMark.apply(null, Array(2)); //2, true

(vâng, ý định chơi chữ). Ánh key => valuexạ có thể không tồn tại trong mảng mà chúng ta đã chuyển qua apply, nhưng nó chắc chắn tồn tại trong argumentsbiến. Đó là lý do tương tự ví dụ cuối cùng hoạt động: Các khóa không tồn tại trên đối tượng chúng ta vượt qua, nhưng chúng tồn tại trongarguments .

Tại sao vậy? Hãy xem Mục 15.3.4.3 , nơi Function.prototype.applyđược định nghĩa. Hầu hết những điều chúng tôi không quan tâm, nhưng đây là phần thú vị:

  1. Đặt len ​​là kết quả của việc gọi phương thức bên trong [[Get]] của argArray với đối số "length".

Về cơ bản có nghĩa là : argArray.length. Thông số kỹ thuật sau đó tiến hành thực hiện một forvòng lặp đơn giản trên lengthcác mục, tạo ra listcác giá trị tương ứng ( listlà một số voodoo nội bộ, nhưng về cơ bản nó là một mảng). Về mặt mã rất, rất lỏng lẻo:

Function.prototype.apply = function (thisArg, argArray) {
    var len = argArray.length,
        argList = [];

    for (var i = 0; i < len; i += 1) {
        argList[i] = argArray[i];
    }

    //yeah...
    superMagicalFunctionInvocation(this, thisArg, argList);
};

Vì vậy, tất cả những gì chúng ta cần bắt chước argArraytrong trường hợp này là một đối tượng có thuộc lengthtính. Và bây giờ chúng ta có thể thấy lý do tại sao các giá trị không được xác định, nhưng các khóa không, trên arguments: Chúng tôi tạo rakey=>value ánh xạ.

Phew, vì vậy điều này có thể không ngắn hơn phần trước. Nhưng sẽ có bánh khi chúng ta hoàn thành, vì vậy hãy kiên nhẫn! Tuy nhiên, sau phần sau (sẽ ngắn, tôi hứa) chúng ta có thể bắt đầu mổ xẻ biểu thức. Trong trường hợp bạn quên, câu hỏi là làm thế nào để làm việc sau đây:

Array.apply(null, { length: 5 }).map(Number.call, Number);

3. Cách Arrayxử lý nhiều đối số

Vì thế! Chúng tôi đã thấy những gì xảy ra khi bạn truyền một lengthđối số cho Array, nhưng trong biểu thức, chúng tôi chuyển một số thứ dưới dạng đối số ( undefinedchính xác là một mảng 5 ). Mục 15.4.2.1 cho chúng ta biết phải làm gì. Đoạn cuối là tất cả những gì quan trọng đối với chúng tôi, và nó được diễn đạt rất kỳ quặc, nhưng nó có nghĩa là:

function Array () {
    var ret = [];
    ret.length = arguments.length;

    for (var i = 0; i < arguments.length; i += 1) {
        ret[i] = arguments[i];
    }

    return ret;
}

Array(0, 1, 2); //[0, 1, 2]
Array.apply(null, [0, 1, 2]); //[0, 1, 2]
Array.apply(null, Array(2)); //[undefined, undefined]
Array.apply(null, {length:2}); //[undefined, undefined]

Tada! Chúng tôi nhận được một mảng của một số giá trị không xác định và chúng tôi trả về một mảng của các giá trị không xác định này.

Phần đầu tiên của biểu thức

Cuối cùng, chúng ta có thể giải mã như sau:

Array.apply(null, { length: 5 })

Chúng tôi thấy rằng nó trả về một mảng chứa 5 giá trị không xác định, với tất cả các khóa tồn tại.

Bây giờ, đến phần thứ hai của biểu thức:

[undefined, undefined, undefined, undefined, undefined].map(Number.call, Number)

Đây sẽ là phần dễ dàng hơn, không phức tạp, vì nó không phụ thuộc quá nhiều vào các bản hack tối nghĩa.

4. Làm thế nào Number xử lý đầu vào

Làm Number(something)( phần 15.7.1 ) chuyển đổi somethingthành một số, và đó là tất cả. Làm thế nào nó là một chút phức tạp, đặc biệt là trong các trường hợp của chuỗi, nhưng hoạt động được xác định trong phần 9.3 trong trường hợp bạn quan tâm.

5. Trò chơi của Function.prototype.call

callapplyanh trai, được định nghĩa trong phần 15.3.4.4 . Thay vì lấy một loạt các đối số, nó chỉ lấy các đối số mà nó nhận được và chuyển chúng về phía trước.

Mọi thứ trở nên thú vị khi bạn kết hợp nhiều hơn một callvới nhau, tạo ra sự kỳ lạ lên đến 11:

function log () {
    console.log(this, arguments);
}
log.call.call(log, {a:4}, {a:5});
//{a:4}, [{a:5}]
//^---^  ^-----^
// this   arguments

Điều này khá xứng đáng cho đến khi bạn nắm bắt được những gì đang diễn ra. log.callchỉ là một hàm, tương đương với bất kỳ callphương thức nào của hàm khác , và như vậy, cũng có một callphương thức:

log.call === log.call.call; //true
log.call === Function.call; //true

calllàm gì? Nó chấp nhận một thisArgvà một loạt các đối số và gọi hàm cha của nó. Chúng tôi có thể xác định nó thông qua apply (một lần nữa, mã rất lỏng lẻo, sẽ không hoạt động):

Function.prototype.call = function (thisArg) {
    var args = arguments.slice(1); //I wish that'd work
    return this.apply(thisArg, args);
};

Hãy theo dõi làm thế nào điều này đi xuống:

log.call.call(log, {a:4}, {a:5});
  this = log.call
  thisArg = log
  args = [{a:4}, {a:5}]

  log.call.apply(log, [{a:4}, {a:5}])

    log.call({a:4}, {a:5})
      this = log
      thisArg = {a:4}
      args = [{a:5}]

      log.apply({a:4}, [{a:5}])

Phần sau, hoặc .maptất cả

Nó chưa kết thúc. Hãy xem điều gì xảy ra khi bạn cung cấp một hàm cho hầu hết các phương thức mảng:

function log () {
    console.log(this, arguments);
}

var arr = ['a', 'b', 'c'];
arr.forEach(log);
//window, ['a', 0, ['a', 'b', 'c']]
//window, ['b', 1, ['a', 'b', 'c']]
//window, ['c', 2, ['a', 'b', 'c']]
//^----^  ^-----------------------^
// this         arguments

Nếu chúng ta không tự cung cấp một thisđối số, nó sẽ mặc định window. Hãy lưu ý thứ tự mà các đối số được cung cấp cho cuộc gọi lại của chúng tôi và hãy làm cho nó trở nên kỳ lạ đến tận 11 lần nữa:

arr.forEach(log.call, log);
//'a', [0, ['a', 'b', 'c']]
//'b', [1, ['a', 'b', 'c']]
//'b', [2, ['a', 'b', 'c']]
// ^    ^

Whoa whoa whoa ... hãy sao lưu một chút. Những gì đang xảy ra ở đây? Chúng ta có thể thấy trong phần 15.4.4.18 , nơi forEachđược định nghĩa, khá nhiều điều sau đây xảy ra:

var callback = log.call,
    thisArg = log;

for (var i = 0; i < arr.length; i += 1) {
    callback.call(thisArg, arr[i], i, arr);
}

Vì vậy, chúng tôi nhận được điều này:

log.call.call(log, arr[i], i, arr);
//After one `.call`, it cascades to:
log.call(arr[i], i, arr);
//Further cascading to:
log(i, arr);

Bây giờ chúng ta có thể thấy cách .map(Number.call, Number)hoạt động:

Number.call.call(Number, arr[i], i, arr);
Number.call(arr[i], i, arr);
Number(i, arr);

Trả về phép biến đổi i, chỉ mục hiện tại thành một số.

Tóm lại là,

Cách diễn đạt

Array.apply(null, { length: 5 }).map(Number.call, Number);

Hoạt động trong hai phần:

var arr = Array.apply(null, { length: 5 }); //1
arr.map(Number.call, Number); //2

Phần đầu tiên tạo ra một mảng gồm 5 mục không xác định. Cái thứ hai đi qua mảng đó và lấy các chỉ số của nó, dẫn đến một mảng các chỉ số phần tử:

[0, 1, 2, 3, 4]

@Zirak Xin hãy giúp tôi hiểu những điều sau đây ahaExclamationMark.apply(null, Array(2)); //2, true. Tại sao nó trở lại 2truetương ứng? Không phải bạn chỉ truyền một đối số tức là Array(2)ở đây sao?
Geek

4
@Geek Chúng tôi chỉ truyền một đối số cho apply, nhưng đối số đó được "tách" thành hai đối số được truyền cho hàm. Bạn có thể thấy điều đó dễ dàng hơn trong các applyví dụ đầu tiên . Đầu tiên console.logsau đó cho thấy rằng thực sự, chúng tôi đã nhận được hai đối số (hai mục mảng) và lần thứ hai console.logcho thấy mảng có key=>valueánh xạ trong khe thứ nhất (như được giải thích trong phần 1 của câu trả lời).
Zirak

4
Do (một số) yêu cầu , giờ đây bạn có thể thưởng thức phiên bản âm thanh: dl.dropboxusercontent.com/u/24522528/SO-answer.mp3
Zirak

1
Lưu ý rằng việc truyền NodeList, là một đối tượng máy chủ, cho một phương thức gốc như trong log.apply(null, document.getElementsByTagName('script'));là không bắt buộc để hoạt động và không hoạt động trong một số trình duyệt và [].slice.call(NodeList)để biến NodeList thành một mảng cũng sẽ không hoạt động trong đó.
RobG

2
Một điều chỉnh: thischỉ mặc định Windowở chế độ không nghiêm ngặt.
ComFalet

21

Tuyên bố miễn trừ trách nhiệm : Đây là một mô tả rất chính thức về đoạn mã trên - đây là cách tôi biết cách giải thích nó. Để có câu trả lời đơn giản hơn - hãy xem câu trả lời tuyệt vời của Zirak ở trên. Đây là một đặc điểm kỹ thuật sâu hơn trong khuôn mặt của bạn và ít "aha".


Một số điều đang xảy ra ở đây. Hãy phá vỡ nó một chút.

var arr = Array.apply(null, { length: 5 }); // Create an array of 5 `undefined` values

arr.map(Number.call, Number); // Calculate and return a number based on the index passed

Trong dòng đầu tiên, hàm tạo mảng được gọi là hàm với Function.prototype.apply.

  • Các thisgiá trị nullđó không quan trọng cho các nhà xây dựng Array ( thiscũng giống thisnhư trong bối cảnh theo 15.3.4.3.2.a.
  • Sau đó, new Arrayđược gọi là được truyền một đối tượng có thuộc lengthtính - điều đó làm cho đối tượng đó là một mảng giống như tất cả những gì nó quan trọng .applyvì mệnh đề sau trong .apply:
    • Đặt len ​​là kết quả của việc gọi phương thức bên trong [[Get]] của argArray với đối số "length".
  • Như vậy, .applylà qua các đối số từ 0 đến .length, vì gọi [[Get]]về { length: 5 }với các giá trị từ 0 đến 4 sản lượng undefinedcác nhà xây dựng mảng được gọi với lăm đối số có giá trị làundefined (nhận được một tài sản không khai báo của một đối tượng).
  • Hàm tạo mảng được gọi với 0, 2 hoặc nhiều đối số . Thuộc tính độ dài của mảng được xây dựng mới được đặt thành số lượng đối số theo đặc tả và các giá trị cho cùng một giá trị.
  • Do đó var arr = Array.apply(null, { length: 5 });tạo ra một danh sách năm giá trị không xác định.

Lưu ý : Lưu ý sự khác biệt ở đây giữa Array.apply(0,{length: 5})Array(5), lần đầu tiên tạo ra năm lần loại giá trị nguyên thủy undefinedvà lần sau tạo ra một mảng trống có độ dài 5. Cụ thể, .maphành vi của (8.b) và cụ thể[[HasProperty] .

Vì vậy, mã ở trên trong một đặc tả tuân thủ giống như:

var arr = [undefined, undefined, undefined, undefined, undefined];
arr.map(Number.call, Number); // Calculate and return a number based on the index passed

Bây giờ đến phần thứ hai.

  • Array.prototype.mapgọi hàm gọi lại (trong trường hợp này Number.call) trên mỗi phần tử của mảng và sử dụng thisgiá trị được chỉ định (trong trường hợp này là đặt thisgiá trị thành `Number).
  • Tham số thứ hai của cuộc gọi lại trong bản đồ (trong trường hợp này Number.call) là chỉ mục và đầu tiên là giá trị này.
  • Điều này có nghĩa Numberlà được gọi với thisas undefined(giá trị mảng) và chỉ mục là tham số. Vì vậy, về cơ bản giống như ánh xạ từng undefinedchỉ mục mảng của nó (vì việc gọi Numberthực hiện chuyển đổi loại, trong trường hợp này từ số này sang số không thay đổi chỉ mục).

Do đó, đoạn mã trên lấy năm giá trị không xác định và ánh xạ từng giá trị vào chỉ mục của nó trong mảng.

Đó là lý do tại sao chúng tôi nhận được kết quả cho mã của chúng tôi.


1
Đối với tài liệu: Đặc điểm kỹ thuật về cách thức hoạt động của bản đồ: es5.github.io/#x15.4.4.19 , Mozilla có một tập lệnh mẫu hoạt động theo đặc tả đó tại developer.mozilla.org/en-US/docs/Web/JavaScript/ Tham khảo / Tập
Patrick Evans

1
Nhưng tại sao nó chỉ hoạt động với Array.apply(null, { length: 2 })và không Array.apply(null, [2])gọi hàm Arraytạo đó 2là giá trị độ dài? fiddle
Andreas

@Andreas Array.apply(null,[2])giống như Array(2)tạo ra một mảng trống có độ dài 2 và không phải là một mảng chứa giá trị nguyên thủy undefinedhai lần. Xem bản chỉnh sửa gần đây nhất của tôi trong ghi chú sau phần đầu tiên, cho tôi biết nếu nó đủ rõ ràng và nếu không tôi sẽ làm rõ về điều đó.
Benjamin Gruenbaum

Tôi đã không hiểu cách nó hoạt động trong lần chạy đầu tiên ... Sau lần đọc thứ hai, nó có ý nghĩa. {length: 2}giả mạo một mảng với hai phần tử mà hàm Arraytạo sẽ chèn vào mảng vừa tạo. Vì không có mảng thực sự truy cập vào các phần tử không hiện diện undefinedmà sau đó được chèn vào. Thủ thuật hay :)
Andreas

5

Như bạn đã nói, phần đầu tiên:

var arr = Array.apply(null, { length: 5 }); 

tạo ra một mảng gồm 5 undefinedgiá trị.

Phần thứ hai đang gọi maphàm của mảng có 2 đối số và trả về một mảng mới có cùng kích thước.

Đối số đầu tiên mapthực sự là một hàm để áp dụng cho từng phần tử trong mảng, nó được dự kiến ​​là một hàm có 3 đối số và trả về một giá trị. Ví dụ:

function foo(a,b,c){
    ...
    return ...
}

nếu chúng ta truyền hàm foo làm đối số đầu tiên, nó sẽ được gọi cho mỗi phần tử với

  • a là giá trị của phần tử lặp hiện tại
  • b là chỉ số của phần tử lặp hiện tại
  • c là toàn bộ mảng ban đầu

Đối số thứ hai mapsẽ được truyền cho hàm mà bạn truyền làm đối số thứ nhất. Nhưng nó sẽ không phải là a, b, hay c trong trường hợp foo, nó sẽ là this.

Hai ví dụ:

function bar(a,b,c){
    return this
}
var arr2 = [3,4,5]
var newArr2 = arr2.map(bar, 9);
//newArr2 is equal to [9,9,9]

function baz(a,b,c){
    return b
}
var newArr3 = arr2.map(baz,9);
//newArr3 is equal to [0,1,2]

và một số khác chỉ để làm cho nó rõ ràng hơn:

function qux(a,b,c){
    return a
}
var newArr4 = arr2.map(qux,9);
//newArr4 is equal to [3,4,5]

Vậy còn Number.call thì sao?

Number.call là một hàm có 2 đối số và cố phân tích đối số thứ hai thành một số (tôi không chắc nó làm gì với đối số thứ nhất).

Vì đối số thứ hai mapđang truyền là chỉ mục, nên giá trị sẽ được đặt trong mảng mới tại chỉ mục đó bằng với chỉ mục. Cũng giống như hàm baztrong ví dụ trên. Number.callsẽ cố phân tích chỉ mục - nó sẽ tự nhiên trả về cùng một giá trị.

Đối số thứ hai bạn truyền cho maphàm trong mã của bạn không thực sự có ảnh hưởng đến kết quả. Xin hãy sửa tôi nếu tôi sai.


1
Number.callkhông có chức năng đặc biệt phân tích đối số thành số. Nó chỉ là === Function.prototype.call. Chỉ số thứ hai, chức năng mà được thông qua như là this-giá trị đến call, có liên quan - .map(eval.call, Number), .map(String.call, Number).map(Function.prototype.call, Number)tất cả đều tương đương.
Bergi

0

Một mảng chỉ đơn giản là một đối tượng bao gồm trường 'độ dài' và một số phương thức (ví dụ: đẩy). Vì vậy, var arr = { length: 5}mảng trong về cơ bản giống như một mảng trong đó các trường 0..4 có giá trị mặc định không xác định (nghĩa là arr[0] === undefinedmang lại giá trị đúng).
Đối với phần thứ hai, ánh xạ, như tên gọi của nó, ánh xạ từ một mảng sang một mảng mới. Nó làm như vậy bằng cách duyệt qua mảng ban đầu và gọi hàm ánh xạ trên mỗi mục.

Tất cả những gì còn lại là để thuyết phục bạn rằng kết quả của chức năng ánh xạ là chỉ mục. Thủ thuật là sử dụng phương thức có tên 'gọi' (*) để gọi một hàm với ngoại lệ nhỏ là tham số đầu tiên được đặt thành bối cảnh 'this' và thứ hai trở thành tham số thứ nhất (v.v.). Thật trùng hợp, khi hàm ánh xạ được gọi, tham số thứ hai là chỉ mục.

Cuối cùng nhưng không kém phần quan trọng, phương thức được gọi là Số "Lớp" và như chúng ta biết trong JS, "Lớp" chỉ đơn giản là một hàm và phương thức này (Số) mong muốn tham số đầu tiên là giá trị.

(*) được tìm thấy trong nguyên mẫu của Hàm (và Số là một hàm).

MASHAL


1
Có một sự khác biệt rất lớn giữa [undefined, undefined, undefined, …]new Array(n)hoặc {length: n}- những cái sau còn thưa thớt , tức là chúng không có yếu tố. Điều này rất phù hợp mapvà đó là lý do tại sao số lẻ Array.applyđược sử dụng.
Bergi
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.