[red book] Chapter 8: understanding objects, classes and object-oriented programming

ECMA-262 defines an object as an unordered set of attributes. We can imagine the object in JS as a hash table, in which the content is a set of key value pairs, and the type of value can be data or function.

1, Understanding object

The usual way to create a custom Object is to create a new instance of the Object, and then add properties and methods to the instance.

let person = new Object();
person.name = 'Macc';
person.age = 18;
person.sayHi = function(){
    console.log('hi');
}

⭐ Now it is more popular to define objects in a literal way

//Object Literal 
let person = {
    name:'Macc',
    age:16,
    sayHi:function(){
        console.log('hi');
    }
}

The properties and methods of the above two objects are the same, which can be regarded as equivalent (note that they are equivalent rather than the same or the same object).

The properties of objects have their own characteristics, which determine their behavior in JS.

(1) Type of property

ECMA-262 uses some internal features to describe the characteristics of attributes. Since it is an internal feature, that is to say, developers cannot directly access these features in JS.

The specification identifies a property as internal by enclosing its name in two square brackets. For example: [[enumerable]]

The properties of objects are divided into two types: data properties and accessor properties.

1. Data attributes

Data attributes have four characteristics that describe their behavior

Property nameeffectDefault value
[[Configurable]]Indicates whether the attribute can be deleted and redefined through delete, whether the attribute characteristics can be modified, and whether the attribute type can be changed to accessor attribute.true
[[Enumberable]]Property can be returned through a for in loop.true
[[Writable]]Whether the value of the property can be modified.true
[[value]]The storage location of the actual value of the attribute and the operation location for reading and writing the attribute value.undefined

Modify default (internal) properties

As mentioned above, developers cannot directly access internal features in js. Therefore, to modify the internal properties of attributes, you must use object Defineproperty() method.

Object.defineProperty(obj, 'propertyName', descriptionobj) method
Parameter nameParameter typedescribe
objObjectObject to add / modify attributes
propertyNameStringThe name of the attribute to add or modify
decriptionObjObjectdescriptor object

The attribute on the descriptor object can contain four internal attribute names (that is, the attribute name is the name of the internal attribute).

let person = {};//Define a person object
//Adding properties to an object
Object.defineProperty(person,'name',{
    writable:false, //The attribute value cannot be modified
    value:'Macc'  //Actual value of property
});
console.log(person.name);//Access the attribute value and output 'Macc'
person.name = 'GaGa';//Try to modify the value of the property
console.log(person.name);//Output 'Macc'

Because the internal property writable of the name attribute of the person object is changed to false, which means that the value of the attribute cannot be modified. Therefore, when later trying to change it to GaGa, the modification behavior is ignored (an error will be reported in strict mode) and the original value is still output.

If the configurable attribute of the attribute is set to false, it cannot be changed back to true. At this time, call object The defineproperty () method and modifying any non writable property will report an error.

Call object When defineproperty(), the values of configurable, enumerable and writable are false by default if not specified.

let person = {
    name: 'Macc',
    age: 18
}
Object.defineProperty(person, 'hair', {
    value: 'black',
    //No other value is specified. Other values default to false
});

let _p = Object.getOwnPropertyDescriptor(person, 'hair');
console.log(_p);

2. Accessor properties

Accessor properties do not contain data values.

The accessor property contains a get function getter and a set function setter.

  • When reading the accessor property, getter will be called. The responsibility of getter is to return a valid value.
  • When writing an accessor property, a setter will be called and a new value will be passed in. The setter decides what to modify the data (the value of the property).

Accessor properties also have four properties that describe their behavior

Property nameeffectDefault value
[[Configurable]]Indicates whether the attribute can be deleted and redefined through delete, whether the attribute characteristics can be modified, and whether the attribute type can be changed to data attribute.true
[[Enumberable]]Property can be returned through a for in loop.true
[[Get]]getter, called when the property is read.undefined
[[Set]]setter, called when writing a property.undefined

⭐ Object is also used to modify accessor properties Defineproperty() method.

