Object 1.

1.1 Object Overview

Objects in JS have two forms: literal and constructed.

Note: The key name of an object can only be a string and Symbol

// the literal form
var myObj = {
    key: value
    // ...
};

// The structure form
var myObj = new Object(a); myObj.key = value;Copy the code

Objects are one of the main types in Javascript. Simple primitives are not objects. Arrays and functions are objects.

JavaScript has some built-in objects called built-in objects, mainly String, Number, Boolean, Function, etc. In JS they are really just built-in functions. The corresponding subtypes can be built with the new operator.

The key of an object in ES6 can be evaluated by an expression:

var prefix = 'foo';

var myObject = {
    [prefix + 'bar'] :'hello',
    [prefix + 'baz'] :'world'
};

myObject['foobar']; // hello
myObject['foobaz']; // world
Copy the code

For copy assignment of objects, see deep copy and shallow copy techniques in ‘Function Tricks’.

1.2 Attribute Descriptors

Object.defineproperty () can be used to add or modify attributes to the value of an Object. There are four main attribute descriptors:

The name of the describe
writable Writability The value of the property cannot be modified when false, and strict throws an exception.
configureable Configurability As long as it is currently configurable, we can use the defineProperty() modifier descriptor definition. Setting this property to false is a single action and is irrevocable. However, wirteable can still be changed from true to false, while the reverse operation cannot, and delete will fail
enumerable Enumerability This descriptor determines whether or not a property can be present in an enumeration operation, for examplefor... inAnd so on.
immutability Immutability (shallow)

Combining different attribute descriptors, we can implement different operations on object attributes

The name of the describe
constant writeable: false+configurable:false
Do not extend Object.preventExtension(obj)
seal Object.seal(obj), essentially calls are not extensible and configurableconfigurable:false
freeze Object.freeze(obj), essentially calls the seal, and setswriteable:false.

1.3 Accessing descriptors

When accessing an object’s property, you are actually performing the object’s [[GET]] operation, which accesses the property both on the object itself and on the prototype chain, and returns undefined if it is not accessed.

There is, of course, [[SET]] as opposed to [[GET]].

There is a method in ES5 that overrides some of these default operations, but not at the object level, but for each property, via getters and setters, calling a hidden function to get the values and properties. This is called “access descriptors “(similar to” data descriptors “). For access descriptors, their value and writable are meaningless. Instead, the set, GET, and the original Systems and Enumerable are different.

let myObject = {
    get a() {
        return 2; }};Object.defineProperty(
    myObject, // Target object
    'b'./ / the property name
    {
        / / descriptors
        // Define the getter for 'b'
        get: function() {
            return this.a * 2;
        },

        // Make sure 'b' appears as an object attribute
        enumerable: true}); myObject.a;/ / 2

myObject.b; / / 4

myObject.a = 3;

myObject.a; / / 2
Copy the code

1.4 Existence judgment

Because nonexistent objects return undefined, we sometimes cannot accurately determine the existence of a property. Here’s an example:

var myObject = {
    a: undefined
};

myObject.a; // undefined

myObject.b; // undefined
Copy the code

There are several ways to determine the existence of an attribute:

//in
'a' in myObject; // true
'b' in myObject; // false

//hasOwnProperty
myObject.hasOwnProperty('a'); // true
myObject.hasOwnProperty('b'); // false

// Better: because you can't tell if the method exists on a given object
Object.prototype.hasOwnProperty.call(myObject, 'a');
Copy the code

The difference between IN and hasOwnProperty is that IN queries the prototype chain, whereas hasOwnProperty returns true only if the property exists in the instance.

Objecy. Keys () and the Object. GetOwnPropertyName () won’t go to query the prototype chain.

If need to distinguish between the enumeration and an enumeration, can use myOject. PropertyIsEnumerable (“..” ).

1.5 Object Iteration

for… The in loop iterates through all iterable properties on an object (including the prototype chain).

ES5 adds forEach(),every() and some(). Each of these methods takes a callback function that will be applied to each element in the array, ForEach () iterates through all the values in the array and ignores the return value of the callback. Every () iterates until the end, or when the callback returns a value of false, while some() waits for a value of true.

ES6 introduces for… To iterate over data or objects requires that the iterator has an iterator interface inside it. Each time the loop calls the next() method of the iterator, the contents of the loop are the successive return values.

Iterator is an interface that provides a unified access mechanism for various data structures. Any data can be iterated (that is, all the members of the data structure can be processed at once) by deploying the Iterator interface. And mainly for… Of provides consumption.

