Most people may not be able to understand this issue in a systematic way. The Javascript language does not implement inheritance well, and engineers need to implement a complete inheritance mechanism themselves. Next, we will master the skills of using JavaScript inheritance from the shallower to the deeper system.
1. Use the prototype chain directly
This is the simplest and crudest method and can hardly be used in specific projects. A simple demo is as follows:
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; } function SubType(){ this.subproperty = false; } //继承 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function(){ return this.subproperty; } var instance = new SubType();
The problem with this approach is that the properties in the prototype will be shared by all instances. Changing an inherited property through one instance will affect other instances. ,This is obviously not inheritance in the conventional sense.
2. Use constructor
The constructor is essentially just a function, which can be called in any scope. By calling the parent constructor in the child constructor, simple inheritance can be achieved.
function SuperType(){ this.colors = {"red","blue","green"} } function SubType(){ SuperType.call(this); } var instance = new SubType();
This implementation avoids the problem of multiple instances sharing attributes, but new problems arise, such as functions cannot be shared, and instance instanceof SuperType is false.
3. Use prototypes and constructors in combination
function SuperType(name){ this.name = name; this.colors = {"red","blue","green"} } SuperType.prototype.sayName = function(){ //code } function SubType(name,age){ SuperType.call(this,name); this.age = age; } SubType.prototype = new SuperType(); var instance = new SubType();
The combined use of prototypes and constructors is the most commonly used inheritance pattern in JavaScript. Using this approach, each instance has its own properties, while methods from the prototype can be shared. But the disadvantage of this approach is that no matter what the situation, the superclass constructor will be called twice. Once when creating the subclass prototype and another time inside the subclass constructor. How to solve this problem?
4. Parasitic combined inheritance
The prototype of SubType does not have to be an instance of SuperType, it only needs to be an ordinary object whose constructor prototype is the prototype of SuperType. Here's how Douglas Crockford does it:
function obejct(o){ function F(){}; F.prototype = o; return new F(); }
In fact, this is the implementation of Object.create in ES5. Then we can modify the third option in this article:
function inheritPrototype(subType,superType){ var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } function SuperType(name){ this.name = name; this.colors = {"red","blue","green"} } SuperType.prototype.sayName = function(){ //code } function SubType(name,age){ SuperType.call(this,name); this.age = age; } inheritPrototype(SubType,SuperType); var instance = new SubTYpe();
In fact, parasitic combined inheritance is already a very good inheritance implementation mechanism, which is enough for daily use. What if we put forward higher requirements: For example, how to call the method of the parent class in the subclass?
5.implementation of simple-inheritance library
Looking at such a difficult-to-understand code, I refused at first, but after getting deeper, I realized that great people are great people, and subtle ideas are everywhere. I have detailed comments for every line of code. If you want to know the details, be sure to study it in detail and read every line. I think the most exquisite part of this implementation is to rewrite the parent class method on demand. In the instance object, the method of the same name of the parent class can be called through _super, similar to the implementation of Java.
(function(){ //initializing用于控制类的初始化,非常巧妙,请留意下文中使用技巧 //fnTest返回一个正则比表达式,用于检测函数中是否含有_super,这样就可以按需重写,提高效率。当然浏览器如果不支持的话就返回一个通用正则表达式 var initializing = false,fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; //所有类的基类Class,这里的this一般是window对象 this.Class = function(){}; //对基类添加extend方法,用于从基类继承 Class.extend = function(prop){ //保存当前类的原型 var _super = this.prototype; //创建当前类的对象,用于赋值给子类的prototype,这里非常巧妙的使用父类实例作为子类的原型,而且避免了父类的初始化(通过闭包作用域的initializing控制) initializing = true; var prototype = new this(); initializing = false; //将参数prop中赋值到prototype中,这里的prop中一般是包括init函数和其他函数的对象 for(var name in prop){ //对应重名函数,需要特殊处理,处理后可以在子函数中使用this._super()调用父类同名构造函数, 这里的fnTest很巧妙:只有子类中含有_super字样时才处理从写以提高效率 prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name])? (function(name,fn){ return function(){ //_super在这里是我们的关键字,需要暂时存储一下 var tmp = this._super; //这里就可以通过this._super调用父类的构造函数了 this._super = _super[name]; //调用子类函数 fn.apply(this,arguments); //复原_super,如果tmp为空就不需要复原了 tmp && (this._super = tmp); } })(name,prop[name]) : prop[name]; } //当new一个对象时,实际上是调用该类原型上的init方法,注意通过new调用时传递的参数必须和init函数的参数一一对应 function Class(){ if(!initializing && this.init){ this.init.apply(this,arguments); } } //给子类设置原型 Class.prototype = prototype; //给子类设置构造函数 Class.prototype.constructor = Class; //设置子类的extend方法,使得子类也可以通过extend方法被继承 Class.extend = arguments.callee; return Class; } })();
By using the simple-inheritance library, we can implement inheritance in a very simple way. Do you find it particularly like the inheritance of a strongly typed language?
var Human = Class.extend({ init: function(age,name){ this.age = age; this.name = name; }, say: function(){ console.log("I am a human"); } }); var Man = Human.extend({ init: function(age,name,height){ this._super(age,name); this.height = height; }, say: function(){ this._super(); console.log("I am a man"); } }); var man = new Man(21,'bob','191'); man.say();
Explain the Javascript inheritance mechanism and simple-inheritance source code analysis from the shallower to the deeper. I hope sharing this article can help everyone.