let book = {
    year_: 2017,//Private member
    edition: 1  //Public member
}
Object.defineProperty(book, 'year', {
    get() {
        return this.year_;
    },
    set(newValue) {
        if (newValue > 2017) {
            this.year_ = newValue;
            this.edition = newValue - 2017;
        }
    }
});

book.year = 2018;
console.log(book.edition); //Output 2

The above code is a typical usage scenario of accessor attribute: setting the value of an attribute will lead to some other changes.

Getters and setter s do not have to be defined:

  • Defining only getter s means that the attribute is read-only, and attempts to modify the attribute will be ignored;
  • Only setter s are defined. Reading properties in non strict mode will return undefined.

There was no object before ES5 Defineproperty() method.

3. Define multiple attributes at the same time

Use object Defineproperties (obj, descriptionobj) method

Parameter nameParameter typedescribe
objObjectObject to add / modify attributes
decriptionObjObjectdescriptor object
let person = {}
Object.defineProperties(person, {
    name: {
        value: 'Macc'
    },
    age: {
        value: 18
    },
    hair: {
        get() {
            return 'black';
        },
        set(newValue) {
            //....
        }
    }
});

4. Read properties

4.1 read a property of an attribute

Use method object Getownpropertydescriptor (property object, property name), this method return s an object.

4.2 read the characteristics of all self owned attributes of the object

Use method object Getownpropertydescriptors (object). This method also returns an object. The object includes the characteristics of all its own properties of the specified object. If the object has no properties, it returns an empty object.

This method actually calls object on each of its own attributes Getownporpertydescriptor () method and return them in a new object.

(2) Merge objects

Copy the desired local attributes of the source object to the target object. This operation is called merge, also known as mixin.

The method used to merge objects is object Assign (target object, source object 1,..., source object n).
This method copies all enumerable and self owned properties in the source object to the target object.

The so-called enumerable attribute refers to calling object Prpertyisenumerable() returns the property of true;
The so-called self owned attribute refers to calling object Hasownproperty() returns the property of true;

During copying, the [[Get]] on the source object will be used to obtain the property value, and then the [[Set]] on the target object will be used to Set the property value.

Object.assign() performs shallow copying, copying only references to objects.

If multiple source objects have the same attribute, the last copied value is used (that is, the value of which source object is used later) (overwrite)

Getter and setter functions cannot be transferred between two objects. The value obtained from the source object accessor property, such as the getter function, will be assigned to the target object as a static value.

If there is an error during the assignment, the operation will stop and exit, and an error will be thrown. However, this method will not roll back. It is a best effort method that may only complete partial replication.

(3) Object identification and equality determination

Before ES6, there was a situation where using congruence (= = =) was powerless:

  1. The expected conditions are as follows:
expressionresult
true === 1false
{} === {}false
"2" === 2false

2. Different js engines perform differently, but are still considered equal

expressionresult
+0 === -0true
+0 === 0true
-0 === 0true
  1. To determine the equality of NaN, you must use the isNaN function
expressionresult
NaN === NaNfalse
isNaN(NaN)true

The method object is added in ES6 Is (), which is similar to congruence, but considers the above boundary conditions. The method must accept two parameters.

expressionresult
Object.is(+0,-0)false
Object.is(+0,0)true
Object.is(-0,0)false
Object.is(NaN,NaN)true

If you want to use object Is() checks for more than two values, which can be realized recursively by using Equality:

function recursivelyCheckEqual(x, ...rest) {
    return Object.is(x, rest[0]) &&
        (rest.length < 2 || recursivelyCheckEqual(...rest));
}

console.log(recursivelyCheckEqual(1, 2, 3, 4)); //false

(4) Enhanced object syntax (syntax sugar)

1. Abbreviation of attribute value

As long as the variable name is used, the short attribute value will be automatically interpreted as the attribute key with the same name. If the variable with the same name is not found, an error will be thrown.

let name = 'Macc',
    age = 18;
let person = {
    name,  //Abbreviation
    //The following is the previous way of writing
    age:age
};
console.log(person);//{name:"Macc",age:18}

