As a front end white, I don’t know if you have the same problem as me. After reading the analysis of an interview question, I thought I could at that time, but I couldn’t look again two days later. Blindly pursuing all kinds of new technology, I feel like I can do everything, but I can’t do it once I get started… After reflecting on the pain, I finally realized the problem and began to focus on the training of basic skills. Nearly half a year to read through (in fact is swallowed) “JavaScript Advanced Programming”, “You don’t know JavaScript on, in, under” and other books, this series of articles is my reading process of knowledge points in some summary. Like the students remember to help me point 😁.

Understand wang series (1) thoroughly understand JavaScript function execution mechanism understand Wang series (2) thoroughly understand JavaScript scope understand Wang series (3) thoroughly understand JavaScript objects understand Wang series (4) thoroughly understand JavaScript classes To understand this, we need to understand JavaScript data types. To understand this, we need to understand JavaScript data types Complete JavaScript type conversion

In JavaScript, if the first three bits of binary are all zeros, it will be considered as an object. The binary representation of null is all zeros, and naturally the first three bits are also zeros, so typeof will return “object”.

Built-in objects

• String

• Number

• Boolean

• Object

• Function

• Array

var strPrimitive = "I am a string"; 
typeof strPrimitive; // "string" 
strPrimitive instanceof String; // false 
var strObject = new String( "I am a string" ); 
typeof strObject; // "object" 
strObject instanceof String; // true 
// Check sub-type objects
Object.prototype.toString.call( strObject ); // [object String]
Copy the code

The original value “I am a string” is not an object, it’s just a literal, and it’s an immutable value. If you want to do something on this literal, such as get the length, access one of its characters, etc., you need to convert it to a String. Fortunately, the language automatically converts String literals to a String when necessary, which means we don’t need to explicitly create an object.

var strPrimitive = "I am a string"; 
console.log( strPrimitive.length ); / / 13
console.log( strPrimitive.charAt( 3));// "m"
Copy the code

Properties or methods can be accessed directly on String literals because the engine automatically converts literals to strings, so the properties and methods can be accessed.

Using a method like 42.359.tofixed (2), the engine converts 42 to a new Number(42). The same is true for Boolean literals

Content of 2.

Stored inside the object container are the names of these properties, which act like Pointers (technically, references) to where the values are really stored

var myObject = { 
    a: 2 
}; 
myObject.a; / / 2
myObject["a"]; / / 2
Copy the code

.a syntax is often referred to as “attribute access” and [“a”] syntax is often referred to as “key access”

The main difference between the two grammas is that the. Operator requires that the attribute name conform to the naming convention of the identifier, whereas [“..”] The syntax can accept any UTF-8/Unicode string as the property name. For example, if you want a reference named “SuperFun!” You must use [” super-fun!”] Syntax to access

In objects, property names are always strings. If you use a value other than string (literal) as the property name, it will first be converted to a string. Even the numbers are no exception

2.1 Name of the computable attribute

var prefix = "foo"; 
var myObject = { 
    [prefix + "bar"] :"hello", 
    [prefix + "baz"] :"world" 
};
Copy the code

2.2 Properties and Methods

Property access returns a function that is no different from any other function (except for the implicit binding to this that may occur)

function foo() { 
    console.log( "foo" ); 
} 
var someFoo = foo; // A variable reference to foo
var myObject = { 
    someFoo: foo 
}; 
foo; // function foo(){.. }
someFoo; // function foo(){.. }
myObject.someFoo; // function foo(){.. }
Copy the code

SomeFoo and myObject.someFoo are just different references to the same function, and do not mean that the function is special or “belongs” to an object. If foo() is defined with an internal reference to this, the only difference between the two function references is that this in myObject.somefoo is implicitly bound to an object.

Even if you declare a function expression in the literal form of an object, the function does not “belong” to the object — they are just multiple references to the same function object.

var myObject = {
    foo: function() { 
        console.log( "foo"); }};var someFoo = myObject.foo; 
someFoo; // function foo(){.. }
myObject.foo; // function foo(){.. }
Copy the code

2.3 an array

If you try to add a property to an array, but the property name “looks” like a number, it becomes a numeric index (thus modifying the contents of the array rather than adding a property) :

var myArray = [ "foo".42."bar" ]; 
myArray["3"] = "baz"; 
myArray.length; / / 4
myArray[3]; // "baz"
Copy the code

2.4 Copying Objects

function anotherFunction() { / *.. * / } 
var anotherObject = { 
    c: true
}; 
var anotherArray = []; 
var myObject = { 
    a: 2.b: anotherObject, // Reference, not copy!
    c: anotherArray, // Another quote!
    d: anotherFunction 
};
anotherArray.push( anotherObject, myObject );
Copy the code