Iterator traversal looks like this:

  1. Create a pointer object that points to the start of the current data structure; that is, Iterator is essentially a pointer object.
  2. The first time you call the next method, you point to the first member
  3. The second time you call the next method, the pointer just wants to be the second member of the data structure
  4. Keep calling until the end of the loop

Here is a method that simulates the return of Next:

var it = makeIterator(['a'.'b']);

it.next(); // { value: "a", done: false }
it.next(); // { value: "b", done: false }
it.next(); // { value: undefined, done: true }

function makeIterator(array) {
    var nextIndex = 0;
    return {
        next: function() {
            return nextIndex < array.length
                ? { value: array[nextIndex++], done: false}, {value: undefined.done: true}; }}; }Copy the code

The result of the traversal is an object containing both value and done properties, where value is the value of the current member and done is a Boolean value indicating whether the traversal is over.

Normally we can just use for… To iterate over the object we want, we can use Symbol. Iterator to get the internal iterator interface:

var myArray = [1.2.3];
var it = myArray[Symbol.iterator]();

it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { done:true }
Copy the code

1.6 Manually Deploying Iterator

var myObject = {
    a: 2.b: 3
};

Object.defineProperty(myObject, Symbol.iterator, {
    enumerable: false.writable: false.configurable: true.value: function() {
        var o = this;
        var idx = 0;
        var ks = Object.keys(o);
        return {
            next: function() {
                return {
                    value: o[ks[idx++]],
                    done: idx > ks.length }; }}; }});// Manually iterate through 'myObject'
var it = myObject[Symbol.iterator]();
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { value:undefined, done:true }

/ / ` for.. Of ` iterative ` myObject `
for (var v of myObject) {
    console.log(v);
}
/ / 2
/ / 3
Copy the code

Prototype 2.

What is a prototype? Almost every object in JS has an internal attribute [[Prototype]], which is a reference to another object.

var anotherObject = {
    a: 2
};

// Create an object linked to 'anotherObject'
var myObject = Object.create(anotherObject);

myObject.a; / / 2
Copy the code

Among them, the main internal principles of CREATE are as follows:

function _create(obj) {
    function F() {}
    F.prototype = obj;
    return new F()
}
Copy the code

2.1 the prototype

Each function has a prototype property, which points to an object that is the prototype of the instance created by calling the constructor:

function Person() {}
// You should note that:
// prototype is a function property
Person.prototype.name = 'Xiaoming';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name); // Xiaoming
console.log(person2.name); // Xiaoming
Copy the code

2.2 __proto__

This is an attribute that every JS object (except null) has, called __proto__, which points to the object’s prototype:

function Person() {}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
Copy the code

2.3 the constructor

The stereotype has a constructor attribute that points to the constructor:

function Person() {}
console.log(Person === Person.prototype.constructor); // true
Copy the code

The relationship between the three (constructor, instance prototype, instance) looks like this:

2.4 prototype chain

In the above code,Person.prototype is the prototype of Person, which is itself an Object, so there should be a prototype of its own: Object.prototype. The prototype of the prototype will continue to be linked in this way, ending in the built-in type Object. The link formed by __proto__ is the prototype chain:

2.5 summary

  1. __proto__andconstructorProperties are unique to the object;
  2. prototypeAttributes are unique to functions
  3. Functions are also objects, so functions have them__proto__andconstructorattribute

__proto__ is unique to an object and refers to an object’s prototype (prototype chain). Prototype is unique to a function and refers to an object’s prototype object. Prototype contains properties and methods that can be shared by all instances of a particular type; that is, objects instantiated by this function can find common properties and methods. When any function is created, its prototype object is created by default.

Contrructor is also object owned. It points from an object to a function, meaning it points to the constructor of that object.

Diagram:

3. Class inheritance

Classes are the most common concept in traditional object-oriented programming, and some important concepts such as constructors, inheritance, polymorphism, multiinheritance and so on are very important concepts in program construction.

Because of the flexibility of the JS language, there are many ways to implement and simulate inheritance. Now let’s introduce them all.

3.1 Inheritance of prototype chain

Stereotype inheritance: The private + public attributes and methods of the parent class are treated as the public attributes of the subclass.

Core: instead of cloning the parent’s private + public attributes into an identical copy of the child’s public attributes; It uses __proto__ to create a chain of prototypes between subclasses. When subclasses need attributes and methods from their parent class, they can be used by __proto__ hierarchy.

/ / parent class
function Parent() {
    this.x = 199;
    this.y = 299;
}
Parent.prototype.say = function() {
    console.log('say');
};

