Deep Clone Objects trong Javascript – Giới thiệu một biện pháp cực mạnh

2415

Bài viết được sự cho phép của tác giả Sơn Dương

Làm việc với Javascript nhiều rồi nhưng bạn đã bao giờ gặp trường hợp mà khi cần clone một object chưa? Có bao giờ bạn chỉnh sửa một object thì cả hai object đều bị thay đổi? Ví dụ một bài toán cụ thể là bạn tạo một object lưu cấu hình mặc định. Sau này, tùy từng chức năng mà cần sao chép cấu hình mặc định và chỉnh sửa nó. Lúc sau quay lại, object cấu hình mặc định cũng bị chỉnh sửa theo. Kì lạ!?

Đặt vấn đề khi copy một object trong Javascript

Để bạn dễ hình dung, mình ví dụ bằng đoạn code khi bạn thực hiện copy một object:

const defaultConfig = {
  debug: true,
  name: "Cấu hình mặc định",
  connectType: "Wifi",
}

const customConfig = defaultConfig;
customConfig["debug"] = false;
customConfig["name"] = "Cấu hình cho KH";

console.log(defaultConfig);

Kết quả:

deep-clone-object-javascript

Như vậy, mặc dù bạn không hề chỉnh sửa defaultConfig nhưng nó cũng bị biến đổi theo sau khi cập nhật customConfig.

Nguyên nhân của vấn đề trên nó liên quan tới kiểu dữ liệu tham chiếu và tham trị. Với Object nó là kiểu dữ liệu tham chiếu. Thức là các biến chỉ lưu trữ địa chỉ vùng nhớ của object mà thôi. Nên khi gán biến đó cho một biến khác thì bản chất là tạo ra hai biến cùng trỏ tới một vùng nhớ.

Do đó, deep clone một object là phương pháp tạo một vùng nhớ mới độc lập với vùng nhớ ban đầu.

  JavaScript Arrays và Objects thật ra không khác gì sách và báo

  7 khái niệm Javascript cơ bản không thể bỏ qua

Deep Clone Objects

Ok, sau khi hiểu vấn đề và bản chất rồi, chúng ta cùng nhau thử một số cách để deep clone một object trong javascript.

1. Sử dụng toán tử Spread

Spread là toán tử thực hiện “làm phẳng” một object. Do đó, sau khi “làm phẳng” bạn cần tạo mới một object thông qua cú pháp {}

const defaultConfig = {
  debug: true,
  name: "Cấu hình mặc định",
  connectType: "Wifi",
}

const customConfig = {...defaultConfig};
customConfig["debug"] = false;
customConfig["name"] = "Cấu hình cho KH";

console.log(defaultConfig);
// {debug: true, name: "Cấu hình mặc định", connectType: "Wifi"}

Nhược điểm của phương pháp này là chỉ có tác dụng với object không lồng nhau. Chứ Object mà nhiều level lồng nhau thì cũng “hết phép”.
Ví dụ:

const defaultConfig = {
  debug: true,
  name: "Cấu hình mặc định",
  connectType: "Wifi",
  data: {
    position: 1
  }
}

const customConfig = {...defaultConfig};
customConfig["debug"] = false;
customConfig["name"] = "Cấu hình cho KH";
customConfig["data"]["position"] = 2

console.log(defaultConfig);
// {debug: true, name: "Cấu hình mặc định", connectType: "Wifi", data: { postion: 2}}

Như vậy là không được rồi. Phải dùng biện pháp mạnh hơn (tham khảo phương pháp số 2 mà mình giới thiệu phía cuối bài viết nhé)

Việc làm JavaScript Hồ Chí Minh dành cho bạn!

2. Sử dụng Object.assign

Giải pháp này cũng tương tự với dùng toán tự Spread.

const defaultConfig = {
  debug: true,
  name: "Cấu hình mặc định",
  connectType: "Wifi",
}

const customConfig = Object.assign({}, defaultConfig);
console.log(defaultConfig);
// {debug: true, name: "Cấu hình mặc định", connectType: "Wifi"}

Điểm khác biệt duy nhất so với sử dụng toán tử Spread là hàm Object.assign() là khả năng hỗ trợ nhiều trình duyệt hơn.

Xem việc làm javascript đãi ngộ tốt trên TopDev

3. Biện pháp rất mạnh là sử dụng JSON

Nếu như hai giải pháp trên không đáp ứng được yêu cầu deep clone của bạn thì đây là liều thuốc cực mạnh, sẽ giải quyết được vấn đề của bạn.

Đó chính là sử dụng hàm JSON.parse() kết hợp với JSON.stringify().

const defaultConfig = {
  debug: true,
  name: "Cấu hình mặc định",
  connectType: "Wifi",
}

const customConfig = JSON.parse(JSON.stringify(defaultConfig));

Giải pháp này thoạt nhìn thì hơi buồn cười nhưng nó thực sự có võ đấy.

4. Tự viết hàm clone object

Nếu trường hợp bạn muốn tự làm chủ công nghệ deep clone Object, chúng ta có thể tự viết hàm create object. Bằng cách đệ quy để duyệt qua từng thuộc tính của object gốc và tạo ra object mới.

function cloneObject(obj) {
  var clone = {};
  for(var i in obj) {
      if(obj[i] != null && typeof(obj[i])=="object")
          clone[i] = cloneObject(obj[i]);
      else
          clone[i] = obj[i];
   }
   return clone;
}

// Cách sử dụng
const defaultConfig = {
  debug: true,
  name: "Cấu hình mặc định",
  connectType: "Wifi",
  data: {
    position: 1
  }
}

const customConfig = cloneObject(defaultConfig)
customConfig["debug"] = false;
customConfig["data"]["position"] = 2;
console.log(defaultConfig);
// {debug: true, name: "Cấu hình mặc định", connectType: "Wifi", data: {position: 1}}

Tạm kết

Trên đây là một số cách để bạn có thể deep Clone Objects trong Javascript. Mình hi vọng sẽ có ích cho dự án của bạn.

Bài viết gốc được đăng tải tại vntalking.com

Tìm việc làm IT mới nhất trên TopDev