您的位置:首页 > Web前端 > JavaScript

可变不可变JavaScript

2020-08-13 10:35 162 查看

When I first dived into JavaScript and programming; I never really thought about immutable data. I would say animal is panda, then animal is lion.

当我第一次学习JavaScript和编程时; 我从未真正想到过不可变的数据。 我会说动物是熊猫,那么动物是狮子。

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

I was free to do whatever I wanted with my data! But… things changed… I grew up. People started telling me: “you should always use const if you can”. So I obediently did. But I didn’t really understand why.

我可以随意处理数据! 但是……事情变了……我长大了。 人们开始告诉我:“如果可以,请始终使用const ”。 所以我听话了。 但是我真的不明白为什么。

为什么使用不可变数据 (Why Use Immutable Data)

Because sometimes code changes things you don’t want to be changed. That’s a very lame answer I know, let me show you with an example.

因为有时代码会更改您不希望更改的内容。 我知道这是一个非常la脚的答案,让我举例说明。

Let’s say we have an e-commerce site.

假设我们有一个电子商务网站。

Module: checkout.js 模块: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
}
}

Let’s say we got our address-validator by installing an

npm
package.

假设我们通过安装

npm
软件包获得了地址验证器。

$ npm install address-validator

Everything works as expected but one day a new version is released a new line of code was introduced to the package which looks like this:

一切都按预期工作,但是有一天发布了新版本,该包中引入了新的代码行,如下所示:

Module: validateAddress.js 模块:validateAddress.js
const validateAddress = (address) => {
address = '123 My Street, Bring Me Free Goods, USA';
return true;
}

Now the variable

userAddress
will always be equal to the value of address! You can see how this is a problem.

现在,变量

userAddress
将始终等于address的值! 您可以看到这是一个问题。

This specific issue can be solved by using immutability. But it can also be solved with proper scoping. Try to figure out how!

可以通过使用不变性解决此特定问题。 但也可以通过适当的作用域解决。 尝试找出方法!

Of course, malicious code is an edge case. But there are many ways in which immutable data can help you write better code. For example, a very common mistake is to accidentally mutate the property of an object.

当然,恶意代码是一种极端情况。 但是不可变数据可以通过多种方式帮助您编写更好的代码。 例如,一个非常常见的错误是意外地更改了对象的属性。

Module: accidental-change.js 模块:accidental-change.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'

This type of mistake can occur very often.

这类错误可能经常发生。

不变性工具 (Immutability Tools)

The most intuitive immutability tool is to use

const
.

最直观的不变性工具是使用

const

const animal = 'panda';

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

const
is great. However, it sometimes only gives the illusion of immutability!

const
很棒。 但是,有时只给人一种不变性的幻想!

Module: example-checkout.js 模块: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' !!

There are different ways to solve this problem and introducing immutability is one of them.

解决此问题的方法有多种,引入不变性是其中一种。

First we can use the

Object.freeze
method.

首先,我们可以使用

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' !!

One issue with

Object.freeze
is that you don’t affect sub-properties. To reach all sub-properties you can write something like:

Object.freeze
一个问题是您不影响子属性。 要访问所有子属性,您可以编写如下内容:

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

Another solution is to use proxies if you need flexibility.

如果需要灵活性,另一种解决方案是使用代理。

使用属性描述符 (Using Property Descriptors)

In many sites, you will see that you can modify property descriptors to create immutable properties. And that’s true, but you have to make sure that

configurable
and
writeable
are set to false.

在许多站点中,您将看到可以修改属性描述符以创建不可变属性。 没错,但是您必须确保将可

configurable
writeable
设置为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'

But the data is still mutable, it is just more difficult to mutate it:

但是数据仍然是可变的,对它进行突变只会更加困难:

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!

If we set writable and configurable to false we get:

如果将writable和configurable设置为false,则会得到:

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

不变数组 (Immutable Arrays)

Arrays have the same problem as Objects.

数组与对象具有相同的问题。

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

Well you can use the same tools we used above.

好吧,您可以使用我们上面使用的相同工具。

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

其他方法 (Other Methods)

There are other tricks to keep your data safe. I highly recommend checking out our article about Proxy Traps to find out other ways to make your data immutable. We only scratched the surface here.

还有其他技巧可以保护您的数据安全。 我强烈建议您阅读有关代理陷阱的文章,以了解使数据不可变的其他方法。 我们只在这里刮了一下表面。

In this post, we explored options to introduce immutability without changing the structure and style of our code. In future posts, we will explore libraries such as Immutable.js, Immer or Ramda to right immutable code.

在本文中,我们探讨了在不更改代码结构和样式的情况下引入不变性的选项。 在以后的文章中,我们将探索Immutable.js,Immer或Ramda之类的库,以编写正确的不可变代码。

翻译自: https://www.digitalocean.com/community/tutorials/js-mutability

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: