[Revisiting the basics] 12. Use objects

Keywords: Javascript Attribute REST JSON

This article is the twelfth in a review of the basic series.
Today's feeling: need to sum up 2018.

These days, a heavy cold and fever, rest at home for several days, injuries.

Series catalogues:

This chapter reviews the use of objects in JS, which is the focus.

Pre-knowledge:
JavaScrip is designed as a simple object-based paradigm.
Object is a collection of a series of attributes. An attribute contains a key and a value, also known as a key-value pair.
If the value of an attribute is a function, it is called a method.

An object can have many ways, just like a cup, it can have attributes such as color, weight, shape and so on.

Be careful:
Object name, case sensitive.

1. Create objects

This article mainly reviews the use of objects. As for the creation of objects, here is a brief introduction to the common methods of creating objects.

Method 1:

let obj = new Object();  // Declare an empty object

Method 2:

let obj = {};            // Declare an empty object

The name above is an attribute in the object obj, and the corresponding value is "leo".

2. Setting Object Properties and Access Properties

There are also two ways to set object properties:
Direct settings:

let obj = {};
obj.name = "leo";        // Adding attributes and values to objects

Settings at creation time:

let obj = {name : 'leo'};
obj.name = "leo";        // Adding attributes and values to objects

When an object defines an attribute, we can access it with a dot symbol (.).

obj.name; // "leo"

Be careful:

  • Another way of writing attributes:
let obj = {};
let n = "name";
obj[n] = "leo";   // Property names use variables
obj["height"] = 188;     // Character string
obj["age" + "Is"] = 15;  // StringBuilder
console.log(obj);
/*
obj = {
    ageIs: 15
    height: 188
    name: "leo"
}
*/
  • The value of an attribute can also be a method:
let obj = {};
obj.getTime = function(){
    return new Date();
}
obj.getTime();  // Method of accessing attributes
//Wed Jan 02 2019 21:07:59 GMT+0800 (China Standard Time) 

3. Enumerate all attributes of an object

from ECMAScript 5 Initially, there are three native ways to list or enumerate the attributes of an object:

3.1 for...in cycle

This method accesses an object and all enumerable attributes in its prototype chain in turn.

let obj = {a:1, b:2, c:3};
for (let i in obj) {
  console.log("obj." + i + " = " + obj[i]);
}
/*
"obj.a = 1"
"obj.b = 2"
"obj.c = 3"
*/

3.2 Object.keys(o)

This method returns an array of names of all attributes of an object o itself (excluding those in the prototype).

let obj = {a:1, b:2, c:3};
let arr = Object.keys(obj);
console.log(arr);
/*
["a", "b", "c"]
*/
arr.forEach(function(val,index,array){
    console.log("key:" + val+",val:"+obj[val])
})
/*
key:a,val:1
key:b,val:2
key:c,val:3
*/

3.3 Object.getOwnPropertyNames(o)

This method returns an array containing the names of all attributes (whether enumerable or not) owned by object o.

let obj = {a:1, b:2, c:3};
let arr = Object.getOwnPropertyNames(obj);
console.log(arr);
/*
["a", "b", "c"]
*/
arr.forEach(function(val,index,array){
    console.log("key:" + val+",val:"+obj[val])
})
/*
key:a,val:1
key:b,val:2
key:c,val:3
*/

4.ES6 Added: Object Expansion

Simple Representation of 4.1 Attributes

let a = 'a1';
let b = { a };  // b => { a : 'a1' }
// Equate to
let b = { a : a };

function f(a, b){
    return {a, b}; 
}
// Equate to
function f (a, b){
    return {a:a ,b:b};
}

let a = {
    fun () {
        return 'leo';
    }
}
// Equate to
let a = {
    fun : function(){
        return 'leo';
    }
}

4.2 Property Name Expressions

JavaScript provides two ways to define the properties of an object.

// Method 1 identifier as attribute name
a.f = true;

// Method 2 string as attribute name
a['f' + 'un'] = true;

Extended also are:

let a = 'hi leo';
let b = {
    [a]: true,
    ['a'+'bc']: 123,
    ['my' + 'fun'] (){
        return 'hi';
    }
};
// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi'
// b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; }

Be careful:
Property name expressions cannot be used in conjunction with concise representations, otherwise errors are reported.

// Report errors
let a1 = 'aa';
let a2 = 'bb';
let b1 = {[a1]};

