?? There are six types of data in JavaScript: Number, String, Boolean, null, Undefined, and object, and ES6 adds a new data type, symbol. Where objects are referred to as reference types, other data types are called underlying types. In object-oriented programming languages, objects are typically instantiated from classes, but there is no class in JavaScript, and objects are produced in a completely different design pattern than the class.
First, create the object
?? The most common way to create is through the way the object literal is simple and convenient. But this is a singleton pattern , and if creating a similar object produces too much duplicate code, as shown in the following code:
var person1 = { name: ‘LiLei‘, age: 18, sayName: function () { console.log(this.name) }}var person2 = { name: ‘HanMeiMei‘, age: 18, sayName: function () { console.log(this.name) }}person1.sayName() // LiLeiperson2.sayName() // HanMeiMei
?? The use of Factory mode avoids duplication of code, but the disadvantage of Factory mode is that it is not very good to classify the resulting objects. As shown in the following code:
function person (name, age) { var obj = new Object() obj.name = name obj.age = age obj.sayName = function () { console.log(this.name) } return obj} var person1 = person(‘LiLei‘,18)var person2 = person(‘HanMeiMei‘,18)person1.sayName() // LiLeiperson2.sayName() // HanMeiMei
1. Constructor mode
?? The constructor pattern is able to categorize the instantiated objects nicely, as shown in the following code:
function Person (name, age) { this.name = name this.age = age this.sayName = function () { console.log(this.name) }}var person1 = new Person(‘LiLei‘,18)var person2 = new Person(‘HanMeiMei‘,18)person1.sayName() // LiLeiperson2.sayName() // HanMeiMei
?? The first thing to note is that there is no syntax for constructors in JavaScript, and some are simply construction calls to functions. Constructors are no different from normal functions, and are called constructors when they are called by the keyword new. The function name that is used as a constructor call begins with an uppercase letter and is a writing specification that does not have this constraint at the JavaScript language level. Calling the constructor with the new keyword goes through the following four stages:
1, create an empty object.
2. The [[Proto]] property of the object points to the object that the constructor prototype property points to.
3. Initializes the empty object with the new empty object as the execution context and through the code inside the constructor.
4, if the constructor has a return value and is an object, the object is returned. Otherwise, the newly created object is returned.
?? In contrast to the factory model, the constructor pattern can clearly classify objects, and in contrast to singleton patterns, constructors do not produce large amounts of duplicated code. But it is not entirely without generating duplicate code, as shown in the code above: Person1 objects and Person2 objects can share a Sayname method. Using only the constructor pattern, each object has its own new method that cannot be shared with one another, and in order to overcome this flaw, the prototype pattern emerges.
2. Prototype mode
?? Each function is created with a prototype property (except for the function returned by the Function.bind () method), which points to the prototype object of the function that was created at the same time, with a non-enumerable property constructor in the prototype object. This property points to the function that corresponds to the prototype.
?? The instance object generated by the constructor has a pointer to the constructor prototype object, which is referred to in ES5 as [[[Proto]],es6] called __proto__. In the process of creating an object through a constructor, there are essentially two things: 1. Point the [[Proto]] property of the instance object to the object that the constructor prototype property points to, 2, the instance object as the execution context of the constructor, and the execution constructor completes the initialization. Once the instance object has been built, it is no longer related to the original constructor, even if the instance object can be categorized as a constructor name by a prototype object.
?? When a property is found in an instance object, it is first looked up on the object itself, and if it does not find it, it will find it on the prototype chain by [[Proto]].
?? The relationships between constructors, prototype objects, and instance objects are as follows:
?? The prototype pattern is essentially shared, and all the [[Proto]] pointers to the instance objects of the prototype object can access the properties on the prototype object. If there is a property with the same name as the prototype object on the instance object, it forms a "mask" that accesses the value on the instance object instead of the value on the prototype object. The property value on the prototype cannot be modified by an instance object , but this property is the same as the keyword const that defines the constant in ES6, and the address of this property cannot be changed, if the property on the prototype object is a reference type, the address of the reference type cannot be changed. References to objects can be changed by instance objects. As shown in the following code:
function Student () {}Student.prototype.score = 60Student.prototype.course = [‘语文‘,‘数学‘]let LiLei = new Student()let HanMeiMei = new Student()console.log(LiLei.score) // 60console.log(HanMeiMei.score) // 60LiLei.score = 90console.log(LiLei.score) // 90console.log(HanMeiMei.score) // 60LiLei.course.push(‘英语‘)console.log(LiLei.course) // [‘语文‘,‘数学‘,‘英语‘]console.log(HanMeiMei.course) // [‘语文‘,‘数学‘,‘英语‘]
?? Prototype patterns are basically not used alone, because prototype patterns produce objects that are the same, and cannot be initialized by passing parameters as constructors do. Russell said: "Uneven polymorphism is the source of happiness," object-oriented programming is to a large extent the simulation of the real world, so this sentence in the programming world also applies.
3. Combination of constructors and prototype patterns
?? Combining constructors and prototype patterns is the most common way to create custom objects. The object is given an instance property by means of a constructor, and a prototype object is made to share the properties of the same type of object. In general, reference objects belong to instance properties, and there is no reference object on the prototype object other than a function to prevent one object from modifying the reference object on the prototype object to affect other objects. As shown in the following code:
function Student (name,age) { this.name = name this.age = age}Student.prototype.score = 60let LiLei = new Student(‘LiLei‘,18)let HanMeiMei = new Student(‘HanMeiMei‘,16)console.log(LiLei.name) // LiLeiconsole.log(LiLei.age) // 18console.log(LiLei.score) // 60console.log(HanMeiMei.name) // HanMeiMeiconsole.log(HanMeiMei.age) // 16console.log(HanMeiMei.score) // 60
4. Modify the Prototype object
?? Adding a property or function to a constructor's prototype is sometimes tedious to add, so it is often a prototype object that changes the constructor directly. As shown in the following code:
function Student (name,age) { this.name = name this.age = age}Student.prototype = { score: 60, sayName: function () { console.log(this.name) }, sayAge: function () { console.log(this.age) }}let LiLei = new Student(‘LiLei‘,18)LiLei.sayName() // LiLei
?? Here are two points to note that 1th is the way to discard the original prototype object of the constructor, creating a new object to act as the prototype object for the constructor function. This leads to the problem: If you create an object from a constructor before adding a new prototype object, the [[Proto]] property of the created object is pointing to the old prototype object, and all properties on the new prototype object are not related to the object. 2nd, there is a non-enumerable property constructor in the prototype object that is automatically generated when the function is created, which is a pointer to the function. There is no such property in the newly created object, and if you want to use that property to determine the category of the object, you can add it yourself. As shown in the following code:
function Student (name,age) { this.name = name this.age = age}Student.prototype.score = 80let HanMeiMei = new Student(‘HanMeiMei‘,16)Student.prototype = { score: 60, sayName: function () { console.log(this.name) }}Object.defineProperty(Student.prototype,‘constructor‘,{ value: Student, enumerable: false})let LiLei = new Student(‘LiLei‘,18)console.log(LiLei.score) // 60LiLei.sayName() // LiLeiconsole.log(HanMeiMei.score) // 80HanMeiMei.sayName() // TypeError
Ii. inheritance
?? Inheritance is an important concept in object-oriented programming, and inheritance in JavaScript is achieved through the prototype chain.
1. Prototype inheritance
?? The idea of prototype inheritance is that the prototype of a child object is an instance of the parent object, and almost all of the objects inherit from object, which is said almost because the Object.create () method can be used to create objects that do not inherit from any object.
function Person () { this.name = ‘person‘}Person.prototype.sayName = function () { console.log(this.name)}function Student () { this.id = ‘001‘}Student.prototype = new Person()Student.prototype.sayId = function () { console.log(this.id)}let LiLei = new Student()LiLei.sayId() // 001LiLei.sayName() // personconsole.log(LiLei.toString()) // [object Object]
?? As shown in the code above, the prototype object of the function student is an instance of the person function, so object Lilei can use the properties on the person prototype object. While the prototype object of the function person is an instance of the object function, the object Lilei can use the Object.prototype.toString () method. As shown in the following:
?? The rule for object lookup properties is to find the object itself first, and then stop if it finds it, or continue searching along the prototype chain. That is, find the object that the [[Proto]] pointer is pointing to, find the stop, and find the object that the object [[Proto]] pointer points to until Object.prototype is not queried.
?? Object Adding properties is complicated: When the object itself has properties to be added, it simply modifies its own value, and when the property to be added is not on the object itself, and on the object prototype chain, there are three cases that can be found to add a property to the object obj as an example:
1. The Add property found on the prototype chain is a data property and is not a read-only property, then add is added to obj to form a mask .
2. If the Add property found on the prototype chain is a data property and is a read-only property, adding fails, and the Obj object does not have an add property on its own.
3. The Add property found on the prototype chain is a setter, the setter is called, and the Add property adds a failure.
?? The latter two scenarios described above are property addition failures, in which case you want to force the addition of a property to use the Object.defineproperty () method.
2. Combination Inheritance
?? There are two problems when creating an object using Prototype mode: One is that the reference type does not fit on the prototype object, and the other is that there is no way to initialize the object by passing the parameter. There are still two problems with the use of prototype inheritance, which takes an instance of the parent constructor as a prototype of a child constructor, and in many cases the constructor's prototype has a property of the reference type and cannot pass arguments to the parent constructor. To eliminate the drawbacks of using prototype inheritance alone, it is common to implement inheritance using a combination of borrowed constructors and prototype inheritance .
?? function constructs a call through new, the execution context defaults to the newly created object, but in JavaScript it is possible to specify the execution context for the function by using the Apply () and call () methods, which is used by the so-called constructor . To use the parent constructor to complete the initialization of the child constructor instance object. As shown in the following code:
function Person (person1,person2) { this.friends = [] this.friends.push(person1) this.friends.push(person2)}function Student (person1,person2) { Person.call(this,person1,person2)}let LiLei = new Student(‘tom‘,‘mary‘)let HanMeiMei = new Student(‘tom‘,‘mary‘)LiLei.friends.push(‘penny‘)console.log(LiLei.friends) // [‘tom‘,‘mary‘,‘penny‘]console.log(HanMeiMei.friends) // [‘tom‘,‘mary‘]
?? Using the borrow constructor alone makes it easy to customize the instance properties of an object, but it is scarce in terms of sharing, so implementing inheritance is typically used in combination with prototype inheritance and borrowing constructors . The idea of combinatorial inheritance is to implement the inheritance of prototype properties and methods through the prototype chain, and to implement the inheritance of instance attributes by borrowing the constructor functions . As shown in the following code:
function Person (name) { this.name = name this.friends = [‘tom‘,‘mary‘]}Person.prototype.sayName = function () { console.log(this.name)}function Student (name,age) { Person.call(this,name) this.age = age}Student.prototype = new Person()Student.prototype.sayAge = function () { console.log(this.age)}let LiLei = new Student(‘LiLei‘,20)LiLei.friends.push(‘pony‘)console.log(LiLei.name)console.log(LiLei.friends)let HanMeiMei = new Student(‘HanMeiMei‘,18)console.log(HanMeiMei.name)console.log(HanMeiMei.friends)
3. The nature of inheritance
?? Before optimizing the above code, let's talk about the nature of the inheritance in JavaScript. When you make a call to a function by using the New keyword, point the [[Proto]] pointer to the object on the constructor's prototype, and then use the instance object as the execution context to execute the constructor again. The so-called inheritance is essentially the object's [[Proto]] pointer to another object, and when the property on the object is found, the query itself is queried by the [[Proto]] pointer. In fact, this method is called a delegate more appropriate, the object part of the property on the object itself, another part of the property or method delegate to another object. You can specify a prototype object directly for the newly created object without taking into account the properties on the object itself, regardless of the new keyword.
?? The addition of the Object.create () method in ES5 shows that inheritance is actually the essence of object delegation. The method creates a new object that is linked to the specified object, can receive two parameters, the first parameter is the specified prototype object, and the second parameter is the property to be added to the new object itself. Where the second parameter received by the method is the same as the second parameter of the Object.definepropertier () method, each attribute is defined by its own descriptor. As shown in the following code:
let Person = { name: ‘any‘, age: 18}let LiLei = Object.create(Person,{ name: { value: ‘LiLei‘ }})console.log(LiLei.name) // LiLeiconsole.log(LiLei.age) // 18
?? The Object.create () method is a canonical implementation of a prototype inheritance . The so-called prototype inheritance means that you do not have to create a custom type to create a new object based on an existing object. In the case of incompatible object.create (), the following code can be used to fill:
if (!Object.create) { Object.create = function(o) { function F(){} F.prototype = o; return new F(); };}
?? Part of the reason is that this is not the way to add a property to a new object using the second parameter like Object.create ().
4. Optimization and inheritance
?? After talking about the nature of inheritance, optimize the code for combining inheritance. The combination of inheritance is done by borrowing the parent constructor to instantiate the new object, and then linking the prototype of the child constructor to the instance object of the parent constructor, which causes the two parent constructors to be called repeatedly to create the same properties. As shown in the following code:
function Person (name) { this.name = name this.friends = [‘tom‘,‘mary‘]}function Student (name,age) { Person.call(this,name) this.age = age}Student.prototype = new Person()let LiLei = new Student(‘LiLei‘,20)console.log(LiLei) // {age: 20, friends:[ "tom", "mary" ], name: "LiLei"}console.log(Student.prototype) // {friends:[ "tom", "mary" ], name: undefined}
?? You can see that the properties on the instance object and the prototype object are duplicated, which is the legacy of the parent constructor instance object as the prototype object. Because the object itself has properties that "mask" the properties on the prototype object when querying the object's properties, the combination of inheritance can achieve the purpose of inheritance completely. However, redundant data is not a good thing, and can be optimized by primitive inheritance: by borrowing the parent constructor to instantiate the object, the inheritance is implemented by directly pointing the prototype of the child constructor to the prototype object of the parent constructor. As shown in the following code:
function Person (name) { this.name = name this.friends = [‘tom‘,‘mary‘]}Person.prototype.score = 60function Student (name,age) { Person.call(this,name) this.age = age}Student.prototype = Object.create(Person.prototype)let LiLei = new Student(‘LiLei‘,20)console.log(LiLei) // {age: 20, friends:[ "tom", "mary" ], name: "LiLei"}console.log(Student.prototype) // {}
?? This approach, known as parasitic combined inheritance , is the best way to implement reference type inheritance.
Iii. Types of objects
?? The typeof operator can be used to detect data types in JavaScript, as shown in the following code:
var a;typeof a; // "undefined"a = "hello world";typeof a; // "string"a = 42;typeof a; // "number"a = true;typeof a; // "boolean"a = null;typeof a; // "object" -- 当做空对象处理a = undefined;typeof a; // "undefined"a = { b: "c" };typeof a; // "object"
?? When you use the TypeOf keyword to detect an object type, you cannot return the object's specific type. For object type detection, the "duck type" principle is usually followed:
A bird that walks, swims, and tweets like a duck is a duck.
?? In JavaScript, you don't care what the object's type is, it's about what the object can do . Of course there are times when you need to know the type of the object, and you can determine the object type by the following flawed methods.
1, constructor
?? In addition to the functions returned by Function.bind (), all functions execute with a prototype property, prototype a prototype object that points to the function, and a non-enumerable property constructor in the Autogenerated prototype object. Constructor points to the function. When the function constructs the call, [[Proto]] in the created instance object points to the same prototype object as the constructor prototype. You can use this relationship to determine the type of an instance object, as shown in the following code:
function Person () { this.age = 18}let LiLei = new Person()console.log(Person.prototype.constructor === Person) // trueconsole.log(LiLei.constructor === Person) // true
?? Using the constructor property to determine the type of an object does not work in multiple execution contexts, such as opening multiple windows in a browser to display sub-pages, objects in the sub-page are of the same type and cannot be determined using the constructor property.
?? Using the constructor property to determine the object type is based on not changing the constructor's default prototype object, but in many cases it changes the default prototype. For example, the feeling of adding a property and method to a function prototype is cumbersome, and typically creates a new object as a prototype object by literal means, and when an instance object or prototype object of a parent constructor is used as a prototype when implementing inheritance; You need to add a non-enumerable property constructor to the constructor on the new prototype object.
?? There is generally no need to deliberately maintain this relatively fragile relationship, in many cases inadvertently changed the constructor attribute point or changed the prototype object, so the use of constructor is not advocated to determine the type of object.
2, prototype
?? A prototype object is a unique flag for an object type. Both objects belong to the same type when and only if two objects inherit from the same prototype object.
?? The isPrototypeOf () method can determine if there is a prototype relationship between objects, and is used to test whether an object exists on the prototype chain of another object. As shown in the following code:
function Person (name) { this.name = name}let LiLei = new Person(‘LiLei‘) console.log(Person.prototype.isPrototypeOf(LiLei)) // trueconsole.log(Object.prototype.isPrototypeOf(LiLei)) // true
?? The new Object.getprototypeof () method in ES5 returns the prototype of the specified object, which is the value of the internal [[Prototype]] property. As shown in the following code:
function Person (name) { this.name = name}Person.prototype.score = 60let LiLei = new Person(‘LiLei‘)console.log(Object.getPrototypeOf(LiLei)) // { score:60 }console.log(Object.getPrototypeOf(LiLei) === Person.prototype) // true
3, instanceof
?? The relationship between the detection object and the prototype object is straightforward, and sometimes you want to determine the relationship between the instance object and the constructor, which can be detected by the instanceof operator. The instanceof operator is used to detect whether the object pointed to by the function's prototype property exists on the prototype chain of the parameter instance object. As shown in the following code:
function Person (name) { this.name = name}Person.prototype.score = 60 function Animal () { this.type = ‘dog‘}function Student (name,age) { Person.call(this,name) this.age = age}Student.prototype = Object.create(Person.prototype)let LiLei = new Student(‘LiLei‘,20)console.log(LiLei instanceof Student) // trueconsole.log(LiLei instanceof Person) // trueconsole.log(LiLei instanceof Object) // trueconsole.log(LiLei instanceof Animal) // false
?? isPrototypeOf (), the instanceof operator to determine the type of an object, you can only detect that the object belongs to a certain type, but not the object to get the type name. The Object.getprototypeof () method can only get the object that the object [[Proto]] pointer points to. In addition, these methods detect the object type and the constructor operator, and do not work in multiple execution context scenarios.
Iv. Summary
?? As an object-oriented programming language, there are no classes in JavaScript, only objects. When creating an object from a constructor, there is no "copy" from the class in other object-oriented programming languages, just creating a new object, executing the constructor as the execution context of the constructor, and finally linking the object through the prototype chain.
?? The most common way to create objects is in the way of object literals, while creating custom objects of a particular type with the most common combinations of constructors and prototype patterns. Use the constructor to add the instance object's own properties, use the prototype object to add properties and methods of the same type of object share, and add the reference type properties of the generic object to the constructor.
?? The more exact name of the inheritance in JavaScript is the delegate , which implements the sharing and reuse of methods and properties through the delegation between objects, which can be done directly through the Object.create () method. An object that wants to inherit all the properties and methods of an object that owns a reference type property , the best way to implement it is parasitic combined inheritance .
?? A prototype object is a unique flag for an object type, and you want to determine the type of the object, which can be accomplished by validating the object's prototype. JavaScript is more concerned with what an object can do, not what type of object it is.
If you need to reprint, please indicate the source: https://www.cnblogs.com/lidengfeng/p/9300910.html
JavaScript tamping basic Series (iv): prototypes