According to inheritance?
- Implementation of code reuse (Key point)
- Introduce a specification for a type system
What inheritance
Once a subclass is defined, it is a separate and distinct class from its parent class. Subclasses contain the original copy of the parent’s behavior, but they can override all inherited behavior and even define new behavior. Class 4.3 Inheritance on JS you don’t know
Inheritance is when a subclass inherits the characteristics and behavior of its parent class, making its object (instance) have the instance fields and methods of its parent class, or a subclass inherits methods from its parent class, making it have the same behavior as its parent class.
Class & instance
For example, if animals are a class, then man is a subclass of animals. In this sentence, the animal is the parent class and the person is the subclass. A person is a class, but when it comes to individual people, individual individuals, I am an instance of a human being, you are an instance of a human being. We are similar but different, we are all the same, but we are all different.
/ / class
Class Person({ some properties }){... };/ / instance instance
var Me = new Person({ name: "".birth : "". });Copy the code
It is worth mentioning that in JavaScript, there is no concept of class originally, only the concept of prototype, so the classes and instances mentioned in this paper need artificial semantic understanding. It looks like we’re all the same, but it’s actually different. To make it easier to distinguish categories from instances, class names are conventionally capitalized to make them easier to distinguish during development. Meanwhile, in this paper, in order to better distinguish stereotype inheritance from pseudo-class inheritance, we only use core code as the method of inheritance, and do not use the new operator. The new operator is used only for new instances.
Front knowledge
New operator
// Function.method(name, func) => Function.prototype.name = func
Function.method('new'.function(){
// 1. Create a new object, inheriting the prototype object from the constructor function, and implicitly creating an empty object
// 2. Set the scope of the constructor. The new object is connected by [[Prototype]].
var that = Object.create(this.prototype)
// 3. Call the execute constructor function and bind this to the new object. This refers to the new object
var other = this.apply(that, arguments)
// 4. If the constructor returns a value other than an object, return that
return (typeof other === 'object' && other) || that
})
Copy the code
The difference between object.create () and new is that the former creates only the chain of prototypes and does not execute the constructor, while the latter creates the Object, joins the chain of prototypes and executes the interior of the constructor.
A small application of the New operator
function fn() {
this.user = 'hello world'
return 1 // Since 1 is not an object, we return the object inside the constructor
}
var a = new fn()
console.log(a.user) // 'hello world'
// ---------
function fn() {
this.user = 'hello world'
return {} // Put the empty object directly back
}
var a = new fn()
console.log(a.user) // undefined
Copy the code
How to inherit
The original type of Prototype
Away from the traditional but slightly weird idea of “classes” in JS, prototype-based inheritance is much easier to understand than class-based inheritance: an object can inherit from an old object
- Construct an object from a literal
var Animal = {
name: 'animal'.arr: [1].get_name: function () {
console.log('this is inner func ' + this.name)
},
getName: function () {
console.log(this.name + ' hello')
return this.name
},
}
Copy the code
- through
Object.create(Animal)
To construct a subclass
var Cat = Object.create(Animal)
console.log(Object.getPrototypeOf(Cat) === Animal) // true indicates and binds the prototype chain
Cat.says = function () {
console.log('cat ' + this.name)
}
// var mycat = object.create (Cat) // Instances can also be constructed using new
// var mycat2 = object.create (Cat) // Instances can also be constructed using new
var mycat1 = new Cat('cici')
var mycat2 = new Cat('mimi')
mycat.name = 'mimi'
mycat2.getName()
mycat.says()
mycat2.arr.push('2')
console.log(mycat.arr, mycat2.arr) // [1,2] [1,2] indicates that stereotype inheritance cannot isolate the attributes of the parent object
Copy the code
Advanced
- using
object.assign
Implement mixed inheritance. The principle isobject.assign( target, source1, source2 )
将source1
Assigns an enumerable property value totarget
. Note, however, that when you have the same name, the last attribute that appears will be overridden. It is also possible to implement mixed inheritance in this way, but it is not necessary.
summary
The prototype chain inheritance approach can focus on the object itself, taking advantage of the fact that a new object can inherit the properties of an old object. Implement a generic template by constructing properties on object literals and binding methods on the stereotype chain. The child object inherits the attributes and methods of the parent object through object.create. At the same time, the parent object can be added to the child object without attributes or methods to achieve differentiated inheritance. On objects, however, instances interfere with each other because the parent’s prototype chain is shared.
However, when instance 1 modifies the attributes on the prototype chain, the modified contents will also affect Instance 2. There is no isolated attribute between instances, which affects each other. This is because: 1. When the child object has an attribute or method with the same name as the parent object, it will overwrite the attribute or method of the parent object. If there is no such attribute or method, it will search for it layer by layer along the prototype chain until the attribute or method is found. 2, contains the stereotype of the reference type value, which can be modified to affect other reference value objects.
In addition, you can see that a subclass cannot pass parameters to its parent class, or that it cannot pass parameters to its parent class without affecting other instances. This is why inheritance in the prototype chain approach is rarely used directly in practice.
Pseudo-class inheritance (constructor)
This approach often masks its own source mechanism with some syntax problems. Instead of inheriting objects from other objects, new objects are generated through a redundant middle layer (constructor).
The constructor
- If a function call is preceded by the new keyword, the call pattern of subsequent functions becomes the constructor’s call pattern
- This pattern call will implicitly create a link to the function
prototype
The new object, meanwhilethis
Will also be bound to the new object ** (usecall
orapply
Is the core of pseudo-class inheritance - If the function returns something other than an object, it implicitly returns the value of this (the new object)
function Animal(type) {
this.type = type || 'animal'
this.animal = '123'
this.arr = [1]
this.get_name = function ()
console.log('this is inner func ' + this.name)}this.get_type = function () {
console.log(this.type)}}Animal.prototype.getName_proto = function () {
console.log(this.name + ' hello')
return this.name
}
function Cat(name) {// The core iscall,applyThe bindingthis
Animal.call(this.'cat'// To prevent overwriting, it should be at the top of the subclass bodythis.name = name| | 'cat'// will overridethis.get_name = function () {
console.log("this is cat's func " + this.name)}}var mycat1 = new Cat('cici')
var mycat2 = new Cat('mimi')
mycat1.get_name() / /this is cat's func cici
mycat1.get_type() / /mycat1.getName_proto() / /animal prototype funcCannot access methods on the parent prototype chainmycat2.arr.push('2')
console.log(mycat1.arr, mycat2.arr) // [1], [1, '2']Copy the code
-
Little question: Can mycat use the prototype method above Cat?
The key is the new operator. As mentioned earlier, the new operator links the prototype chain + the execution constructor, so mycat is a prototype method that can access the Cat class.
function Cat(name) {
Animal.call(this.'cat')
// your code here. } Cat.prototype.getName_proto =function () {
console.log('prototype')}var mycat1 = new Cat('cici')
mycat1.getName_proto() // 'prototype' is accessible
Copy the code
- When the constructor needs to accept a long list of arguments, it is more friendly to change the function argument list to an object
// var myObj = maker(a,b,c)
var myObj = maker({ first:a, second:b, third:c })
Copy the code
summary
In fact, pseudo-class inheritance (constructors) is done by reusing snippets of code with call or Apply at their core and constructing instances through the new method. Parentclass.call (childClass, args..); parentClass.call (childClass, args..); parentClass.call (childClass, args..) Pass parameters. To prevent the superclass from overwriting the subclass method, the superclass constructor should be called at the top of the subclass function body.
But since only constructors are executed between subclasses of the parent class, but there is no link to the stereotype chain, there is no way for subclasses to use methods on the parent stereotype chain.
Functionalization (parasitic inheritance)
All properties of objects are publicly visible, so we can privatize them through functional inheritance. Some private functions can be defined inside functions, as long as they are not bound to an outward returning object and the external world cannot access the internal properties or methods, thus achieving privatization.
Practical examples:
var Animal = function (spec) {
var that = {} // The object actually thrown
var _name = 'private'
var _age = 0
_private_func = function () {
console.log('this is private function')}// Only those bound to that are exposed to external instances
that.get_name = function () {
console.log(spec.name)
return spec.name
}
that.says = function () {
console.log(spec.saying || "can't say")
return spec.saying || "can't say"
}
that.getPrivateName = function () {
return _name
}
that.addAge = function (year = 1) {
_age += year
return _age
}
that.getAge = function () {
return _age
}
return that
}
Animal._privateFunc = function () {
console.log('private call')}var cat = Animal({ name: 'cat'.saying: 'meow' })
// cat.says()
var cat = function (spec) {
spec.saying = 'meow'
var that = Animal(spec)
that.say_my_name = function () {
console.log(that.says() + spec.name + that.says())
return that.says() + spec.name + that.says()
}
_func = function () {
return 'private'
}
return that
}
// console.log(cat.says())
var mycat = cat({ name: 'cai1' })
var mycat2 = cat({ name: 'cai2' })
mycat.addAge(2)
mycat2.addAge(5)
console.log(cat2.getAge())
Copy the code
summary
Passing in an object (understood as a parasitic object) inside the function enhances the object in some way, and then returns the object so it can be accessed externally. This object is bound only to properties or methods that need to be explicit, and private methods are stored only in the body of functions, thus privatizing properties or methods. Externally referenced objects can only access properties or methods bound to the host object. This pattern applies to any function that can return a new object.
Components *
All functions that are reused are extracted and the focus is on the function itself, not the object. When the object needs the function, the object is passed in to bind the function, thus realizing the reuse of the function code.
- Define a function that takes an object as a parameter. After the function is bound to an argument passed in, it is rethrown, which is equivalent to the object being bound to a function.
var addFunc = function (obj) { obj.name = !! obj.name ? obj.name :'test_name'
obj.Func = function () {
console.log('say my name:' + obj.name)
return 'say my name:' + obj.name
}
return obj
}
var getName = function (obj) { obj.name = !! obj.name ? obj.name :'test_name'
obj.getName = function () {
console.log('get my name:' + obj.name)
return obj.name
}
return obj
}
var cat = { name: 'minya' }
cat = addFunc(cat)
cat = getName(cat) // Bind a new function method to this object by wrapping it functionally. Implement code reuse.
console.log(cat)
cat.Func() // Can be the same as the wrapper function, because the reference is different
cat.getName()
Copy the code
conclusion
This article is organized only for the continuation of the Javascript Essentials book. There are many different types or combinations of different types of content in the Little Red Book. In practice, the above inheritance method does not appear alone. Ref2 provides a similar inheritance pattern to the Little Red Book. Come back next time you have a chance.
Reference:
-
Liao Xuefeng JAVA inheritance
-
💎 read JS inheritance
-
ES6 Tutorial