As a front-end developer, you will feel the powerful concept of Object in JS. We say “everything is an object in JS”. The core features, such as strings, arrays, and browser APIs, are everywhere. Here you can learn everything about JS Objects.

Meanwhile, with React’s rise, whether you’ve followed the framework or not, you’ve probably heard of the concept immutable.js. What exactly is immutable data? This article will start from the source of JS, objects, to give you an introduction to the important concepts of functional programming.

Objects in JS are beautiful: we can copy them at will, change and delete one of their properties, etc. But remember one thing:

“With privilege comes greater responsibility.” (With great power comes great responsibility)

Indeed, there are so many concepts in JS Objects that we must not use Objects arbitrarily. Next, I’ll start with basic objects and talk about immutable data and JS.

This post was posted by Daniel Leite on March 16, 2017: Things you should know about Objects and Immutability in JavaScript, I made a rough translation and a wide range of “transformation”, and at the same time rewrote the examples used, and made a lot of more extensions.

“Variable and shared” is the root of all evil

Immutable data is actually an important concept related to functional programming. In contrast, functional programming believes that variability is the root of all evil. But why?

This is probably a problem many programmers have. In fact, if your code is logically variable, don’t panic; it’s not “politically incorrect”. For example, JS array operations, most of the original array will be directly changed, this of course is not a problem. Such as:

let arr = [1.2.3.4.5];
arr.splice(1.1); / / return [2];
console.log(arr); // [1, 3, 4, 5];Copy the code

This is the usual “delete an item in an array” operation. Well, there’s nothing wrong with him.

The problem is with “abuse” variability, which can have “side effects” on your program. Regardless of what “side effects” are, it is a functional programming concept.

Let’s take a look at a code example:

const student1 = {
    school: 'Baidu'.name: 'HOU Ce'.birthdate: '1995-12-15',}const changeStudent = (student, newName, newBday) = > {
    const newStudent = student;
    newStudent.name = newName;
    newStudent.birthdate = newBday;
    return newStudent;
}

const student2 = changeStudent(student1, 'YAN Haijing'.'1990-11-10');

// both students will have the name properties
console.log(student1, student2);
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"} 
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"}Copy the code

We found that while a new object student2 was created, the old object student1 was also changed. This is because assignment in JS objects is “reference assignment”, that is, during assignment, a memory reference is passed. Specifically, it is the problem of “stack storage” and “heap storage”. I will not draw the specific diagram, can not understand me alone.

The power and implementation of immutable data

When we say immutable, we mean that an object remains in the same state. This has the advantage of making development simpler, traceable, and test-friendly, reducing any possible side effects. Functional programming says:

Only pure functions without side effects are qualified functions.

Okay, now for the Side effect:

In computer science, a function side effect is an additional effect on the calling function that occurs when a function is called in addition to returning the value of the function. Such as modifying global variables (variables outside a function) or modifying parameters. – Wikipedia

Function side effects can cause unnecessary problems in the program design, lead to hard-to-find errors, and reduce the readability of the program. Strictly functional languages require that functions have no side effects.

The main implementation idea for creating immutable data to avoid side effects is that the original object should not be changed during an update, but a new object should be created to host the new data state.

We use pure functions for immutability. A pure function is one that has no side effects. So, how do you construct a pure function? We can take a look at the code implementation, and I’ll modify the above example:

