Thứ năm, 12/12/2019 | 00:00 GMT+7

Cách sử dụng .every () và .some () để điều khiển mảng JavaScript

ES5 và ES6 đã mang lại nhiều thay đổi cho JavaScript, bao gồm các cách làm việc tốt hơn với tập hợp dữ liệu được biểu thị dưới dạng mảng. Trong khi ngôn ngữ này đã cải thiện đáng kể khả năng hỗ trợ thao tác dữ liệu khai báo trên mảng và nhiều trình duyệt hiện đại hỗ trợ chúng, chỉ một tập hợp con giới hạn các khả năng của mảng thường được sử dụng. Đặc biệt, các hàm .every().some() có thể cải thiện cách các nhà phát triển thao tác với các mảng và có khả năng mang lại cho họ hiệu suất tăng.

Bài viết này sẽ cho biết các hàm mảng JavaScript nổi bật là .map() , .filter().reduce() , sau đó sẽ đi qua các ví dụ về các trường hợp trong đó .every().some() sẽ tiết kiệm sức mạnh tính toán qua các giải pháp nổi bật hơn.

.map() , .filter().reduce() : Các hàm mảng JavaScript nổi bật

Việc sử dụng .map() , .filter().reduce() rất phổ biến. Với mục đích của bài viết này, ta hãy gọi ba chức năng này là MFR .

Các hàm này đã trở nên nổi bật hơn so với các hàm khác để thao tác dữ liệu mảng trong JavaScript. Ví dụ: Google Xu hướng hiển thị các tìm kiếm tích cực hơn cho MFR so với việc lấy mẫu các chức năng khác:

So sánh xu hướng của Google về tần suất tìm kiếm `.foreach`,` .filter`, `.map`,` .reduce` và `.some`. `.filter`,` .map` và `.reduce` là cao nhất.

Google Tìm kiếm cũng cho thấy rằng các truy vấn tìm kiếm cho MFR tạo ra nhiều kết quả hơn.

Biểu đồ thanh so sánh `.map`,` .filter`, `.reduce`,` .every` và `.some` về số lượng tìm kiếm trên Google. `.map`,` .filter`, và `.reduce` là cao nhất với một lề lớn.

Kết quả tìm kiếm cho .filter() nhiều hơn 74M. Con số này cao hơn 99,97% so với kết quả cho .every() và cao hơn 99,98% so với kết quả cho. some() . Điều này nghĩa là mọi người đang nói, viết và dạy MFR nhiều hơn, điều này cho thấy nhiều người sử dụng hơn theo thời gian.

Lợi ích của .every().some()

Trong JavaScript, nó thường được dạy để giới hạn các phép tính trên mảng ở các phép toán lặp (.forEach) và chuyển đổi (.map, .filter, .reduce - hay còn gọi là MFR ). Điều này có thể dẫn đến tính toán có thể được thực hiện với .some() hoặc .every() được buộc vào MFR, đặc biệt là một chuỗi các .filter() theo sau là một. reduce() .

Hầu hết mọi cách sử dụng MFR mà bạn cần thoát ra sớm và không lược qua toàn bộ bộ sưu tập là thành phần hàng đầu cho .some() hoặc .every() , vì chúng đều gây ngắn mạch và thoát khỏi lặp mảng càng sớm càng tốt, thay vào đó chạy cho đến cuối và có khả năng lãng phí tài nguyên máy tính.

