Thứ năm, 02/04/2020 | 00:00 GMT+7

JavaScript bất biến có thể thay đổi


Khi tôi lần đầu tiên tìm hiểu về JavaScript và lập trình; Tôi chưa bao giờ thực sự nghĩ về dữ liệu bất biến. Tôi sẽ nói động vật là pandas , sau đó động vật là sư tử.

var animal = 'panda';
animal = 'lion';

Tôi được tự do làm bất cứ điều gì tôi muốn với dữ liệu của bạn ! Nhưng… mọi thứ đã thay đổi… tôi lớn lên. Mọi người bắt đầu nói với tôi: "Bạn nên luôn sử dụng const nếu bạn có thể". Nên tôi ngoan ngoãn làm theo. Nhưng tôi không thực sự hiểu tại sao.

Tại sao sử dụng dữ liệu bất biến

Vì đôi khi mã thay đổi những thứ bạn không muốn bị thay đổi. Đó là một câu trả lời rất khập khiễng mà tôi biết, hãy để tôi cho bạn xem một ví dụ.

Giả sử ta có một trang thương mại điện tử.

Mô-đun: checkout.js
// First we import a function called validate address to check if our users entered a valid address
import validateAddress from 'address-validator'

const checkout = (user, cart) => {
 //... checkout code

 var userAddress = user.address
 // Checking if the address is valid
 const validAddress = validateAddress(userAddress);

 // If the address is valid then
 if (validAddress) {
  //... proceed to checkout
 }
}

Giả sử ta có trình xác thực địa chỉ bằng cách cài đặt gói npm .

$ npm install address-validator

Mọi thứ hoạt động như mong đợi nhưng một ngày nọ, một version mới được phát hành, một dòng mã mới đã được đưa vào gói trông giống như sau:

Mô-đun: validateAddress.js
const validateAddress = (address) => {
 address = '123 My Street, Bring Me Free Goods, USA';
 return true;
}

Bây giờ biến userAddress sẽ luôn bằng giá trị của địa chỉ! Bạn có thể thấy đây là một vấn đề.

Vấn đề cụ thể này có thể được giải quyết bằng cách sử dụng tính bất biến. Nhưng nó cũng có thể được giải quyết với phạm vi phù hợp. Cố gắng tìm ra cách!

Tất nhiên, mã độc là một trường hợp khó. Nhưng có nhiều cách trong đó dữ liệu bất biến có thể giúp bạn viết mã tốt hơn. Ví dụ, một sai lầm rất phổ biến là vô tình làm thay đổi thuộc tính của một đối tượng.

Mô-đun: tình cờ-thay đổi.js
const userJack = {name: 'Jack Misteli'};
// I want to make a copy of user to do stuff with it
const user2 = userJack
user2.name = 'Bob'

// Because we didn't do a copy:
// userJack.name === 'bob'

Loại sai lầm này có thể xảy ra rất thường xuyên.

Công cụ bất biến

Công cụ bất biến trực quan nhất là sử dụng const .

const animal = 'panda';

// This will throw a TypeError!
panda = 'lion';

const là tuyệt vời. Tuy nhiên, nó đôi khi chỉ mang lại ảo giác về sự bất biến!

Mô-đun: example-checkout.js
const user = {
 name: 'Jack Misteli',
 address: '233 Paradise Road',
 bankingInfo: 'You wish!'
};

const maliciousAddressValidator = (user) => {
 user.address = 'Malicious Road';
 return true;
};

const validAddress = maliciousAddressValidator(user);
// Now user.address === 'Malicious Road' !!

Có nhiều cách khác nhau để giải quyết vấn đề này và giới thiệu tính bất biến là một trong số đó.

Đầu tiên, ta có thể sử dụng phương thức Object.freeze .

const user = {
 address: '233 Paradise Road'
};
Object.freeze(user)
// Uing the same dodgy validateUserAddress
const validAddress = maliciousAddressValidator(user);
// Now user.address === '233 Paradise Road' !!

