zoukankan      html  css  js  c++  java
  • JavaScript 原型继承

    JavaScript 中虽然有对象的概念,但它并不是一门严格意义上的面向对象编程的语言。

    尽管 ES6 引入了 class 关键字,但是本质上仍然是对原型链的操作。

    通过修改 JavaScript 的原型,可以实现类之间的继承关系。

    首先用 function 关键字定义一个 ParentClass

     1 function ParentClass(props) {
     2     this.alpha   = props.alpha || 1.0;
     3     this.color   = props.color || [0.8, 0.8, 0.8];
     4     console.log("ParentClass constructor");
     5 };
     6 
     7 ParentClass.prototype = {
     8     constructor: ParentClass,
     9 
    10     init: function(gl) {
    11         console.log("ParentClass.proptotype.init");
    12     },
    13 
    14     paint: function(gl, addon) {
    15         console.log("ParentClass.proptotype.paint");
    16     }
    17 }

    并且在 ParentClass 的原型上定义了 init 和 paint 函数。注意这里的构造器仍然是 ParentClass。

    再用 function 定义 ChildClass

    1 function ChildClass(props) {
    2     ParentClass.call(this, props);
    3     this.sides = props.sides || 24;
    4     console.log("ChildClass constructor");
    5 }

    这里的 ParentClass 的 call 函数相当于在 ChildClass 继承了 ParentClass 之后调用 super 函数,也就是调用父类 ParentClass 的构造器。

    当然这里还没有让  ChildClass 继承 ParentClass,如果调用 ChildClass 的 init 方法就会报错。

    在调用 ChildClass 对象的方法时,首先就会在 ChildClass 的原型上查找是否有同名的方法,如果找不到方法,就会到原型链的上一层查找,直到 Object,

    如果这个时候仍然找不到对应名称的方法,就会报错了。原型链过长的话就会影响运行速度。

    要做到对象间的继承,就要修改 ChildClass 的原型链,把 ChildClass 的原型链指向 ParentClass。

    这里直接给出廖雪峰的继承代码

    1 /**
    2   * this function is copied from liaoxuefeng's javascript tutorial
    3 **/
    4 inherits = function(child, parent) {
    5     var F = function() {};
    6     F.prototype = parent.prototype;
    7     child.prototype = new F();
    8     child.prototype.constructor = child;
    9 }

    通过一个中间函数 F,把它的原型链指向 parent 的父类,再把子类的原型改为 F 函数 new 出来的对象。child 的构造器当然还应该是 child 自己。

    注意这里的 child 和 parent 应该都是指向 function 的对象(而不是用 new 关键字创造的对象)

    1 inherits(ChildClass, ParentClass);  // ChildClass 继承 ParentClass

    创建子类的对象

    1 var child = new ChildClass({}); 

    那么就会输出两行内容,分别是:

    ParentClass constructor
    ChildClass constructor 

    再看看子类中相应的属性或方法:

    1 console.log(child.alpha);  // 1
    2 
    3 child.paint();  // ParentClass.prototype.paint

    默认的 alpha 值是1,paint 继承于 ParentClass,所以分别输出 1 和 ParentClass.prototype.paint

    要重写 ChildClass 的 paint 方法,直接修改原型上的 paint 属性,指向别的函数就好了:

    1 ChildClass.prototype.paint = function() { console.log("ChildClass.prototpye.paint")};
    2 child.paint();     // ChildClass.prototype.paint
    3 ChildClass.paint;  // undefined

    再调用 child.paint(),输出的就是 ChildClass.prototype.paint

    关于 ChildClass 原型上的属性,只有在 new 出了对象后,由对象调用,而 ChildClass 本身是没有 paint 属性的。

    再看一个例子:

    1 ChildClass.testPaint = function() {console.log("ChildClass.testPaint")};
    2 child = new ChildClass({});
    3 child.testPaint  // undefined

    如果直接给 ChildClass 添加 testPaint 属性(方法),new 出来的对象不能访问相应的属性(方法)。

    那么就可以这样理解,ChildClass 原型上的属性可以被 new 出来的对象访问,相当于 Java 中类中的普通方法;

    而直接在 ChildClass 上添加属性,只能被 ChildClass 访问,而不能被 new 出来的对象访问,相当于 Java 中类的静态方法。

    另外,我尝试了下直接将子类(姑且这么叫吧)的原型直接指向父类的对象中,好像没有问题:

    1 function SecChildClass(props) {
    2     ParentClass.call(this, props);
    3     this.sides = props.sides || 24;
    4     console.log("SecChildClass constructor");
    5 }
    6 
    7 SecChildClass.prototype = new ParentClass({});  //  ParentClass constructor
    8 SecChildClass.constructor = SecChildClass;

    当然在修改 SecChildClass 的原型链,指向 ParentClass 的对象时,就会执行一遍 ParentClass 的构造器,所以这个方式确实并不好。

    再看看相应对象的属性

    1 var c2 = new SecChildClass({});
    2 //  ParentClass constructor
    3 //  SecChildClass constructor
    4 
    5 c2.alpha;   // 1
    6 c2.paint();  // ParentClass.prototype.paint

    创造对象时就会输出两行内容,因为在子类的构造器里首先调用的是父类的 call 方法,所以首先执行的是父类的构造器。

    值得一提的是,如果在子类的构造器中就调用 inherits 函数,传入 this 是没有用的,因为 this 指向的是子类的对象而非 ChildClass,这样的继承是没有效果的。

    reference

    廖雪峰 JavaScript 教程

    本博客由 BriFuture 原创,并在个人博客(WordPress构建) BriFuture's Blog 上发布。欢迎访问。
    欢迎遵照 CC-BY-NC-SA 协议规定转载,请在正文中标注并保留本人信息。
  • 相关阅读:
    活动精彩实录 | 在Kubernetes上自动运行Cassandra
    行业动态 | 利用云端Cassandra实时推送个性化广告
    技术基础 | 用JSON在抖音上发布动态——使用Stargate即可轻松实现
    Apache Cassandra——可扩展微服务应用程序的持久数据存储
    使用微服务构建现代应用程序
    何为“混合云”?
    Linux基本命令(5)管理使用者和设立权限的命令
    Linux基本命令(4)有关关机和查看系统信息的命令
    Linux基本命令(3)文件备份和压缩命令
    Linux基本命令(2)有关磁盘空间的命令
  • 原文地址:https://www.cnblogs.com/brifuture/p/8288616.html
Copyright © 2011-2022 走看看