Extension of Objects in ES

Time:2022-8-10

Enumerability of properties

enumerability

Each property of the object has a description object (Descriptor), which is used to control the behavior of the property. The Object.getOwnPropertyDescriptor method can get the description object of the property

    let obj = {foo: 'foo'}
    Object.getOwnPropertyDescriptor(obj, 'foo')
    /*
        {
            value: 'foo',
            writable: true,
            enumerable: true,
            configurable: true
        }
    */ 

Describes the enumerable property of the object, called 'enumerability', if this property is false, it means that some operations will ignore the current property

Currently, there are four operations that ignore properties whose enumerable is false

  • for in only iterates over the object itself and inherited enumerable properties
  • object.keys only returns traversable property names
  • Json.stringify only serializes the enumerable properties of the object itself
  • Object.assign ignores properties whose enumerable is false and only copies the enumerable properties of the object itself

The original purpose of introducing the concept of enumerability is to allow some properties to avoid being for…in, otherwise the internal methods and properties will be traversed. For example, the tostring of an object, the length of an array

    Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumable
    // false

    Object.getOwnPropertyDescriptor([], 'length').enumable
    // false

The toString of the above code object and the enumable of the length property of the array are both false, so they will not be traversed by for…in

ES6 stipulates that all inherited properties of classes are not enumerable

    Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
    // false

Since for…in always introduces inherited properties, try to use Object.keys

Traversal of properties

There are five methods in ES6 to traverse the properties of an object

(1)for…in
for…in iterates over the object itself and inherited enumerable properties (excluding Symbol)

(2)Object.keys(obj)
Object.keys returns an array containing all its own (excluding inheritance) enumerable properties (excluding Symbol) key names

