create object
Factory Mode
Example:
function createPerson(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function () { console.log(this.name); }; return o; } var person1 = createPerson('Zhang San', 20, 'actor'); console.log(person1); person1.sayName(); var person2 = createPerson('Li Si', 21, 'teacher'); person2.sayName();
return statement:
- When there is no "return" statement in the createPerson() function, person1 points to createPerson {} and an error is reported when calling Uncaught TypeError: person1.sayName is not a function
Role: Solves the problem of creating multiple similar objects
Disadvantage: Unable to determine the type of object
Build Function Patterns
Example:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { console.log(this.name); }; } var person1 = new Person('Zhang San', 20, 'actor'); person1.sayName();
Unlike factory functions:
- Object was not explicitly created
- Assign attributes and methods directly to this object
- No return statement
The new operator, which calls the constructor in four steps:
- Create a new object;
- Assign the scope of the constructor to the new object (so this points to the new object)
- Execute the code in the constructor (add attributes to this new object)
- Return a new object
function New(f) { return function () { var o = { __proto__: f.prototype, }; f.apply(o, arguments); return o; }; } var person1 = New(Person)('Zhang San', 20, 'actor'); console.log('per', person1); person1.sayName();
Treat the constructor as a function
var person1 = new Person('Zhang San', 20, 'actor'); person1.sayName(); // As a normal function call: Person('Li Si', 21, 'teacher'); // Add to window window.sayName(); // Called in the scope of another object var o = new Object(); Person.call(o, 'Small White', 21, 'doctor'); o.sayName();
Major issues with using constructors
- Each method is recreated on each instance, and if the sayName function is transferred outside the constructor, inside the constructor, the sayName property is set to equal the global sayName function. This solves the problem of two functions doing the same thing, but the new problem is that a function defined in a global scope can actually only be called by an object, which makes the global scope a bit understated.
- What's more unacceptable is that if an object needs to define many methods, then it needs to define many global functions, so our custom reference type is not encapsulated at all.
- These problems can be solved by using a prototype pattern.
Prototype mode
Operators and Methods
- instanceof: Determine the relationship between the prototype and the instance
person1 instanceof Person - isPrototypeOf(): Determines the relationship Person between the prototype and the instance. Prototype. IsPrototypeOf (person1)
- Object.getPrototypeOf(): Returns the value Object of [[Prototype]]. GetPrototypeOf (person1) == Person. Prototype
- hasOwnProperty(): Detects whether an attribute exists in an instance or in a prototype.
in
- The in operator returns true when a given property is accessible through an object, whether it exists in an instance or in a prototype
- Using both hasOwnProperty() method and in operator, you can determine whether the property exists in the object or in the prototype
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object); }
- When using a for-in loop, all enumerable properties (instance properties + prototype properties) that can be accessed through the object are returned.
- Object.keys(): Gets all enumerable instance properties on the object
Object.getOwnPropertyNames(): Gets all instance properties, whether enumerable or not.
Notes for prototype use
function Person(){} Person.prototype = { name: 'Ha', age: 29, sayName: function(){} } /* This overrides the default prototype object, and the constructor property no longer points to Person */ // Reset Constructor Object.defineProperty(Person.prototype, "constructor", { eumerable:false, value: Person })
Combination mode
- Constructor mode is used to define instance properties, while prototype mode is used to define methods and shared properties. As a result, each instance will have its own copy of the instance properties, but at the same time it will share references to the methods, minimizing memory savings. In addition, this pattern supports passing parameters to constructors.
- Example
function Person(name) { this.name = name; this.friends = ["Shelby", "Court"] } Person.prototype = { constructor: Person, sayName: function() { console.log(this.name) } } var person1 = new Person('Ha'); var person2 = new Person('Xi'); person1.friends.push("Van") console.log(person1.friends) //"Shelby,Court,Van" console.log(person2.friends) //"Shelby,Court" console.log(person1.friends === person2.friends) //false console.log(person1.sayName === person2.sayName) // true
Dynamic prototype mode
- Dynamic prototype mode encloses all information in the constructor while maintaining the advantage of using both the constructor and the prototype by initializing the prototype in the constructor (only if necessary). In other words, you can decide whether you need to initialize the prototype by checking whether a method that should exist is valid.
- Example
function Person(name) { this.name = name; if (typeof this.sayName != "function") { Person.prototype.sayName = function() { console.log(this.name) } } } var person1 = new Person("Ha") person1.sayName() // Ha
Parasitic constructor mode
- The basic idea of this pattern is to create a function that simply encapsulates the code that creates the object and then returns the newly created object
- Example
function Person(name) { var o = new Object() o.name = name; o.sayName = function() { console.log(this.name) } return o } var person1 = new Person('Ha') person1.sayName() // 'Ha'
Characteristic:
- In addition to using the new operator and calling the wrapper function used a constructor, this pattern is exactly the same as the factory pattern. The constructor returns a new object instance by default without returning a value, and by adding a return statement at the end of the constructor, you can override the value returned when the constructor is called.
- This pattern can be used in special cases to create constructors for objects. If you create a special array with additional methods, you can use this pattern because the Array constructor cannot be modified directly
- Regarding the parasitic constructor pattern, there is no relationship between the returned object and the constructor or the prototype properties of the constructor; You cannot rely on the instanceof operator to determine the object type
Secure Constructor Mode
- Secure objects are best suited in secure environments where this and new are prohibited, or to prevent data from being used by other applications. A safe constructor follows a pattern similar to a parasitic constructor, but differs in two ways: one is that the instance of the newly created object does not reference this, and the other is that the constructor is called without the new operator
- Example
function Person(name) { var o = new Object() o.name = name; o.sayName = function() { console.log(name) } return o } var person1 = Person('Ha') person1.sayName() // 'Ha'
inherit
- Many OO languages support two forms of inheritance: interface inheritance and implementation inheritance. Interface inheritance inherits only method signatures, while implementation inheritance inherits the actual methods.
Implementing inheritance is only supported in ECMAScript, and its implementation inheritance relies mainly on prototype chains. The basic idea is to use prototypes to let one reference type inherit the properties and methods of another.
Prototype Chain
- The prototype chain is built by assigning instances of one type to the prototype of another constructor.
- Example
function SuperType(){ this.colors = ["red","blue","green"] } function SubType(){} SubType.prototype = new SuperType() var instance1 = new SubType() instance1.colors.push("black"); console.log(instance1.colors) // "red,blue,green,black" var instance2 = new SubType() console.log(instance2.colors) // "red,blue,green,black"
Prototype chain issues:
The main problem comes from the prototype that contains the value of the reference type. When inheriting through the prototype, the prototype actually becomes an instance of another type, so the original instance properties become the prototype properties of the present.Borrowing constructors (also known as fake objects or classical inheritance)
- To solve the problem caused by the inclusion of reference type values in the prototype, the basic idea of a constructor is to call a supertype constructor inside a subtype constructor. Functions are simply objects that execute code in a particular environment, because constructors can also be executed on newly created objects (in the future) by using apply() and call() methods
- Example
function SuperType(){ this.colors = ["red","blue","green"] } function SubType(){ SuperType.call(this) } var instance1 = new SubType() instance1.colors.push("black") console.log(instance1.colors) // "red,blue,green,black" var instance2 = new SubType() console.log(instance2.colors)
Characteristic
- Pass-through parameters:
Borrowing constructors has a great advantage over prototype chains in that they allow you to pass parameters to supertype constructors in subtype constructors. - Problems Borrowing Constructors
If you just borrow a constructor, you will not be able to avoid the problem with the constructor pattern -- methods are defined in the constructor, because function reuse is impossible to talk about, and methods defined in the supertype's prototype are also invisible to subtypes, resulting in all types using only the constructor pattern. Given these issues, the technique of borrowing constructors is rarely used alone.
- Pass-through parameters:
Combinatorial inheritance (also known as pseudoclassical inheritance)
- Refers to an inheritance pattern that combines prototype chains with borrowed constructor techniques to bring their strength into play. The idea behind this is to use prototype chains to inherit prototype attributes and methods, and borrow constructors to inherit instance attributes. This enables function reuse by defining methods on the prototype and ensures that each instance has its own properties
- Example
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"] } SuperType.prototype.sayName = function() { console.log(this.name) } function SubType(name, age) { SuperType.call(this, name) // The second call to SuperType() this.age = age } SubType.prototype = new SuperType() // First call to SuperType() SubType.prototype.sayAge = function() { console.log(this.age) } var instance1 = new SubType("Ha", 29) instance1.colors.push("black") console.log(instance1.colors) //"red,blue,green,black" instance1.sayName() //"Ha" instance1.sayAge() //29 var instance2 = new SubType("Xi", 27) console.log(instance2.colors) //"red,blue,green" console.log(instance2.sayName) //"Xi" console.log(instance2.sayAge)
shortcoming
- The biggest problem with combinatorial inheritance is that in any case, a supertype constructor is called twice: once when a prototype of a subtype is created; The other time is inside the subtype constructor.
- Subtypes eventually contain all instance properties of supertype objects, but we have to override them when we call the subtype constructor
Prototype Inheritance
- ECMAScript 5 passes through Object. The create() method normalizes prototype inheritance. This method takes two parameters: an object to prototype the new object and (optionally) an object to define additional properties for the new object. In the case of passing in a parameter, Object.create() behaves the same as the object() method
- Inside the function, a temporary constructor is created, the incoming object is prototyped, and a new instance of the temporary type is returned. Essentially, object() makes a shallow copy of the object passed in
- Example
function object(o){ function F(){} F.prototype = o; return new F(); }
Parasitic Inheritance
- Parasitic inheritance is similar to parasitic constructors and factory patterns in that it creates a function that encapsulates the inheritance process only, enhances the object internally in some way, and returns the object
- Example
function createAnother(original){ var clone = object(original); clone.sayHi = function(){ console.log('hi'); }; return clone; }
Parasitic Combinatorial Inheritance
- Solves the problem of combining inheritance calling supertype constructors twice.
- Parasitic combinatorial inheritance inherits properties by borrowing constructors and methods by mixing prototype links. The basic idea behind this is that instead of calling a supertype constructor to specify the prototype of a subtype, all we need is a copy of the supertype prototype. Essentially, parasitic inheritance is used to inherit the supertype's prototype and assign the result to the prototype of the subtype.
- Example
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } /* Inside the function, the first step is to create a copy of the supertype prototype, and the second step is to add the constructor attribute to the created copy to compensate for the default constructor attribute lost by overriding the prototype. Finally, assign the newly created object (that is, the copy) to the prototype of the subtype. */ function SuperType(name){ this.name = name; thsis.colors = ["red","blue","green"] } SuperType.prototype.sayName = function(){ console.log(this.name) } function SubType(name, age){ SuperType.call(this,name) this.age = age } inheritPrototype(SubType, SuperType) SubType.prototype.sayAge = function(){ console.log(this.age) }