Mảng đối tượng đệ quy JavaScript

Trong toán học, ngôn ngữ học và nghệ thuật, đệ quy đề cập đến sự xuất hiện của một thứ được xác định theo chính nó. Trong khoa học máy tính, đệ quy đề cập đến một chức năng gọi chính nó. Các hàm đệ quy giải các bài toán phức tạp thông qua phương pháp “chia để trị” (một phương pháp giải các bài toán bao gồm giải các phần nhỏ hơn của cùng một bài toán cho đến khi bạn giải được bài toán ban đầu, lớn hơn. )

Mảng đối tượng đệ quy JavaScript

Đệ quy chắc chắn là một trong những khái niệm lâu đời nhất trong lập trình. Đó là mô hình mà một chức năng gọi chính nó. Kỹ thuật này thường được sử dụng để giải các bài toán yêu cầu chia chúng thành các bài toán con nhỏ hơn. Trong bài viết này, chúng tôi sẽ thảo luận về đệ quy và hiển thị các ví dụ về ứng dụng của nó

Hai trường hợp cơ bản

Để các hàm đệ quy được triển khai chính xác, chúng phải có hai trường hợp, để tránh tràn Stack và tính toán kết thúc chính xác

trường hợp cơ sở

Trường hợp cơ sở (còn được gọi là trường hợp kết thúc) ngăn một hàm gọi chính nó khi đạt đến một điều kiện nhất định

Ví dụ: kiểm tra chức năng sau, in các số đếm ngược từ n đến 0

 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);

đầu ra

// 5
// 4
// 3
// 2
// 1
// 0

Trong đoạn mã trước, trường hợp cơ sở là khi

// 5
// 4
// 3
// 2
// 1
// 0
4 nhỏ hơn
// 5
// 4
// 3
// 2
// 1
// 0
5 vì chúng tôi muốn dừng đếm tại
// 5
// 4
// 3
// 2
// 1
// 0
5. Nếu một số âm được đưa ra làm đầu vào, hàm
// 5
// 4
// 3
// 2
// 1
// 0
7 sẽ không in số đó vì trường hợp cơ bản

trường hợp đệ quy

Trinh bay đơn giản. Chức năng của chúng tôi đang gọi chính nó. Trong ví dụ về

// 5
// 4
// 3
// 2
// 1
// 0
7,
// 5
// 4
// 3
// 2
// 1
// 0
9;

________số 8_______

Ngăn xếp cuộc gọi và đệ quy

Khi một hàm gọi chính nó theo cách đệ quy, nó sẽ được thêm vào ngăn xếp cuộc gọi. Ngăn xếp là cấu trúc LIFO (Last In, First Out), nghĩa là mục cuối cùng được thêm vào ngăn xếp là mục đầu tiên sẽ bị xóa khỏi ngăn xếp sau đó

Hãy xem cách Stack xử lý hàm đệ quy

// 5
// 4
// 3
// 2
// 1
// 0
7 của chúng ta

 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);

Mảng đối tượng đệ quy JavaScript

Đây là lý do tại sao một trường hợp cơ sở là rất quan trọng. không có trường hợp cơ sở, vòng lặp vô hạn sẽ gây tràn ngăn xếp

Phát lại phiên dành cho nhà phát triển

Khám phá sự thất vọng, hiểu lỗi và khắc phục sự cố chậm hơn bao giờ hết với OpenReplay — bộ phát lại phiên mã nguồn mở dành cho nhà phát triển. Nó có thể tự lưu trữ trong vài phút, cho phép bạn kiểm soát hoàn toàn dữ liệu khách hàng của mình

Mảng đối tượng đệ quy JavaScript
Chúc mừng gỡ lỗi. Hãy thử sử dụng OpenReplay ngay hôm nay

Áp dụng đệ quy