(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames returns an array containing the key names of all properties of the object itself (excluding Symbol, including non-enumerable) properties

(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols returns an array containing all its own keys containing Symbol properties

(5)Reflect.ownKeys(obj)
Reflect.ownKeys returns an array containing all the properties of the object itself (excluding inheritance), regardless of whether the key name is Symbol, whether it is enumerable or not

The above five methods traverse the key names of objects, all follow the same order rules of attribute traversal

First iterate through all the numeric keys and sort them in ascending numerical order
Next, iterate over all string keys and sort them in ascending order by joining time
Finally traverse all the Symbol keys and sort them in ascending order by joining time

    let obj = {
        [Symbol()]: 0,
        b: 0,
        3: 9,
        10: 20,
        a: 9
    }
    Reflect.ownKeys(obj)
    // [3, 10, b, a, Symbol()]

The Reflect.ownKeys method returns an array, and the return order of this array is as follows,

First the numbers 3 and 10, then the strings b and a, and finally the symbol

super keyword

this always points to the current object where the function is located. ES6 added the super keyword, which refers to the prototype object of the current object

this –> current object
super –> the prototype object of the current object

    let person = {
        name: 'person'
    }

    let son = {
        name: 'sun',
        printPerson () {
            return super.name
        }
    }
    
    Object.setPrototypeOf(son, person)
    son.printPerson()
    // 'person'

The above is by setting the prototype of son and calling the son method to find the properties of the prototype

When super represents a prototype object, it can only be used in the method of the object, and an error will be reported in other places.

let obj = {
        foo: super.foo
    }
    // used on properties

    let obj1 = {
        foo: () => super.foo
    }
    // used in method

    let obj2 = {
        foo: function () {
            return super.foo
        }
    }

The above three will report an error, because for the js engine, super is not used at all.

The first is used in attributes
The second and third are used in functions, but assigned to the foo property

Now only the shorthand object method can be confirmed by the js engine, which defines the method of the object

Inside the js engine, super.foo is equivalent to
Object.getPrototypeOf(this).foo attribute or Object.getPrototypeOf(this).foo.call(this) method.

    let father = {
        name: 'person',
        sayHello () {
            return this.name
        }
    }

    let son = {
        name: 'son',
        sayHi () {
            return super.sayHello();
        }
    }
    
    Object.setPrototypeOf(son, father);
    son.sayHi();
    // 'son'

The above code is divided into three steps

  • The first step is to call the son.sayHi function and return the prototype's sayHello method
  • In the second step, the father itself calls the sayHello method and returns this.name
  • The third part of this.name at this time, because it is executed in the environment of son, this points to son, so the print result is 'son'

If it is changed to father.sayHello(), it will be different

Object spread operator

The spread operator (…) in the array is handy, and the writing method of the object is basically similar to the function

destructuring assignment

Object destructuring is used to take out all the values ​​of an object (traversable), and properties that have not been read, assign them to another object, all their keys and values, and finally form a new object

    let {x, y, ...z} = {x: 1, y: 2, z: 3, u: 10, n: 20}
    x // 1
    y // 2
    z // {z: 3, u: 10, n: 20}

In the above code, only z is the successfully destructed object, x and y belong to the read values, z will copy the keys and values ​​that xy did not read, and return a new array

The right side of the destructuring assignment equal sign must be an object, otherwise an error will be reported

    let {...y} = undefined // error
    let {...n} = null        // error 

Destructuring assignment must be the last parameter, otherwise an error will be reported

    let {...x, y} = obj; // error
    let {x, ...y, z} = obj; // error

Destructuring assignment is a shallow copy, that is, if the copied value is an array, object, function, then the copy is actually a reference, not a copy

    let obj = { a: {b: 1} }
    let {...newObj} = obj
    newObj.a.b = 2;
    obj.a.b
    // 2

The above code copies an object. Since destructuring is only a shallow copy, it points to the same pointer address, causing the data of the new address to change, and the data pointed to by the original address to change, because they point to the same room.

Destructuring assignment cannot get the properties of the prototype

    let a1 = {bar: 'bar'}
    let a2 = {foo: 'foo'}
    Object.setPrototypeOf(a1, a2)
    let {...a3} = a2
    a3
    // {foo: 'foo'}
    a3.bar
    // undefined

The above code a2 inherits a1, and a3 copies a2, but the destructuring assignment cannot get the prototype attribute, resulting in a3 not getting the attribute of the ancestor of a1

// create a prototype object of obj
    let obj = Object.create({x: 1, y: 2})
    // Add a z property to itself
    obj.z = 3
    
    let {x, ...newObj} = obj
    // x is a pure destructuring assignment, so inherited properties can be read.
    // y and z are spread operator destructuring assignments, which can only read the properties of the object itself
    let {y, z} = newObj
    // y is an inherited property, so it cannot be obtained in newObj.
    x // 1
    y // undefined
    z // 3

Some people may think it can save trouble, so they will write like this

    let {x, ...{y, z}} = obj
    // error

ES6 stipulates that in a variable declaration statement, if destructuring assignment is used, the spread operator must be followed by a variable name, not a destructuring assignment expression.

One use of destructuring assignment is to expand the parameters of a function, pass it to another function, or introduce other operations

function test (a, b) {
        return a + b
    }
    
    function bar (x, y, ...anther) {
        // use xy for internal operations
        // Export the spread operator, provide it to another function, or do something else
        console.log(x * y);
        return test(anther);
        
    }

    bar(1, 3, 9, 9)

spread operator

The spread operator of the object is used to take out all traversable properties of the parameter object and copy them to the current object

    let z = {x: 1, y: 2}
    let n = {...z}
    n // {x: 1, y: 2}

Since arrays are special objects, the object spread operator can also be used on arrays

    let foo = {...['x', 'y', 'z']}
    foo 
    // {0: 'x', 1: 'y', 2: 'z'}

Has no effect if the spread operator is followed by an empty object

    let obj = {...{}, a: 1}
    obj
    // {a: 1}

If it is not an object, it will be converted to an object

// {...Object(1)} is equivalent to
    {...1}
    // {}

The above will call the wrapper class and convert it to Number(1), but the object itself does not have any properties, so it returns empty

There are also some types

    {...true} // {}
    {...undefined} // {}
    {...null} // {}
    {...'es6'} // {0: 'e', 1: 's', 2: '6'}

The first three will return an empty object, and the last string, since the string will be converted to an array-like, returns a set of indexed objects

The spread operator for objects is equivalent to using the Object.assign() method

let aClone = {...a};

    // Equivalent to
    let aClone = Object.assign({}, a)

The above only copies the object instance properties. If you want to copy the prototype properties, you need to write like this

// fn1 
    let clone1 = {
        // Get the prototype of obj and assign it to its own proto, which points to the prototype of obj 
        __proto__ : Object.getPrototypeOf(obj),
        // Assign the instance attribute of obj to its own prototype, copy the instance attribute and prototype attribute of obj
        ...obj
    }

    // fn2
    let clone2 = Object.assign(
        // Create an object whose prototype is the obj prototype object
        Object.create(Object.getPrototypeOf(obj)),
        // put obj into the above object
        obj
    )
    
    // fn3
    let clone3 = Object.create(
        // Create a reference object, which is the prototype object of obj
        Object.getPrototypeOf(obj),
        // Pass all enumerable properties of obj to the reference object
        Object.getOwnPropertyDescriptors(obj)
    )

spread operator, which can combine objects

    let obj1 = {...a, ...b}
    let obj2 = Object.assign({}, a, b)

If there is a user-defined property after the spread operator, the property with the same name inside the spread operator will be overwritten

let obj = {...a, x: 1, y: 2}
    // Equivalent to
    let obj1 = {...a, ...{x: 1, y: 2}}
    // Equivalent to
    let x = 1, y = 2, obj2 = {...a, x, y}
    // Equivalent to
    let obj3 = Object.assign({}, a, {x: 1, y: 2})

In the above code, the x and y properties in the a object will be overwritten by the xy behind a when they are copied to the new object

This makes it easy to change some of the existing properties

    let newVersion = {
        ...oldVersion,
        name: 'new Version'
    }

The old version name above will be replaced

If the custom property is placed before the spread operator, it becomes the default property value for the new object

    let obj = {x: 1, y: 2, ...a}
    ==
    let obj1 = Object.assign({}, {x: 1, y: 2}, a
    == 
    let obj2 = Object.assign({x: 1, y: 2}, a)

The object spread operator can also be followed by an expression

    let obj = {
        ...(x > 1 : {name: 'wei'} ? {}),
        bar: 'foo'
    }

If there is a value function get in the parameter object of the spread operator, it will automatically execute

    let obj = {
        get foo () {
            console.log(1)
        }
    }
    
    let newObj = {...obj}
    // 1

The above code, the statement of the get function will be executed after the expansion is completed

Recommended Today

Windows terminal uses nushell+oh-my-posh to beautify

First look at the renderings Please point out if the steps are wrong. Install nushell from github Download from github releaseRelease 0.66.2 · nushell/nushell (github.com) github release download speed may be slow, you can download it hereDownload (serctl.com) Install windows terminal in microsoft store Install oh-my-posh on Microsoft Store Winget can also be used, but […]