For a shallow copy, the value of a in the new object copies the value of a in the old object, which is 2, but the properties B, C, and d in the new object are just three references to the same objects that b, C, and d referenced in the old object. For deep replication, anotherObject and anotherArray are copied in addition to myObject. The problem is that anotherArray references both anotherObject and myObject, so you need to copy myObject again, which will cause an infinite loop due to the circular reference.

  1. With a JSON string
var newObj = JSON.parse( JSON.stringify( someObj ) );
Copy the code
  1. Object.assign(..)

Object.assign(..) The first argument to the method is the target object, which can be followed by one or more source objects. It iterates over all the owned keys of one or more source objects (see the code below) and copies them (using the = operator assignment) to the target object, returning the target object

3.5 Attribute descriptors

Writable, Enumerable, and freely configurable

  1. writable

The unwritable attribute is invalid in non-strict mode, and an error is reported in strict mode

var myObject = {}; 
Object.defineProperty( myObject, "a", { 
    value: 2.writable: false.// Do not write!
    configurable: true.enumerable: true}); myObject.a =3; 
myObject.a; / / 2
Copy the code
"use strict"; 
var myObject = {}; 
Object.defineProperty( myObject, "a", { 
    value: 2.writable: false.// Do not write!
    configurable: true.enumerable: true}); myObject.a =3; // TypeError
Copy the code

In simple terms, writable:false means that the property is immutable, so you define an empty setter for the operation. Strictly speaking, to be consistent with writable:false, your setter should raise a TypeError when called

  1. Configurable

As long as the property is configurable, you can use defineProperty(..) Method to modify the property descriptor. Conversely, when the property is not configurable, use defineProperty(..) Method to modify a property

One small exception is that you can change the writable state from true to false even if the property is freely :false, but it does not change from false to true.

In addition to being unable to be modified, a different :false information also disables the removal of this property

var myObject = { 
    a:2 
}; 
myObject.a; / / 2
delete myObject.a; 
myObject.a; // undefined 
Object.defineProperty( myObject, "a", { 
    value: 2.writable: true.configurable: false.enumerable: true}); myObject.a;/ / 2
delete myObject.a; 
myObject.a; / / 2
Copy the code
  1. Enumerable

This descriptor controls whether an attribute appears in an object’s attribute enumeration, such as for.. In circulation.

3.6 invariance

All methods are created to be shallow invariants, that is, they only affect the target object and its immediate properties. If the target object references other objects (arrays, objects, functions, etc.), the contents of the other objects are not affected and are still mutable

myImmutableObject.foo; / / [1, 2, 3]
myImmutableObject.foo.push( 4 ); 
myImmutableObject.foo; / / [1, 2, 3, 4]
Copy the code
  1. Object is constant

With the writable:false and different :false functions, you can create a real constant property that cannot be modified, redefined, or deleted.

var myObject = {}; 
Object.defineProperty( myObject, "FAVORITE_NUMBER", { 
    value: 42.writable: false.configurable: false});Copy the code
  1. Prohibit extensions

If you want to prevent a new generic from being added to an Object and keep the existing generic, you can use Object. Prevent Extensions(..) :

var myObject = { 
    a:2 
}; 
Object.preventExtensions( myObject ); 
myObject.b = 3; 
myObject.b; // undefined
Copy the code

In non-strict mode, the creation of property B fails silently. In strict mode, a TypeError is raised.

  1. seal

Object.seal(..) Will create a “sealed” Object, this method will actually call object.preventExtensions (..) on an existing Object. And marks all existing properties as different :false. So, not only can you not add new attributes after sealing, you can’t reconfigure or remove any existing attributes (although you can change the values of the attributes).

  1. freeze

Object.freeze(..) Creates a frozen Object, which actually calls object.seal (..) on an existing Object. And mark all “data access” properties as writable:false so that their values cannot be modified.

Deep freeze an Object by first calling object.freeze (..) on the Object. , then iterates through all the objects it references and calls object.freeze (..) on those objects. .

3.7 [[get]]

var myObject = { 
    a: 2 
}; 
myObject.a; / / 2
Copy the code

Myobject. a actually implements the [[Get]] operation on myObject (a bit like a function call: [Get]). The default built-in [[Get]] operation on an object first looks for a property with the same name in the object and returns the value of that property if found. If no attribute with the same name is found anyway, the [[Get]] operation returns the value undefined:

Note that this approach is not the same as when accessing variables. If you refer to a variable that does not currently exist in the lexical scope, instead of returning undefined as an object property, a ReferenceError exception is thrown:

3.8 [[Put]]

Typically, assigning a property to an object triggers [[Put]] to set or create the property. But that’s not quite the case.

