由浅入深讲解Javascript继承机制与simple-inheritance源码分析

作者:mrr 时间:2024-05-10 14:06:50 

老生常谈的问题,大部分人也不一定可以系统的理解。Javascript语言对继承实现的并不好,需要工程师自己去实现一套完整的继承机制。下面我们由浅入深的系统掌握使用javascript继承的技巧。

1. 直接使用原型链

这是最简粗暴的一种方式,基本没法用于具体的项目中。一个简单的demo如下:


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();

这种方式的问题是原型中的属性会被所用实例共享,通过一个实例改变一个继承过来的属性时,会影响到其他实例。,这显然不是一种常规意义上的继承。

2.使用构造函数

构造函数本质上也只是一个函数而已,可以在任何作用域中调用,在子构造函数中调用父构造函数,就可以实现简单的继承。


function SuperType(){
 this.colors = {"red","blue","green"}
}
function SubType(){
 SuperType.call(this);  
}
var instance = new SubType();

这种实现避免了多个实例共享属性的问题,但是又出现了新的问题,比如没法共享函数,而且 instance instanceof SuperType 为false。

3. 组合使用原型和构造函数


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();

组合使用原型和构造函数是javascript中最常用的继承模式。使用这种方式,每个实例都有自己的属性,同时可以共享原型中的方法。但是这种方式的缺点是:无论什么情况,都会调用两次超类构造函数。一次是在创建子类原型时,另一次是在子类构造函数内部。这种问题该怎么解决呢?

4. 寄生组合式继承

SubType的原型并不一定非要是SuperType的实例,只需是一个构造函数的原型是SuperType的原型的普通对象就可以了。Douglas Crockford的方法如下:


function obejct(o){
 function F(){};
 F.prototype = o;
 return new F();
}

其实这也就是ES5中Object.create的实现。那么我们可以修改本文中的第3种方案:


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();

其实寄生组合式继承已经是一种非常好的继承实现机制了,足以应付日常使用。如果我们提出更高的要求:比如如何在子类中调用父类的方法呢?

5.simple-inheritance库的实现

看这么难懂的代码,起初我是拒绝的,但是深入之后才发现大牛就是大牛,精妙思想无处不在。我对每一行代码都有详细的注释。如果你想了解细节,请务必详细研究,读懂每一行。我觉得这个实现最精妙的地方就是按需重写父类方法,在实例对象中可以通过_super调用父类的同名方法,类似于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;
 }
})();

通过使用simple-inheritance库,我们就可以通过很简单的方式实现继承了,是不是发现特别像强类型语言的继承。


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();

由浅入深讲解Javascript继承机制与simple-inheritance源码分析,希望本文分享能够帮助到大家。

标签:javascript,继承,simple,inheritance
0
投稿

猜你喜欢

  • python使用matplotlib绘制柱状图教程

    2021-01-29 20:20:00
  • Python详解argparse参数模块之命令行参数

    2021-06-20 21:16:18
  • php中-> 、=>、::、$this->四种常见符号使用方法技巧

    2023-05-30 07:46:24
  • js表格拖选动态效果COOL而实用

    2007-08-05 12:07:00
  • 了解ASP的基本语法和变量

    2008-01-16 13:03:00
  • 如何解决tensorflow恢复模型的特定值时出错

    2023-12-22 14:59:36
  • 解决pycharm每次打开项目都需要配置解释器和安装库问题

    2021-10-19 08:38:24
  • Go语言学习笔记之错误和异常详解

    2024-02-05 11:39:15
  • MySQL数据库的约束使用实例

    2024-01-17 04:36:57
  • python中实现栈的三种方法

    2023-02-10 18:17:14
  • Django 视图层(view)的使用

    2023-07-02 22:52:45
  • python 实现返回一个列表中出现次数最多的元素方法

    2023-01-04 14:36:54
  • 一文让你秒懂精通pip并快速体验深度学习应用【建议收藏】

    2022-07-08 22:27:56
  • Python正则表达式教程之二:捕获篇

    2023-08-24 00:27:39
  • Python 保存矩阵为Excel的实现方法

    2022-07-23 07:05:00
  • python实现超简单端口转发的方法

    2022-12-25 04:00:53
  • Go打包附件内容到执行文件的方法

    2024-04-27 15:32:21
  • laravel 5异常错误:FatalErrorException in Handler.php line 38的解决

    2024-06-05 09:22:11
  • CSS关于Border你可能会不注意的东西

    2007-10-20 13:50:00
  • SQL Server 2005数据转换服务设计问题集锦

    2008-12-26 17:29:00
  • asp之家 网络编程 m.aspxhome.com