Object

Overview

Generation method

Object is the core concept of JavaScript language and the most important data type.

What is an object? Simply put, an object is a set of "key-value pairs", which is an unordered collection of composite data.

var obj = {
  foo: "Hello",
  bar: "World",
};

In the above code, the curly braces define an object, which is assigned to the variable obj, so the variable obj points to an object. The object contains two key-value pairs (also called two "members"). The first key-value pair is foo:'Hello', where foo is the "key name" (name of the member), The string Hello is the "key value" (the value of the member). Use a colon to separate the key name and the key value. The second key-value pair is bar:'World', bar is the key name, and World is the key value. Use a comma to separate the two key-value pairs.

Key name

All key names of objects are strings (ES6 also introduces Symbol values ​​that can also be used as key names), so you can put them in or out of quotation marks. The above code can also be written as follows.

var obj = {
  foo: "Hello",
  bar: "World",
};

If the key name is a numeric value, it will be automatically converted to a string.

var obj = {
  1: "a",
  3.2: "b",
  1e2: true,
  1e-2: true,
  0.234: true,
  0xff: true,
};

obj;
// Object {
// 1: "a",
// 3.2: "b",
// 100: true,
// 0.01: true,
// 0.234: true,
// 255: true
//}

obj["100"]; // true

In the above code, although all the key names of the object obj look like numeric values, they are actually automatically converted into strings.

If the key name does not meet the conditions of the identification name (for example, the first character is a number, or contains a space or operator), and it is not a number, you must add quotation marks, otherwise an error will be reported.

// report an error
var obj = {
  1p:'Hello World'
};

// No error
var obj = {
  '1p':'Hello World',
  'h w':'Hello World',
  'p+q':'Hello World'
};

The three key names of the above objects do not meet the conditions of the identifier name, so quotes must be added.

Each key name of an object is also called a "property", and its "key value" can be any data type. If the value of an attribute is a function, this attribute is usually called a "method", and it can be called like a function.

var obj = {
  p: function (x) {
    return 2 * x;
  },
};

obj.p(1); // 2

In the above code, the property p of the object obj points to a function.

If the value of the attribute is still an object, a chained reference is formed.

var o1 = {};
var o2 = { bar: "hello" };

o1.foo = o2;
o1.foo.bar; // "hello"

In the above code, the property foo of the object o1 points to the object o2, and the property of o2 can be referenced in a chain.

The attributes of the object are separated by commas, and the trailing comma can be added after the last attribute or not.

var obj = {
  p: 123,
  m: function () {... },
}

In the above code, the comma after the m attribute can be used or not.

Attributes can be created dynamically and do not have to be specified when the object is declared.

var obj = {};
obj.foo = 123;
obj.foo; // 123

In the above code, directly assign a value to the foo property of the obj object, and as a result, the foo property is created at runtime.

Object reference

If different variable names point to the same object, they are all references to this object, which means they point to the same memory address. Modifying one of the variables will affect all other variables.

var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a; // 1

o2.b = 2;
o1.b; // 2

In the above code, o1 and o2 point to the same object, so add an attribute to any one of the variables, and the other variable can read and write the attribute.

At this point, if you cancel a variable's reference to the original object, the other variable will not be affected.

var o1 = {};
var o2 = o1;

o1 = 1;
o2; // {}

In the above code, o1 and o2 point to the same object, and then the value of o1 becomes 1, which will not affect o2, and o2 still points to the original object.

However, this kind of reference is limited to objects, if two variables point to the same primitive type value. Then, variables are copies of values ​​at this time.

var x = 1;
var y = x;

x = 2;
y; // 1

In the above code, when the value of x changes, the value of y does not change, which means that y and x do not point to the same memory address.

Expression or statement?

Objects are represented by curly braces, which leads to a question: if the beginning of a line is a curly brace, is it an expression or a statement?

{
  foo: 123;
}

When the JavaScript engine reads the above line of code, it may have two meanings. The first possibility is that this is an expression that represents an object containing the attribute of foo; the second possibility is that this is a statement that represents a code block with a label foo that points to the expression 123.