Một vấn đề với Object.freeze là bạn không ảnh hưởng đến các thuộc tính phụ. Để tiếp cận tất cả các thuộc tính phụ, bạn có thể viết thông tin như :

const superFreeze = (obj) => {
 Object.values(obj).forEach(val =>{
  if (typeof val === 'object')
    superFreeze(val)
  })
  Object.freeze(obj)
}

Một giải pháp khác là sử dụng proxy nếu bạn cần sự linh hoạt.

Sử dụng Trình mô tả Thuộc tính

Trong nhiều trang web, bạn sẽ thấy rằng bạn có thể sửa đổi các bộ mô tả thuộc tính để tạo ra các thuộc tính bất biến. Và điều đó đúng, nhưng bạn phải đảm bảo có thể configurablewriteable được đặt thành false.

// Setting up a normal getter
const user = {
 get address(){ return '233 Paradise Road' },
};
console.log(Object.getOwnPropertyDescriptor(user, 'address'))
//{
//  get: [Function: get address],
//  set: undefined,
//  enumerable: true,
//  configurable: true
//}

const validAddress = maliciousAddressValidator(user);

// It looks like our data is immutable!
// user.address ===  '233 Paradise Road'

Nhưng dữ liệu vẫn có thể thay đổi, chỉ là khó hơn để biến đổi nó:

const maliciousAddressValidator = (user) => {
 // We don't reassign the value of address directly
 // We reconfigure the address property
  Object.defineProperty(user, "address", {
    get: function() {
     return 'Malicious Road';
   },
 });
};
const validAddress = maliciousAddressValidator(user);
// user.address === 'Malicious Road'

Arr!

Nếu ta đặt có thể ghi và có thể cấu hình thành false, ta nhận được:

const user = {};
Object.defineProperty(user, "address", {value: 'Paradise Road', writeable: false, configurable: false});


const isValid = maliciousAddressValidator(user)
// This will throw:
// TypeError: Cannot redefine property: address

Mảng bất biến

Mảng có cùng vấn đề với Đối tượng.

const arr = ['a','b', 'c']
arr[0] = 10
// arr === [ 10, 'b', 'c' ]

Bạn có thể sử dụng các công cụ tương tự mà ta đã sử dụng ở trên.

Object.freeze(arr)
arr[0] = 10
// arr[0] === 'a'

const zoo = []
Object.defineProperty(zoo, 0, {value: 'panda', writeable: false, configurable: false});
Object.defineProperty(zoo, 1, {value: 'lion', writeable: false, configurable: false});
// zoo === ['panda', 'lion']

zoo[0] = 'alligator'
// The value of zoo[0] hasn't changed
// zoo[0] === 'panda

Các phương pháp khác

Có những thủ thuật khác để giữ an toàn cho dữ liệu . Tôi thực sự khuyên bạn nên xem bài viết của ta về Bẫy proxy để tìm ra các cách khác để làm cho dữ liệu không thay đổi. Ta chỉ làm xước bề mặt ở đây.

Trong bài đăng này, ta đã khám phá các tùy chọn để giới thiệu tính bất biến mà không thay đổi cấu trúc và kiểu mã của ta . Trong các bài viết trong tương lai, ta sẽ khám phá các thư viện như Immutable.js, Immer hoặc Ramda cho mã bất biến bên phải.


Tags:

Các tin liên quan

Hiểu các tham số mặc định trong JavaScript
2020-03-31
Cookie là gì và cách làm việc với chúng bằng JavaScript
2020-03-19
Tham quan nhanh về date-fns, Thư viện ngày JavaScript đơn giản
2020-03-18
Phương thức getOwnPropertyDescriptors trong JavaScript
2020-03-12
Cây tìm kiếm nhị phân thông qua JavaScript
2020-03-03
Tree Traversal qua JavaScript
2020-03-02
Hiểu về Trình tạo trong JavaScript
2020-02-28
Triển khai Thành phần Tab từ Scratch trong Vanilla JavaScript
2020-02-24
Khám phá cây qua JavaScript
2020-02-23
Giới thiệu về danh sách được liên kết qua JavaScript - Phần 2: Triển khai
2020-02-23