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


Posted in Javascript onDecember 13, 2015

老生常谈的问题,大部分人也不一定可以系统的理解。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 相关文章推荐
[Web]防止用户复制页面内容和另存页面的方法
Feb 06 Javascript
再谈javascript面向对象编程
Mar 18 Javascript
javascript 兼容所有浏览器的DOM扩展功能
Aug 01 Javascript
jquery实现点击弹出带标题栏的弹出层(从右上角飞入)效果
Sep 19 Javascript
javascript闭包(Closure)用法实例简析
Nov 30 Javascript
JS控制文本域只读或可写属性的方法
Jun 24 Javascript
使用weixin-java-tools完成微信授权登录、微信支付的示例
Sep 26 Javascript
Vue源码学习之关于对Array的数据侦听实现
Apr 23 Javascript
layui table表格数据的新增,修改,删除,查询,双击获取行数据方式
Nov 14 Javascript
vue element 中的table动态渲染实现(动态表头)
Nov 21 Javascript
微信小游戏中three.js离屏画布的示例代码
Oct 12 Javascript
uniapp微信小程序实现一个页面多个倒计时
Nov 01 Javascript
分享Javascript实用方法二
Dec 13 #Javascript
JavaScript判断按钮被点击的方法
Dec 13 #Javascript
jquery插件uploadify实现带进度条的文件批量上传
Dec 13 #Javascript
JavaScript代码判断点击第几个按钮
Dec 13 #Javascript
JavaScript模块化开发之SeaJS
Dec 13 #Javascript
node.js require() 源码解读
Dec 13 #Javascript
JavaScript 模块的循环加载实现方法
Dec 13 #Javascript
You might like
基于数据库的在线人数,日访问量等统计
2006/10/09 PHP
PHP读取目录下所有文件的代码
2008/01/07 PHP
php adodb分页实现代码
2009/03/19 PHP
第七章 php自定义函数实现代码
2011/12/30 PHP
PHP中替换键名的简易方法示例详解
2014/01/07 PHP
twig模板获取全局变量的方法
2016/02/05 PHP
jquery1.4 教程二 ajax方法的改进
2010/02/25 Javascript
JavaScript 学习历程和心得分享
2010/12/12 Javascript
JS无限树状列表实现代码
2011/01/11 Javascript
js 中的switch表达式使用示例
2020/06/03 Javascript
JS中解决谷歌浏览器记住密码输入框颜色改变功能
2017/02/13 Javascript
一道面试题引发的对javascript类型转换的思考
2017/03/06 Javascript
详解nodejs中的process进程
2017/03/19 NodeJs
Vue实现数字输入框中分割手机号码的示例
2017/10/10 Javascript
vuex进阶知识点巩固
2018/05/20 Javascript
打通前后端构建一个Vue+Express的开发环境
2018/07/17 Javascript
JavaScript日期工具类DateUtils定义与用法示例
2018/09/03 Javascript
Vue.js + Nuxt.js 项目中使用 Vee-validate 表单校验
2019/04/22 Javascript
详解vue更改头像功能实现
2019/04/28 Javascript
纯 JS 实现放大缩小拖拽功能(完整代码)
2019/11/25 Javascript
JS自定义滚动条效果
2020/03/13 Javascript
用vite搭建vue3应用的实现方法
2021/02/22 Vue.js
python 远程统计文件代码分享
2015/05/14 Python
基于Python实现定时自动给微信好友发送天气预报
2018/10/25 Python
python 安装impala包步骤
2020/03/28 Python
Python局部变量与全局变量区别原理解析
2020/07/14 Python
使用Python操作MySQL的小技巧
2020/09/10 Python
使用Python将xmind脑图转成excel用例的实现代码(一)
2020/10/12 Python
localStorage的过期时间设置的方法详解
2018/11/26 HTML / CSS
凯特·丝蓓英国官网:Kate Spade英国
2016/11/07 全球购物
美国电子产品折扣网站:Daily Steals
2017/05/20 全球购物
销售高级职员求职信
2013/10/29 职场文书
高级方案规划工程师岗位职责
2013/11/29 职场文书
购房意向书范本
2014/04/01 职场文书
小学优秀班干部事迹材料
2014/05/25 职场文书
教你怎么用Python操作MySql数据库
2021/05/31 Python