/ / subclass
function Child() {
    this.g = 90;
}

Child.prototype = new Parent(); / / inheritance

var p = new Parent();
var c = new Child();
console.dir(c);
Copy the code

However, it is important to note that when we need to add a new method to a subclass or rewrite a method from a parent class, we must always put it after the statement replacing the prototype.

function Parent() {
    this.x = 199;
    this.y = 299;
}
Parent.prototype.say = function() {
    console.log('say Person');
};
function Child() {
    this.z = 90;
}
/*Child.prototype.Bs = function(){ console.log('Bs') }*/
// It is useless to write the prototype methods and attributes of the subclass here because it will change the orientation of the prototype, so it should be put after the reassignment
Child.prototype = new Parent();
Child.prototype.constructor = Child; // The constructor of the default Child prototype is lost because we remodified it. We need to add it ourselves
Child.prototype.sayB = function() {
    console.log('say B');
};
Child.prototype.say = function() {
    console.log('say Child');
};
var p = new Parent();
var c = new Child();
console.dir(c);
c.sayB(); // say B
c.say(); // say Child
p.say(); // Say Person does not affect the methods that instances of the parent class access
Copy the code

Problems with prototype inheritance:

1, a subclass inherits properties and methods of the parent is the parent class of private property and public methods as their public attributes and methods, we want to clear one thing is our basic data types when operation is the value of application in operation operation is the address of the data type, if the private property of the parent of a property of a reference type, So it’s going to be a public property when it’s inherited by subclasses, so when subclass one manipulates this property, it affects subclass two.

When creating an instance of a subclass, you cannot pass arguments to the constructor of the parent type. There is no way to pass arguments to the constructor of a parent class without affecting all object instances

So prototype inheritance alone is rarely used in practice.

3.2 Call inheritance

Using a parent class constructor to enhance a child class instance is equivalent to copying an instance of the parent class to a child class (without using a stereotype).

function Parent() {
    this.x = 100;
    this.y = 199;
}
Parent.prototype.fn = function() {};

function Child() {
    this.d = 100;
    Parent.call(this); // This in the constructor is the current instance
}
var p = new Parent();
var c = new Child();
console.log(p); //Parent {x: 100, y: 199}
console.log(c); //Child {d: 100, x: 100, y: 199}
Copy the code

Disadvantages:

  • Only instance properties and methods of the parent class can be inherited, not stereotype properties/methods
  • Unable to reuse, each subclass has a copy of the parent class instance function, affecting performance

3.3 Impersonating object inheritance

The principle of impersonating object inheritance is to loop through the parent instance and then take all of the parent instance’s methods and add them to the subclass instance

function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function() {
    console.log('getX');
};
function Child() {
    var p = new Parent();
    for (var attr in p) {
        // For in traverses public custom properties on the stereotype
        this[attr] = p[attr];
    }
    // The following code gets only private methods and attributes. If you don't add this, you can iterate through all methods and attributes
    /*if(e.hasOwnProperty(attr)){ this[attr] = e[attr] } e.propertyIsEnumerable()*/ // Enumerable attributes ==> Attributes that can be taken out to list one by one
}
var p = new Parent();
var c = new Child();
console.dir(c);
Copy the code

3.4 Mixed Inheritance

Using the prototype chain to achieve the inheritance of the prototype method, using the constructor to inherit attributes. Essentially a mix of Call integration and prototype chain inheritance:

function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function() {};