In order to avoid this ambiguity, the approach of the JavaScript engine is that if it encounters this situation, it cannot be determined whether it is an object or a code block, and it is interpreted as a code block.

{
  console.log(123);
} // 123

The above statement is a code block, and can only be executed if it is interpreted as a code block.

If you want to interpret it as an object, it is best to put parentheses before the curly brace. Because inside the parentheses can only be expressions, make sure that the braces can only be interpreted as objects.

({ foo: 123 }) // correct
({ console.log(123) }) // report an error

This difference is most clearly reflected in the eval statement (which is used to evaluate a string).

eval("{foo: 123}"); // 123
eval("({foo: 123})"); // {foo: 123}

In the above code, if there are no parentheses, eval will understand it as a code block; after adding parentheses, it will be understood as an object.

Attribute operation

Attribute reading

There are two ways to read the properties of an object, one is to use the dot operator, and the other is to use the square bracket operator.

var obj = {
  p: "Hello World",
};

obj.p; // "Hello World"
obj["p"]; // "Hello World"

The above code uses the dot operator and the square bracket operator to read the attribute p.

Please note that if you use the square bracket operator, the key name must be enclosed in quotation marks, otherwise it will be treated as a variable.

var foo = "bar";

var obj = {
  foo: 1,
  bar: 2,
};

obj.foo; // 1
obj[foo]; // 2

In the above code, when referencing the foo property of the object obj, if the dot operator is used, foo is a string; if the square bracket operator is used but no quotation marks are used, then foo is a variable pointing to The string bar.

Expressions can also be used inside square bracket operators.

obj["hello" + "world"];
obj[3 + 3];

The number keys can be without quotation marks, because they will be automatically converted into strings.

var obj = {
  0.7: "Hello World",
};

obj["0.7"]; // "Hello World"
obj[0.7]; // "Hello World"

In the above code, the number key 0.7 of the object obj can be added or not in quotation marks, because it will be automatically converted to a string.

Note that the dot operator cannot be used for numeric key names (because it will be treated as a decimal point), only the square bracket operator can be used.

var obj = {
  123:'hello world'
};

obj.123 // report an error
obj[123] // "hello world"

The first expression of the above code uses the dot operator for the numeric key name 123, and the result is an error. The second expression uses the square bracket operator, and the result is correct.

Assignment of attributes

The dot operator and the square bracket operator can be used not only to read values, but also to assign values.

var obj = {};

obj.foo = "Hello";
obj["bar"] = "World";

In the above code, the dot operator and the square bracket operator are used to assign values ​​to attributes.

JavaScript allows "post-binding" of properties, that is, you can add properties at any time. It is not necessary to define properties when defining objects.

var obj = { p: 1 };

// Equivalent to

var obj = {};
obj.p = 1;

View of attributes

To view all the properties of an object itself, you can use the Object.keys method.

var obj = {
  key1: 1,
  key2: 2,
};

Object.keys(obj);
// ['key1','key2']

Attribute deletion: delete command

The delete command is used to delete the attributes of an object, and returns true after successful deletion.

var obj = { p: 1 };
Object.keys(obj); // ["p"]

delete obj.p; // true
obj.p; // undefined
Object.keys(obj); // []

In the above code, the delete command deletes the p attribute of the object obj. After deleting, reading the p property again will return undefined, and the return value of the Object.keys method no longer includes this property.

Note that to delete a non-existent attribute, delete does not report an error, and returns true.

var obj = {};
delete obj.p; // true

In the above code, the object obj does not have a p attribute, but the delete command still returns true. Therefore, it cannot be determined that a certain attribute exists based on the result of the delete command.

There is only one case, the delete command will return false, that is, the attribute exists and cannot be deleted.

var obj = Object.defineProperty({}, "p", {
  value: 123,
  configurable: false,
});

obj.p; // 123
delete obj.p; // false

In the above code, the p property of the object obj cannot be deleted, so the delete command returns false (For the introduction of the Object.defineProperty method, please refer to the "Object Object" chapter of the Standard Library ).