const student1 = {
    school: "Baidu".name: 'HOU Ce'.birthdate: '1995-12-15',}const changeStudent = (student, newName, newBday) = > {
    return {
        ...student, // Use deconstruction
        name: newName, // Override the name attribute
        birthdate: newBday // Override the birthdate attribute}}const student2 = changeStudent(student1, 'YAN Haijing'.'1990-11-10');

// both students will have the name properties
console.log(student1, student2);
// Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15"} 
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"}Copy the code

Note that I used destructuring assignment in ES6. This gives us what we want: a new object is generated based on the parameters, the value is assigned correctly, and most importantly, the original object is not changed.

Create pure functions that filter side effects

Now we know what immutable means. Next, we need to analyze how pure functions should be implemented to produce immutable data.

In fact, there are many ways to create immutable data. In addition to using native JS, my recommended method is to use the existing Objects API and destruct assignment in ES6 (as demonstrated in the above example). Now look at the objects. assign implementation:

const student1 = {
    school: "Baidu".name: 'HOU Ce'.birthdate: '1995-12-15',}const changeStudent = (student, newName, newBday) = > Object.assign({}, student, {name: newName, birthdate: newBday})

const student2 = changeStudent(student1, 'YAN Haijing'.'1990-11-10');

// both students will have the name properties
console.log(student1, student2);
// Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15"};
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"};Copy the code

Similarly, when dealing with arrays, we can use.map,.filter, or.reduce to do this. The common feature of these APIs is that they do not alter the original array, but instead generate and return a new array. This is consistent with the idea of pure functions.

However, when using object. assign, note the following: 1) It copies all enumerable attributes to the target Object. In other words, non-enumerable properties cannot be copied. 2) If the object contains undefined and null content, an error will be reported. 3) Most important point: Object.assign implements shallow copy, not deep copy.

The third important point is that if the value of an attribute of the source object is an object, then the target object copies a reference to that attribute object. This means that when objects are nested, there is still a problem. For example:

const student1 = {
    school: "Baidu".name: 'HOU Ce'.birthdate: '1995-12-15'.friends: {
        friend1: 'ZHAO Wenlin'.friend2: 'CHENG Wen'}}const changeStudent = (student, newName, newBday, friends) = > Object.assign({}, student, {name: newName, birthdate: newBday})

const student2 = changeStudent(student1, 'YAN Haijing'.'1990-11-10');

// both students will have the name properties
console.log(student1, student2); 
// Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15", friends: Object}
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10", friends: Object}

student2.friends.friend1 = 'MA xiao';
console.log(student1.friends.friend1); // "MA xiao"Copy the code

The modification of Friend1 in the student2 friends list also affects Friend1 in the student1 friends list.

The powerlessness of JS VS the immutable data library

Above, we analyzed how pure JS implements immutable data. One downside of this approach is that some of the classic APIs are shallow, such as the object. assign mentioned above, which is a typical shallow copy. If we encounter deeply nested structures, we need to recurse manually. There is also a performance problem.

For example, I implemented a deep copy recursively myself, which required consideration of the “dead loop” problem of circular references, and the performance disadvantages were obvious when working with large data structures. One version of the jquery extends method that we are familiar with (I don’t know about the latest version) is implemented with three levels of copy and not a full deep copy.

In summary, to implement immutable data, we must be concerned about performance. For this reason, I recommend the well-known immutable data library, immutable. Js.

His implementation ensures both immutability and maximum performance optimization. The principle is very interesting. Here is an excerpt from a previous article by Camsong:

Immutable is a Persistent Data Structure, which ensures that old Data can be used to create new Data without changing it. And to avoid the performance cost of deepCopy copying all nodes once, Immutable uses Structural Sharing, where if a node in the object tree changes, only that node and its affected parent are modified, and the other nodes are shared.

It’s interesting for interested readers to delve into it. If necessary, I would also like to write another immutable source code analysis.

conclusion

We use JavaScript to manipulate objects, which is easy and convenient. However, the foundation of such manipulation is mastery of JavaScript’s flexible mechanics. It’s easy to get a big head.

In the private message project of a certain department of Baidu, I used the React+Redux technology stack and the data structure was more responsible, so I also used immutable.

Finally, in front-end development, functional programming is becoming increasingly popular and has to some extent replaced “procedural” programming and object-oriented thinking.

My idea is to embrace the future without fear of change in certain situations. As one of my favorite Portuguese poets, Andrade, says:

I also did not know what the sea was, and stood barefoot on the sand, eagerly waiting for the dawn.

Happy Coding!

PS: Baidu knowledge search department continues to recruit, with senior engineer and intern positions, as well as product and operation positions. Interested parties contact us immediately…