function Child() {
    Parent.call(this);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

var p = new Parent();
var c = new Child();
console.log(c); //Child {x: 100}
Copy the code

There are multiple ways to mix inheritance, you can mix call and stereotype, you can mix call and impersonator inheritance, and so on

The hybrid inherit the biggest problem is that under no circumstances, will be called two constructors: one is to create a subtype prototype, another is in the interior of the subtype constructor, that’s right, a subtype will eventually contain all parent type object instance attributes, but we have to rewrite these properties when invoking the subclass constructor.

Here is another hybrid inheritance: Call inheritance + copy inheritance

// Hybrid inheritance: call inheritance + copy inheritance
function extend(newEle, oldEle) {
    for (var attr inoldEle) { newEle[attr] = oldEle[attr]; }}function F() {
    this.x = 100;
    this.showX = function() {};
}
F.prototype.getX = function() {};
F.prototype.getX1 = function() {};

var f1 = new F();
console.dir(f1);

function S() {
    F.call(this); / / call to inherit
}
extend(S.prototype, F.prototype); // Copy inheritance
S.prototype.cc = function() {};

var p1 = new S();
console.dir(p1);
Copy the code

3.5 Parasitic inheritance

Core: On the basis of the original type inheritance, enhance the object, return the constructor

// "Traditional JS class" 'Vehicle'
function Vehicle() {
    this.engines = 1;
}
Vehicle.prototype.ignition = function() {
    console.log('Turning on my engine.');
};
Vehicle.prototype.drive = function() {
    this.ignition();
    console.log('Steering and moving forward! ');
};

// 'parasite' 'Car'
function Car() {
    // First, 'car' is an 'Vehicle'
    var car = new Vehicle();

    // Now we modify 'car' to make it specialized
    car.wheels = 4;

    // Save a reference to 'Vehicle:: Drive ()
    var vehDrive = car.drive;

    / / cover ` Vehicle: : drive ` ()
    car.drive = function() {
        vehDrive.call(this);
        console.log('Rolling on all ' + this.wheels + ' wheels! ');
    };

    return car;
}

var myCar = new Car();

myCar.drive();
// Turning on my engine.
// Steering and moving forward!
// Rolling on all 4 wheels!
Copy the code

3.6 Parasitic combinatorial inheritance

Call inheritance + object.create ();

Parasitic combinatorial inheritance inherits properties by borrowing constructors and inherits methods through a hybrid form of stereotype chains. The basic idea is that you don’t need to call the parent class’s constructor to specify a subclass’s prototype; all you need is a copy of the parent type’s prototype. Essentially, you use parasitic inheritance of the parent’s prototype and then assign the result to the child’s prototype.

// Inherit the prototype chain
function inheritPrototype(subType, superType) {
    var prototype = Object.create(superType.prototype); // Create object, create a copy of the parent class prototype
    prototype.constructor = subType; // Enhance the object to compensate for the loss of the default constructor property by rewriting the prototype
    subType.prototype = prototype; // Assign the newly created object to the prototype of the subclass
}

// The parent class initializes instance properties and stereotype properties
function SuperType(name) {
    this.name = name;
    this.colors = ['red'.'blue'.'green'];
}
SuperType.prototype.sayName = function() {
    alert(this.name);
};

// Use the constructor to pass enhanced subclass instance attributes (support passing arguments and avoid tampering)
function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}

// Point the parent class prototype to the child class
inheritPrototype(SubType, SuperType);

// Add a new subclass stereotype attribute
SubType.prototype.sayAge = function() {
    alert(this.age);
};

var instance1 = new SubType('xyc'.23);
var instance2 = new SubType('lxy'.23);

instance1.colors.push('2'); // ["red", "blue", "green", "2"]
instance2.colors.push('3'); // ["red", "blue", "green", "3"]
Copy the code

3.7 Classical Inheritance (Douglas Inheritance)

// Function encapsulation
function create(o) {
    function F(){}
    F.prototype=o;
    return new F();
}

var o={name:"Zhang".age:18};var o2=create(o);// O2 inherits from O
Copy the code

3.8 ES6 extends

The extends keyword is used primarily in class declarations or class expressions to create a class that is a subclass of another class. A class can have only one constructor, and SyntaxError is raised if more than one constructor is specified. If no constructor is explicitly specified, the default constructor method is added, as shown in the following example.

class Rectangle {
    // constructor
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }

    // Getter
    get area() {
        return this.calcArea();
    }

    // Method
    calcArea() {
        return this.height * this.width; }}const rectangle = new Rectangle(10.20);
console.log(rectangle.area);
/ / output 200

/ / inheritance
class Square extends Rectangle {
    constructor(length) {
        super(length, length);

        // If a constructor exists in a subclass, you need to call super() first before using "this".
        this.name = 'Square';
    }

    get area() {
        return this.height * this.width; }}const square = new Square(10);
console.log(square.area);
/ / output 100
Copy the code

The core code of extends extends is the same as the parasitic combinational inheritance above:

function _inherits(subType, superType) {
    // Create object, create a copy of the parent class prototype
    // Enhance the object to compensate for the loss of the default constructor property by rewriting the prototype
    // Assign the newly created object to the prototype of the subclass
    subType.prototype = Object.create(superType && superType.prototype, {
        constructor: {
            value: subType,
            enumerable: false.writable: true.configurable: true}});if (superType) {
        Object.setPrototypeOf ? Object.setPrototypeOf(subType, superType) : (subType.__proto__ = superType); }}Copy the code

4. Class keyword