Trong lập trình, tất cả các vấn đề có thể được giải quyết bằng cách tiếp cận đệ quy cũng có cách tiếp cận lặp lại có thể được sử dụng để giải quyết chúng. Điều đó đang được nói, đối với nhiều vấn đề đệ quy cung cấp một giải pháp dễ dàng hơn nhiều. Một ví dụ đang làm việc với cấu trúc cây, chẳng hạn như các đối tượng được lồng sâu

Đọc một trường lồng sâu

Giả sử chúng ta có một đối tượng như sau và chúng ta muốn truy cập vào một trường được lồng sâu bên trong đối tượng

 const person = {
     data: {
         age: 20,
         name: {first_name: "John", last_name: "Doe"},
     }
 }
 
 console.log(person.data.last_name)

đầu ra

undefined

Chúng tôi muốn đọc

 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
1 của
 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
2. Chúng ta phải viết
 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
3. Và, tất nhiên, nếu một số dữ liệu bị thiếu, chúng tôi sẽ nhận được
 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
4

Để giải quyết vấn đề này, chúng tôi sử dụng những người trợ giúp như phương pháp

 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
5 của lodash

get(person, 'data.name.last_name', 'unknown');

Tiện ích này sẽ cố đọc giá trị một cách an toàn và trả về chuỗi

 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
6 nếu nó không tồn tại

Đây là cách triển khai một tiện ích như vậy có thể trông như thế nào khi sử dụng hàm đệ quy

 function get(obj, path, fallback) {
     const parts = path.split(".");
     const key = parts.shift();
     if(typeof obj[key] !== "undefined") {
         return parts.length > 0 ? get(obj[key], parts.join("."), fallback) : obj[key];
     }
     return fallback;
 }

console.log(get(person, "data.name.first_name"));
console.log(get(person, "data.name.last_name"));
console.log(get(person, "data.age"));
console.log(get(person, "data.date_of_birth"));
console.log(get(person, "data.date_of_birth", false));

đầu ra

John 
Doe
30
undefined
false

Lưu ý cách

 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
5 gọi chính nó theo cách đệ quy cho đến khi nó đến phần cuối cùng của đường dẫn. Ngoài ra, tham số
 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
8 được trả về khi chúng ta cố đọc một giá trị không tồn tại—
 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
9 trả về
 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
4, đây là giá trị mặc định khi không cung cấp ____8_______8. Khi một
 function countDownToZero(n) {
     .....
     // recursive case
     } else {
         console.log(n);
         countDownToZero(n - 1) // count down 1
     }
 }

countDownToZero(5);
8 được cung cấp, giá trị được cung cấp được trả về—
 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
3 trả về
 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
4

Tạo một bản sao sâu của một đối tượng

Theo

Bản sao sâu của một đối tượng là một bản sao có thuộc tính không chia sẻ cùng tham chiếu (trỏ đến cùng giá trị cơ bản) như thuộc tính của đối tượng nguồn mà từ đó bản sao được tạo. Kết quả là, khi bạn thay đổi nguồn hoặc bản sao, bạn có thể yên tâm rằng mình không làm cho đối tượng kia thay đổi; . Hành vi đó trái ngược với hành vi của một bản sao nông, trong đó các thay đổi đối với nguồn hoặc bản sao sẽ khiến đối tượng khác thay đổi (vì hai đối tượng có chung tham chiếu)

Hãy xem cách tạo một bản sao sâu của một đối tượng bằng cách sử dụng đệ quy

const createDeepCopy = (input) => {
  if (typeof input !== "object" || input === null) {
    return input; //BASE CASE
  }
    
  let copy = Array.isArray(input) ? [] : {};
    
  for (let key in input) {
    const value = input[key];
    copy[key] = createDeepCopy(value); //recursive call for each element of 
};

Trong đoạn mã trước

 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
5 là một hàm đệ quy. Nó tạo một bản sao sâu của một đối tượng được truyền tới nó thông qua đối số
 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
6 của nó

trường hợp cơ sở

// 5
// 4
// 3
// 2
// 1
// 0
0

Trong đoạn mã trước, nếu

 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
6 là kiểu nguyên thủy (chuỗi, số, v.v. ) hoặc bằng giá trị
 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
8 mà nó được trả về, điều này đảm bảo chỉ những đối tượng không phải là
 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
8 mới được chuyển vào làm đầu vào

trường hợp đệ quy

// 5
// 4
// 3
// 2
// 1
// 0
1

Đối với trường hợp đệ quy

  • Bản sao của đối tượng được lưu trữ trong biến
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    0
  • Sau đó, chúng tôi kiểm tra xem đối tượng là một mảng hay một đối tượng
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    1, nếu là
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    2, một bản sao của một mảng trống được khởi tạo;
  • Sau đó lặp qua
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    3(đối tượng) hoặc
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    4(mảng) của đối tượng bằng cách sử dụng vòng lặp
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    5
  • Giá trị
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    6 hoặc
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    7 tương ứng được lưu trữ trong biến
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    8—
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    9
  • Hàm
    undefined
    0 được gọi đệ quy với
     const person = {
         data: {
             age: 20,
             name: {first_name: "John", last_name: "Doe"},
         }
     }
     
     console.log(person.data.last_name)
    8
  • Kết quả được lưu trữ với cùng tên khóa trong đối tượng sao chép của chúng ta—______12_______2
  • Bản sao đối tượng của chúng tôi được trả lại—
    undefined
    3

Về cơ bản, chúng tôi đang gọi đệ quy hàm

 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
5 cho từng phần tử trong đối tượng tham chiếu của chúng tôi (đối tượng mà chúng tôi đang sao chép từ đó). Nếu kiểu dữ liệu của phần tử là nguyên thủy, nó sẽ được trả về nguyên trạng;

Ví dụ: hãy chuyển một đối tượng vào hàm

 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
5 để lấy bản sao sâu tương đương của nó

// 5
// 4
// 3
// 2
// 1
// 0
2

đầu ra

// 5
// 4
// 3
// 2
// 1
// 0
3

Trong mã trước, chúng tôi có thể xác minh rằng đối tượng được chuyển vào

 function countDownToZero(n) {
     // base case. Stop at 0
     if(n < 0) {
         return; // stop the function
     } else {
         console.log(n);
         countDownToZero(n - 1)
     }
 }

countDownToZero(5);
5 thực sự được sao chép. Từ đầu ra, các giá trị đã thay đổi trong đối tượng
undefined
8 không được phản ánh trong đối tượng mà chúng được sao chép từ đó(
undefined
9). Xem bản demo tại đây

Phần kết luận

Với hiểu biết cơ bản về đệ quy, giờ đây nó có thể được sử dụng như một phần trong kho vũ khí giải quyết vấn đề của bạn với tư cách là nhà phát triển—đặc biệt đối với các vấn đề yêu cầu chia chúng thành các bài toán con nhỏ hơn

MẸO TỪ NGƯỜI BIÊN TẬP. Đừng bỏ lỡ Chức năng Mãi mãi của chúng tôi. Bài viết Giải đố với đệ quy và JavaScript, với một số ví dụ khác về ứng dụng đệ quy

Tôi có thể tạo một mảng đối tượng trong JavaScript không?

Để tạo một mảng các đối tượng trong javascript, chúng ta phải khai báo một mảng trống trước rồi khởi tạo nó với các đối tượng được phân tách bằng dấu phẩy.

Bạn có thể thêm đối tượng vào mảng không?

Nhưng bạn có thể thắc mắc làm thế nào để thêm một phần tử vào một vị trí cụ thể trong một mảng. Chà, bạn có thể làm điều đó bằng phương thức splice() . Phương thức splice() là một phương thức có mục đích chung để thay đổi nội dung của một mảng bằng cách loại bỏ, thay thế hoặc thêm các phần tử vào các vị trí được chỉ định của mảng.