In addition, it should be noted that the delete command can only delete the properties of the object itself, and cannot delete the inherited properties (refer to the "Object-Oriented Programming" chapter for inheritance).

var obj = {};
delete obj.toString; // true
obj.toString; // function toString() {[native code]}

In the above code, toString is a property inherited by the object obj. Although the delete command returns true, the property is not deleted and still exists. This example also shows that even if delete returns true, the property may still read a value.

Does the attribute exist: in operator

The in operator is used to check whether the object contains a certain attribute (note that the check is the key name, not the key value), if it contains, it returns true, otherwise it returns false. On the left is a string representing the name of the property, and on the right is an object.

var obj = { p: 1 };
"p" in obj; // true
"toString" in obj; // true

One problem with the in operator is that it cannot identify which properties are the object itself and which properties are inherited. Just like in the code above, the object obj itself does not have a toString property, but the in operator will return true because this property is inherited.

At this time, you can use the object's hasOwnProperty method to determine whether it is a property of the object itself.

var obj = {};
if ("toString" in obj) {
  console.log(obj.hasOwnProperty("toString")); // false
}

Traversal of properties: for...in loop

The for...in loop is used to traverse all the attributes of an object.

var obj = { a: 1, b: 2, c: 3 };

for (var i in obj) {
  console.log("Key name:", i);
  console.log("Key value:", obj[i]);
}
// Key name: a
// Key value: 1
// Key name: b
// Key value: 2
// Key name: c
// Key value: 3

There are two points to note when using the for...in loop.

-It traverses all the enumerable properties of the object, and skips the non-traversable properties. -It not only traverses the properties of the object itself, but also traverses inherited properties.

For example, all objects inherit the toString property, but the for...in loop will not traverse to this property.

var obj = {};

// toString property exists
obj.toString; // toString() {[native code]}

for (var p in obj) {
  console.log(p);
} // No output

In the above code, the object obj inherits the toString property, which will not be traversed by the for...in loop, because it is "not traversable" by default. For the traversability of object properties, see the introduction in the Object chapter in the "Standard Library" chapter.

If the inherited property is traversable, it will be traversed by the for...in loop. However, under normal circumstances, you only want to traverse the properties of the object itself, so when using for...in, you should use the hasOwnProperty method in combination to determine whether a certain property belongs to the object itself. Attributes.

var person = { name: "Old Zhang" };

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name

with statement

The format of the with statement is as follows:

with (object) {
  Statement;
}

Its function is to provide some writing convenience when operating multiple attributes of the same object.

// Example 1
var obj = {
  p1: 1,
  p2: 2,
};
with (obj) {
  p1 = 4;
  p2 = 5;
}
// Equivalent to
obj.p1 = 4;
obj.p2 = 5;

// Example 2
with (document.links[0]) {
  console.log(href);
  console.log(title);
  console.log(style);
}
// Equivalent to
console.log(document.links[0].href);
console.log(document.links[0].title);
console.log(document.links[0].style);

Note that if there is a variable assignment operation inside the with block, it must be an existing property of the current object, otherwise a global variable of the current scope will be created.

var obj = {};
with (obj) {
  p1 = 4;
  p2 = 5;
}

obj.p1; // undefined
p1; // 4

In the above code, the object obj does not have a p1 attribute. Assigning a value to p1 is equivalent to creating a global variable p1. The correct way of writing should be to define the property p1 of the object obj first, and then operate it in the with block.

This is because the with block does not change the scope, and its interior is still the current scope. This causes a big drawback of the with statement, which is that the binding object is not clear.

with (obj) {
  console.log(x);
}

From the code block above, it is impossible to determine whether x is a global variable or an attribute of the object obj. This is very unfavorable for debugging and modularization of the code, and the compiler cannot optimize this code, and can only be left to run-time judgment, which slows down the running speed. Therefore, it is recommended not to use the with statement, and consider using a temporary variable instead of with.

with (obj1.obj2.obj3) {
  console.log(p1 + p2);
}

// can be written as
var temp = obj1.obj2.obj3;
console.log(temp.p1 + temp.p2);