ES6 proposed the Class keyword to realize the js Class writing method.

  1. The class declaration is promoted, but not assigned, and there is a TDZ(temporary dead zone), similar to thislet.const:
const bar = new Bar(); // it's ok
function Bar() {
    this.bar = 42;
}

const foo = new Foo(); // ReferenceError: Foo is not defined
class Foo {
    constructor() {
        this.foo = 42; }}Copy the code
  1. Strict mode is enabled inside the class
// Reference an undeclared variable
function Bar() {
    baz = 42; // it's ok
}
const bar = new Bar();

class Foo {
    constructor() {
        fol = 42; // ReferenceError: fol is not defined}}const foo = new Foo();
Copy the code
  1. classAll methods (both static and instance methods) of the.
// Reference an undeclared variable
function Bar() {
    this.bar = 42;
}
Bar.answer = function() {
    return 42;
};
Bar.prototype.print = function() {
    console.log(this.bar);
};
const barKeys = Object.keys(Bar); // ['answer']
const barProtoKeys = Object.keys(Bar.prototype); // ['print']

class Foo {
    constructor() {
        this.foo = 42;
    }
    static answer() {
        return 42;
    }
    print() {
        console.log(this.foo); }}const fooKeys = Object.keys(Foo); / / []
const fooProtoKeys = Object.keys(Foo.prototype); / / []
Copy the code
  1. classNone of the methods (both static and instance methods) of theprototypeSo no[[construct]], cannot be called with new:
function Bar() {
    this.bar = 42;
}
Bar.prototype.print = function() {
    console.log(this.bar);
};

const bar = new Bar();
const barPrint = new bar.print(); // it's ok

class Foo {
    constructor() {
        this.foo = 42;
    }
    print() {
        console.log(this.foo); }}const foo = new Foo();
const fooPrint = new foo.print(); // TypeError: foo.print is not a constructor
Copy the code
  1. You must use thenewcallclass.
function Bar() {
    this.bar = 42;
}
const bar = Bar(); // it's ok

class Foo {
    constructor() {
        this.foo = 42; }}const foo = Foo(); // TypeError: Class constructor Foo cannot be invoked without 'new'
Copy the code
  1. classThe class name cannot be overridden internally.
function Bar() {
    Bar = 'Baz'; // it's ok
    this.bar = 42;
}
const bar = new Bar();
// Bar: 'Baz'
// bar: Bar {bar: 42}

class Foo {
    constructor() {
        this.foo = 42;
        Foo = 'Fol'; // TypeError: Assignment to constant variable}}const foo = new Foo();
Foo = 'Fol'; // it's ok
Copy the code

5. About classes and objects in JS

Think about the meaning of prototype chains: Why does one object need to be linked to another? Before we can understand that, we need to understand what [[Prototypr]] is not.

In JavaScript, there is no abstract schema for objects, that is, there is nothing called a class like in class-oriented languages. JS only has objects.

In fact, this particular aspect of JS is almost unique among all languages. Because there are few languages where you can create objects directly without classes at all. JS is one of them. In JS, classes cannot describe what objects can do. The object directly defines its own behavior. There are only objects

Class function

In the code above, we created a lot of things that look like classes.

The strange behavior of “classes of some kind” depends on a strange property of functions: all functions get a public, non-enumerable property. Called Prototype. It can point to any object.

function Foo() {
	// ...
}

Foo.prototype; // { }
Copy the code

This object is called a “prototype of Foo” because we access it through a foo.prototype.

The most straightforward way to explain this is that every object created by calling new Foo() will eventually be linked to the foo.prototype object by [[Prototype]].

function Foo() {
	// ...
}

var a = new Foo();

Object.getPrototypeOf( a ) === Foo.prototype; // true
Copy the code

In class-oriented languages, it is possible to make multiple instances of a class, like stamping something out of a mold. This is because the process of initializing a class means copying the behavior from the class into a physical object.

But in JS, no such copying takes place. You don’t create multiple instances of a class. You can create multiple objects. Their [[Proptotype]] connects to a common object. But by default, no copy occurs. These objects are not completely separated and disconnected from each other, but are linked together.

In fact, the new Foo() function call has nothing to do directly with the handling of the link. New Foo() is an indirect, roundabout way to get what we want: an object that is linked to another object.

Refer to the link

  • There are eight inheritance schemes commonly used in JavaScript – mu yiyang says
  • Common inheritance in JavaScript
  • JavaScript goes from prototype to prototype chain
  • What is the difference between ES5/ES6 inheritance other than the way it is written?
  • You don’t know JS