// Correct
let a1 = 'aa';
let b1 = { [a1] : 'bb'};

4.3 Object.is()

Object.is() is used to compare whether two values are strictly equal. In ES5, only the equal operator (==) and the strict equal operator (===) can be used to compare, but they all have shortcomings. The former automatically converts the data type, the latter's NaN is not equal to itself, and + 0 equals - 0.

Object.is('a','a');   // true
Object.is({}, {});    // false

// ES5
+0 === -0 ;           // true
NaN === NaN;          // false

// ES6
Object.is(+0,-0);     // false
Object.is(NaN,NaN);   // true

4.4 Object.assign()

The Object.assign() method is used to merge objects and copy all enumerable attributes of the original object to the target object.
Basic usage:
The first parameter is the target object, and the latter is the source object.

let a = {a:1};
let b = {b:2};
Object.assign(a,b);  // a=> {a:1,b:2}

Be careful:

  • If the target object has the same name as the source object, the latter attribute will override the former one.
let a = {a:1, b:2};
let b = {b:3, c:4};
Object.assign(a, b); // a => {a:1, b:3, c:4}
  • If there is only one parameter, it is returned.
let a = {a:1};
Object.assign(a) === a;  // true
  • If the parameter is not an object, it is first converted to an object and then returned.
typeof Object.assign(2); // 'object'
  • Because undefined or NaN cannot be converted to objects, errors are reported as parameters.
Object.assign(undefined) // Report errors
Object.assign(NaN);      // Report errors
  • Object.assign() implements a shallow copy.

Object.assign() is copied to get a reference to this object. Any change in this object will be reflected on the target object.

let a = {a: {b:1}};
let b = Object.assign({},a);
a.a.b = 2;
console.log(b.a.b);  // 2
  • The array is treated as an object. The key name is the array subscript, and the key value is the corresponding value of the array subscript.
Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]

5.ES8 Added: Object.values(), Object.entries()

The newly added Object.values() and Object.entries() in ES7 are similar to the previous Object.keys(), returning the array type.
Look back at Object.keys():

var a = { f1: 'hi', f2: 'leo'};
Object.keys(a); // ['f1', 'f2']

5.1 Object.values()

Returns an array whose members are the key values of all traversable attributes of the parameter object itself (excluding inheritance).

let a = { f1: 'hi', f2: 'leo'};
Object.values(a); // ['hi', 'leo']

If the parameter is not an object, an empty array is returned:

Object.values(10);   // []
Object.values(true); // []

5.2 Object.entries()

Returns an array whose members are key-value pairs of all traversable attributes of the parameter object itself (excluding inheritance).

let a = { f1: 'hi', f2: 'leo'};
Object.entries(a); // [['f1','hi'], ['f2', 'leo']]
  • Purpose 1:

Traverse object properties.

let a = { f1: 'hi', f2: 'leo'};
for (let [k, v] of Object.entries(a)){
    console.log(
        `${JSON.stringfy(k)}:${JSON.stringfy(v)}`
    )
}
// 'f1':'hi'
// 'f2':'leo'
  • Purpose 2:

Turn objects into real Map structures.

let a = { f1: 'hi', f2: 'leo'};
let map = new Map(Object.entries(a));

Manually implement the Object.entries() method:

// Generator function implementation:  
function* entries(obj){
    for (let k of Object.keys(obj)){
        yield [k ,obj[k]];
    }
}

// Non-Generator function implementation:
function entries (obj){
    let arr = [];
    for(let k of Object.keys(obj)){
        arr.push([k, obj[k]]);
    }
    return arr;
}

6.ES8 Added: Object. getOwn Property Descriptors ()

Previous Object.getOwnPropertyDescriptor method will return the description object of an object property. The new Object.getOwnPropertyDescriptors() method returns the description object of all its own attributes (non-inherited attributes) of the specified object. The attribute name of all the original object is the attribute name of the object, and the corresponding attribute value is the description object of the attribute.

let a = {
    a1:1,
    get f1(){ return 100}
}
Object.getOwnPropetyDescriptors(a);
/*
{ 
    a:{ configurable:true, enumerable:true, value:1, writeable:true}
    f1:{ configurable:true, enumerable:true, get:f, set:undefined}
}
*/

Realization principle:

function getOwnPropertyDescriptors(obj) {
  const result = {};
  for (let key of Reflect.ownKeys(obj)) {
    result[key] = Object.getOwnPropertyDescriptor(obj, key);
  }
  return result;
}