2. Computable attribute

Before introducing computable attributes, if you want to use variable values as attributes (names), you must declare objects first, and then add attributes using bracket syntax. That is, you cannot dynamically name attributes directly in object literals.

const nameKey = 'name';
let person = {};//Declare object first
person[nameKey] = 'Macc';//Add attributes using bracket syntax

After introducing the computable attribute, the dynamic attribute assignment can be completed in the object literal.

let person = {
    [nameKey]:'Macc'
}

Any error thrown in the computable property expression will interrupt the creation of the object and will not be rolled back.

const nameKey = 'Macc';
const ageKey = 'age';

let person = {
    [nameKey]: 'Macc',
    [jobKey]: 'Code farmer', //There will be mistakes here
    [ageKey]: 18
}
console.log(person);//It cannot be printed here because the creation of the object is interrupted.

3. Abbreviated method name

Before that, when defining methods for objects, the format is as follows:

let person = {
    //Method name colon anonymous function expression
    sayHi:function(){
        //...
    }
}

Now it is:

let person = {
    sayHi(){
        //...
    }
}

Moreover, the abbreviated method names are compatible with computable properties.

const methodKey = 'sayHi';
let person = {
    [methodKey](name){
        //...
    }
}

(5) Object deconstruction

Use nested data to implement one or more assignment operations in a statement.
In short, the assignment of object attributes is realized by using the structure matching with the object.

Matching structure: it's a bit like taking a seat according to the number.

  1. You can use abbreviated syntax
let person = {
    name:'Macc',
    job:'Code farmer'
};
let {name,job} = person;
console.log(name,job);//Macc, Manon
  1. The deconstruction assignment does not necessarily match the attributes of the object (some attributes can be ignored during assignment, without one-to-one correspondence)
let {name,age} = person;//No one-to-one correspondence is required
console.log(name,age);//"Macc",undefined
  1. The default value can be set while deconstructing the assignment
let {name,age:18} = person;
console.log(name,age);//"Macc",18

Deconstruction uses the ToObject() method internally to deconstruct and convert the source data into an object, that is, in the context of object deconstruction, the original value will be regarded as an object. null and undefined cannot be deconstructed, otherwise an error is reported.

let { _ } = null;//report errors
let { _ } = undefined;//report errors

Deconstruction does not require that variables must be declared in the deconstruction expression, but if a value is assigned to a previously declared variable, the expression must be enclosed in a pair of parentheses.

let personName,personAge;//Declare variables in advance
({name:personName,age:personAge} = person);

1. Nested deconstruction

First, you can use deconstruction to copy the attributes of an object:

let person = {
    name: 'Macc',
    age: 18,
};

let personCopy = {}; //Remember to add this semicolon here, or you will report an error

({ name: personCopy.name, age: personCopy.age } = person);
console.log(personCopy);
//{name: 'Macc', age: 18}

Then think about it. What if the copied attribute is a nested structure? Can deconstruction still work? The answer is yes. Nested structure can be used for deconstruction assignment, but it cannot be used when the outer attribute is not defined.

let person = {
    job: {
        title: 'Code farmer'
    }
};

let personCopy = {}; //Remember to add this semicolon here, or you will report an error

let { job: { title } } = person;
console.log(title); //Code farmer

//foo is undefined on the source object, and an error is reported
({ foo: { bar: person.bar } } = person);
//The job is undefined on the source object, and an error is reported
({ job: { title: person.job.title } } = person);

2. Partial deconstruction

If a deconstruction expression designs multiple assignment operations, the initial assignment is successful and the subsequent assignment is wrong, the whole deconstruction assignment will only be completed in part.

3. Parameter context matching

Deconstruction assignment can also be performed in the function parameter list without affecting the arguments object.

let person = {
    name: 'Macc',
    age: 18
};

function printPerson(foo, { name, age }, bar) {
    console.log(arguments);
}
printPerson('1st', person, '2nd');

Keywords: Javascript Front-end

Added by NJordan72 on Tue, 01 Feb 2022 07:50:56 +0200