If this attribute already exists, the [[Put]] algorithm will generally check for the following.

  1. Is the property an access descriptor? Call the setter if it is and there is a setter.
  2. Is writable false in the data descriptor of the property? If so, it fails silently in non-strict mode and raises TypeError in strict mode.
  3. If neither, set the value to the value of the property.

3.9 Getter and Setter

When a getter, setter, or both are defined for a property, the property is defined as an “access descriptor” (as opposed to a “data descriptor”). For access descriptors, JavaScript ignores the value and writable features, and instead cares about the set and get features (as well as different and Enumerable).

var myObject = { 
    // Define a getter for a
    get a() { 
        return 2; }}; myObject.a =3; 
myObject.a; / / 2
Copy the code

Since we only define the getter for A, the set operation ignores the assignment and does not throw an error when setting the value of A. And even if there is a legal setter, our custom getter will only return 2, so the set operation is meaningless

In general, getters and setters come in pairs (defining only one of them will often result in unexpected behavior).

var myObject = {
    // Define a getter for a
    get a() { 
        return this._a_; 
    }, 
    // Define a setter for a
    set a(val) { 
        this._a_ = val * 2; }}; myObject.a =2; 
myObject.a; / / 4
Copy the code

The existence of 3.10

var myObject = { 
    a:2 
}; 
("a" in myObject); // true 
("b" in myObject); // false 
myObject.hasOwnProperty( "a" ); // true 
myObject.hasOwnProperty( "b" ); // false
Copy the code

The in operator checks whether the property is in the object and its [[Prototype]] Prototype chain

hasOwnProperty(..) Only check if the property is in the myObject object, not the [[Prototype]] chain

All common objects can be accessed by using the hasOwnProperty(..) by assigning to object.prototype. , but some objects may not be connected to object.prototype (via object.create (null)). In this case, myobecjT.hasOwnProperty (..) Will fail

Then you can use a tougher approach to sentence: broken Object. The prototype. The hasOwnProperty. Call (myObject, “a”), it use basic hasOwnProperty (..) Method and explicitly bind it to myObject.

It looks like the in operator checks for the presence of a value in the container, but it actually checks for the presence of an attribute name. This distinction is very important for arrays. The result of 4 in [2, 4, 6] is not True as you would expect, because the array [2, 4, 6] contains properties with names 0, 1, 2, and no 4.

  1. The enumeration
var myObject = { }; 
Object.defineProperty( 
    myObject, 
    "a".// Make a enumerable like a normal property
    { enumerable: true.value: 2});Object.defineProperty( 
    myObject, 
    "b".// make b unenumerable
    { enumerable: false.value: 3}); myObject.b;/ / 3
("b" in myObject); // true
myObject.hasOwnProperty( "b" ); // true 
/ /...
for (var k in myObject) { 
    console.log( k, myObject[k] ); 
} 
// "a" 2
Copy the code

Myobject.b does exist and has an access value, but it does not appear in for.. In loop (although it can be determined by the in operator). The reason is that “enumerable” is the same thing as “can appear in traversal of object properties.”

Apply for.. to arrays The in loop can sometimes produce unexpected results, because this enumeration contains not only all numeric indexes, but also all enumerable attributes. It is best to apply for.. only to objects. In loop, if you want to traverse the array you use the traditional for loop to traverse the numeric index.

PropertyIsEnumerable (..) PropertyIsEnumerable (..) propertyIsEnumerable(..) It checks if the given property name exists directly in the object (not on the prototype chain) and satisfies Enumerable: True.

myObject.propertyIsEnumerable( "a" ); // true 
myObject.propertyIsEnumerable( "b" ); // false
Copy the code

Object.keys(..) Returns an array containing all the enumerated attribute Object. GetOwnPropertyNames (..) Returns an array containing all properties, whether or not they are enumerable.

In and hasOwnProperty (..) The difference is whether to find [[Prototype]] chains however, object.keys (..) And the Object. GetOwnPropertyNames (..) Only look for properties that the object directly contains.

4. Traversal

For numerically indexed arrays, you can use the standard for loop to iterate over the values:

var myArray = [1.2.3]; 
for (var i = 0; i < myArray.length; i++) { 
    console.log( myArray[i] ); 
} 
/ / 1 2 3
Copy the code

This is not actually iterating over values, but over subscripts to point to values, like myArray[I]

A set of subscripts is traversed in numeric order (for loop or other iterator), but the order in which object attributes are traversed is uncertain and may vary in different JavaScript engines. Therefore, when you need to ensure consistency in different environments, you must not trust any observed order; it is unreliable.

for.. In doesn’t get the property value directly when it walks through the object, because it actually walks through all the enumerable properties in the object, and you need to get the property value manually

ES6 added a for.. The of loop syntax (you can also iterate over an object if the object itself defines an iterator) is used to iterate over values directly