This method is introduced to solve the problem that Object.assign() can not copy the get and set attributes correctly.

let a = {
    set f(v){
        console.log(v)
    }
}
let b = {};
Object.assign(b, a);
Object.a(b, 'f');
/*
f = {
    configurable: true,
    enumable: true,
    value: undefined,
    writeable: true
}
*/

value is undefined because the Object.assign method does not copy the get and set methods. It uses getOwn Property Descriptors and Object.defineProperties to achieve the correct copy:

let a = {
    set f(v){
        console.log(v)
    }
}
let b = {};
Object.defineProperties(b, Object.getOwnPropertyDescriptors(a));
Object.getOwnPropertyDescriptor(b, 'f')
/*
    configurable: true,
    enumable: true,
    get: undefined,
    set: function(){...}
*/

Object. getOwnProperty Descriptors method cooperates with Object.create method to clone object attributes into a new object and realize shallow copy.

const clone = Object.create(Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj));

// perhaps
const shallowClone = (obj) => Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);

7.ES9 Added: Object Extension Operator

7.1 introduction

The extension operator of an object, namely the Rest/Spread attribute of an object, can be used to deconstruct and assign values from an object and search key pairs to a specified object, similar to the expansion operator of an array:

let  {x, y, ...z} = {x:1, y:2, a:3, b:4};
x;  // 1
y;  // 2
z;  // {a:3, b:4} 

The deconstruction assignment of an object requires that the right side of the equals sign must be an object, so if the right side of the equals sign is undefined or null, an error will be reported that cannot be converted into an object.

let {a, ...b} = null;      // Runtime error reporting
let {a, ...b} = undefined; // Runtime error reporting

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

let {...a, b, c} = obj;     // Syntax error
let {a, ...b, c} = obj;     // Syntax error

Be careful:

  • 1. Deconstruction assignment is a shallow copy.
let a = {a1: {a2: 'leo'}};
let {...b} = a;
a.a1.a2 = 'leo';
b.a1.a2 = 'leo';
  • 2. The deconstruction assignment of the extended operator cannot copy the attributes inherited from the prototype object.
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3;    // { b: 2 }
o3.a;  // undefined

7.2 Use scenarios

  • 1. Remove all traversable attributes of the parameter object and copy them to the current object.
let a = { a1:1, a2:2 };
let b = { ...a };
b;   // { a1:1, a2:2 }

// Similar to Object.assign method
  • 2. Merge two objects.
let a = { a1:1, a2:2 };
let b = { b1:11, b2:22 };
let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22}
// Equate to
let ab = Object.assign({}, a, b);
  • 3. Place the custom attributes behind the extension operators to override the original identical-name attributes of the object.
let a = { a1:1, a2:2, a3:3 };
let r = { ...a, a3:666 };   
// r {a1: 1, a2: 2, a3: 666}

// Equate to
let r = { ...a, ...{ a3:666 }};
// r {a1: 1, a2: 2, a3: 666}

// Equate to
let r = Object.assign({}, a, { a3:666 });
// r {a1: 1, a2: 2, a3: 666}
  • 4. Placing custom attributes in front of expansion operators will become the default value for setting new objects.
let a = { a1:1, a2:2 };
let r = { a3:666, ...a };
// r {a3: 666, a1: 1, a2: 2}

// Equate to
let r = Object.assign({}, {a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}

// Equate to
let r = Object.assign({a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}
  • 5. Expansion operators can be followed by expressions.
let a = {
    ...(x>1? {a:!:{}),
    b:2
}
  • 6. If the extension operator is followed by an empty object, it has no effect.
{...{}, a:1};  // {a:1}
  • 7. If the parameter is null or undefined, it is ignored and no error is reported.
let a = { ...null, ...undefined }; // No mistake
  • 8. If there is a get function, it will be executed.
// It won't print because the f attribute is just defined and not executed
let a = {
    ...a1,
    get f(){console.log(1)}
}

// It will print because f executes.
let a = {
    ...a1,
    ...{
        get f(){console.log(1)}
    }
}

Reference material

1.MDN Usage Objects
2.W3school JavaScript object

This is the end of this section.

Author Ping An Wang
E-mail pingan8787@qq.com
Blog www.pingan8787.com
WeChat pingan8787
Daily article recommendation https://github.com/pingan8787...
ES Brochure es.pingan8787.com

Posted by wheakory on Wed, 15 May 2019 10:15:35 -0700