Trong thời điểm ta đang xây dựng các ứng dụng phức tạp hơn và gửi nhiều byte hơn cho khách hàng, dẫn đến chi phí đáng kể để phân tích cú pháp JavaScript (xem TTI , bất cứ điều gì ta có thể làm để có ba lần lặp (do ngắn mạch) thay vì ba mươi đều được hoan nghênh .

Array.prototype.every()

Phương thức every() cho phép ta cài đặt nếu mọi phần tử trong một mảng đáp ứng một yêu cầu nhất định. Nó ngừng đánh giá mảng (ngắn mạch) lần đầu tiên nó tìm thấy một phần tử không thỏa mãn yêu cầu đã cho.

Array.prototype.some()

Phương thức some() cho phép ta cài đặt nếu một số (ít nhất một) phần tử trong một mảng đáp ứng một yêu cầu nhất định. Nó ngừng đánh giá mảng (ngắn mạch) lần đầu tiên nó tìm thấy một phần tử thỏa mãn yêu cầu đã cho.

Một ví dụ, hai tình huống

Giả sử bạn được yêu cầu viết một hàm add() đơn giản để thêm một loạt các số nguyên. Hàm sẽ được cung cấp cho bất kỳ số nguyên nào được thêm vào và nó sẽ trả về tổng của chúng sau khi tính toán phép cộng.

Sau đây là hai cách để hoàn thành nhiệm vụ này.

Kịch bản một

Nếu hàm add () được cung cấp đầu vào hỗn hợp (ví dụ: số nguyên, số float, không xác định, chuỗi, v.v.), thì hàm này sẽ chỉ tiếp tục với các số nguyên và trả về tổng từ việc thêm chúng.
Ta có thể triển khai hàm add () theo cách này:

const add = (...entries) => {   return entries     .filter(Number.isInteger)     .reduce((sum, int) => {       return sum + int;     }, 0); }; 

Vì kịch bản này mong đợi hàm add() tính tổng từ bất kỳ số nguyên nào tồn tại trong các đầu vào đã cho, người ta có thể triển khai hàm như ta có trong khối mã trước đó. Trước tiên, ta sử dụng .filter() để chỉ extract các số nguyên và sau đó tính tổng với .reduce() . Tuy nhiên, hoạt động .filter() sẽ lặp lại trên toàn bộ mảng ngay cả khi không có số nguyên nào trong đó, gây lãng phí tài nguyên tính toán.

Người ta cũng có thể tranh luận rằng ta có thể có được một giải pháp tốt hơn như thế này:

const add = (...entries) => {   return entries.reduce((sum, entry) => {     if(Number.isInteger(entry)) {       return sum + entry;     }     return sum;   }, 0); }; 

Điều này trên add() tốt hơn một chút bởi vì, không giống như lần đầu tiên lặp lại để xác định dữ liệu hợp lệ và sau đó lặp lại để tính toán kết quả dựa trên dữ liệu hợp lệ, bây giờ chỉ có một khối lặp. Tuy nhiên, ta vẫn đang chạy đến cuối mảng ngay cả khi có thể không có số nguyên bên trong nó.

Trước tiên, ta cần một cách để biết liệu các đầu vào đã tập hợp có ít nhất một số nguyên hay không. Vì ta đang cố gắng tìm tổng từ các đầu vào, ta thực sự cần ít nhất hai số nguyên trong đó. Ta sẽ tiến hành giảm các đầu vào thành tổng các số nguyên nếu ta có thể tìm thấy hai hoặc nhiều số nguyên.

Cho phép sử dụng .some() đảm bảo điều kiện này được đáp ứng:

const add = (...entries) => {   let theSum = 0;   if(hasTwoOrMoreInts(entries)){     // there are >= 2 integers, lets sum them     theSum = entries.reduce((sum, entry) => {       if(Number.isInteger(entry)) {         return sum + entry;       }       return sum;     }, 0);   }   return theSum; }; 

Bây giờ ta có một điều kiện ngăn phép tính tổng trừ khi ta chắc chắn có hai hoặc nhiều số nguyên. Ta đang thực hiện việc này với một hasTwoOrMoreInts() mà ta sẽ tạo trong phần sau:

const hasTwoOrMoreInts = (entries) => {   let lastIndex = -1;   let hasMinimumIntsCount = false;    const hasAnInt = entries.some((entry, index) => {     lastIndex = index;     return Number.isInteger(entry);   });    if(hasAnInt === true) {     // we've got one int, is there another?     const hasMoreInts = entries.slice(lastIndex + 1).some(Number.isInteger);     hasMinimumIntsCount = (hasMoreInts === true) && hasAnInt;   }    return hasMinimumIntsCount; }; 

Tình huống hai

Nếu hàm add() có thể nhận đầu vào hỗn hợp (chẳng hạn như số nguyên, số thực, không xác định, chuỗi, v.v.) nhưng chỉ cần tiến hành tính toán tổng các đầu vào đã cho nếu tất cả các đầu vào là số nguyên, thì một phương pháp phổ biến bị ảnh hưởng bởi sự nổi bật của MFR có thể trông như thế này:

const add = (...entries) => {   let theSum = 0;   const nonInts = entries.filter(entry => !Number.isInteger(entry));   if(nonInts.length === 0) {  // are there non-ints?     theSum = entries.reduce((sum, int) => {       return sum + int;     }, 0);   }   return theSum; } 

entries.filter() cố gắng xem có đầu vào không hợp lệ hay không bằng cách lặp lại toàn bộ mảng entries để thu thập mọi đầu vào không phải là số nguyên. Nếu không có đầu vào không hợp lệ ( nonInts.length === 0 ), ta tính tổng với .reduce() . Điều này sẽ có ý nghĩa nếu ta cần tổng số tất cả các đầu vào không hợp lệ hoặc tất cả các đầu vào. Nếu không, ta nên tìm kiếm thông tin đầu vào không hợp lệ đầu tiên và quyết định xem phải làm gì tiếp theo từ đó.

Giống như .some() , .every() sẽ hoạt động trên ít thông tin cần thiết nhất. Trong trường hợp này, một khi nó tìm thấy một đầu vào không phải là một số nguyên, nó sẽ không tìm được gì nữa (bằng cách thoát ra với giá trị trả về là false ) và cho phép ta tiếp tục điều quan trọng tiếp theo.

Hãy xem cách .every() thực hiện điều này cho ta :

const add = (...entries) => {   let theSum = 0;   const areAllInts = entries.every(Number.isInteger);   if(areAllInts === true) {  // are these indeed all ints?     theSum = entries.reduce((sum, int) => {       return sum + int;     }, 0);   }   return theSum; }; 

entries.every() sẽ trả về false ngay khi nó tìm thấy những gì không phải là số nguyên, ta có thể thoát khỏi các thử nghiệm tiếp theo đối với các phần tử không hợp lệ trong entries , giải phóng các tài nguyên có thể cần thiết để mang lại cho user di động trải nghiệm cuộn mượt mà.

Một ví dụ thực tế hơn

Các lập trình viên không thường xuyên viết các hàm add() thường xuyên từ ngày này sang ngày khác, vì vậy hãy xem một ví dụ trong thế giới thực: áp dụng .every().some() vào xác thực biểu mẫu HTML giả định.

Ta chỉ muốn gửi biểu mẫu sau khi tất cả dữ liệu bắt buộc đã được thu thập và xác thực từ biểu mẫu. Ta đều muốn ít nhất một trong các dữ liệu tùy chọn được điền vào:

const requiredFields = Array.of(   isFirstNameValid(),   isLastNameValid(),   isEmailValid(),   isAboutMeValid() );  const optionalFields = Array.of(   isTwitterValueValid(),   isFacebookValue(),   isGoogleplusValueValue() );  const isValid = (inputStatus) => inputStatus === true;  if(requiredFields.every(isValid) && optionalFields.some(isValid)) {   // all required fields are valid   // and at least one social media field is valid   // lets proceed to submit the form now } else {     // lets tell the user we are serious here     // this will happen really fast since we are short-circuiting      // with .some and .every above } 

Như đã thấy ở trên, tất cả các hàm trong Array.of() đang thực hiện xác thực cho các trường biểu mẫu cụ thể, sau đó trả về true hoặc false . Vì Array.of() là một nhà máy để xây dựng mảng từ các tham số mà nó được cung cấp (các hàm xác thực trong trường hợp của ta ), điều này nghĩa là các trường optionalFields cuối cùng có thể trông giống như [true, false, false] . Bằng cách này, nếu bất kỳ giá trị nào trong Trường requiredFields là sai hoặc nếu không có Trường nào trong Trường optionalFields là đúng, ta sẽ không gửi biểu mẫu. Việc gửi biểu mẫu được quyết định với con đường thực hiện ngắn nhất.

Kết luận

Cuộc thăm dò này đã giúp tiết lộ MFR đã trở nên nổi bật như thế nào. Nó cũng làm sáng tỏ tại sao .every().some() có thể cung cấp cho bạn các chiến lược hiệu quả hơn để thao tác các mảng.

Dưới đây là một số tình huống có thể xảy ra để quảng bá hoặc áp dụng. some() hoặc .every() qua MFR trong cơ sở mã Javascript của bạn:

  • Một .filter() ngay sau đó là .forEach() , .map() , .reduce() hoặc bất kỳ tổ hợp chuỗi nào liên quan đến chúng.
  • Một .filter() ngay sau đó là một điều kiện kiểm tra kết quả của lệnh gọi và trong đó nội dung của điều kiện chứa .forEach() , .map() , .reduce() hoặc bất kỳ kết hợp chuỗi nào liên quan đến chúng, hoạt động trên kết quả từ .filter() .

Đối với những trường hợp này và các trường hợp có liên quan khác, thoát khỏi kiểm tra càng sớm càng tốt sẽ tối ưu hóa tài nguyên của bạn.


Tags:

Các tin liên quan

Cách gói một gói JavaScript Vanilla để sử dụng trong React
2019-12-12
Cách phát triển một trình tải lên tệp tương tác với JavaScript và Canvas
2019-12-12
Cách sử dụng map (), filter () và Reduce () trong JavaScript
2019-12-12
Giải thích về lập trình chức năng JavaScript: Ứng dụng một phần và làm xoăn
2019-12-12
Giới thiệu về Closures và Currying trong JavaScript
2019-12-12
Bắt đầu với các hàm mũi tên ES6 trong JavaScript
2019-12-12
Cách đếm số nguyên âm trong một chuỗi văn bản bằng thuật toán JavaScript
2019-12-12
Cách sử dụng phép gán cấu trúc hủy trong JavaScript
2019-12-12
Giải thích về lập trình chức năng JavaScript: Kết hợp & truyền tải
2019-12-12
Chuyển đổi Mảng sang Chuỗi trong JavaScript
2019-12-05