zoukankan      html  css  js  c++  java
  • JS继承

    1. 原型链继承

    它是下面这个形式的。

    function F() {
      this.f_age = 8;
      this.f_name = 'father';
    }
    F.prototype.getFAge = function() {
      return this.f_age;
    }
    function S() {
      this.s_age = 3;
    }
    S.prototype = new F();
    S.prototype.getSAge = function() {
      return this.s_age;
    }
    let s = new S();
    console.log(s.getFAge()); // 8
    console.log(s.getSAge()); // 3
    console.log(s.constructor);  // [Function: F]
    console.log(s instanceof S); // true
    console.log(s instanceof F); // true
    

    它的主要过程:

    • 实例化父类 F -> f
    • 将子类的原型对象 S.prototype 指向 f
    • 实例化子类 S -> s

    加深理解:
    s.__proto__ -> S.prototype -> f.__proto__ -> F.prototype

    需要注意的是:

    • 本来 S 的 constructor 是 S,但是因为 S.prototype 重写了,所以现在 constructor 是 F.
    • 查找机制: s -> S.prototype -> F实例 -> F.prototype
    function F() {
      this.name = 'F';
    }
    F.prototype.name = 'F-prototype';
    function S() {}
    S.prototype = new F();
    let s = new S();
    console.log(s.name); // F
    

    原型继承中 子类 重写 父类的方法

    function F() {
      this.x = true;
    }
    F.prototype.getF = function() {
      return this.x;
    }
    function S() {
      this.y = false;
    }
    S.prototype = new F();
    S.prototype.getS = function() {
      return this.y;
    }
    // 重写父类型中的 getF
    S.prototype.getF = function() {
      return '33';
    }
    let s = new S();
    console.log(s.getS()); // false 
    console.log(s.getF()); // 33,   原来的 父类中 返回 true
    delete(S.prototype.getF);
    console.log(s.getF()); // true
    
    如果你想要改变(覆盖)父类的方法,必须在子类的 原型对象 被 父类的实例 赋值之后 覆盖。后来居上,你懂的。
    后来居上的 字面量方法 也一样,如下
    function F() {}
    function S() {}
    S.prototype = new F();
    // 下面的这句话重写了上面这句话。
    S.prototype = {
      getX: function() {
        return '33';
      }
    }
    

    原型链继承的特点和示例代码:

    • a. 原来的父类实例属性 变为了 子类的原型属性。共享性。(也可以是缺点)
    function F() {
      this.colors = ['red', 'blue'];
    }
    function S() {}
    S.prototype = new F();
    let s1 = new S();
    let s2 = new S();
    s1.colors.push('yellow');
    console.log(s1.colors); // ['red', 'blue', 'yellow']
    console.log(s2.colors); // ['red', 'blue', 'yellow'] 
    
    • b. 创建子类型的实例的时候,不能像超类型的构造函数传递参数。(是不足之处)

    2. 借用构造函数 继承

    真是骚操作啊。

    原理:

    • A.谁调用了函数,函数中的 this 就指向谁。
    • B.利用 apply 和 call 在 子类 内部调用 函数。
    function F() {
      this.colors = ['red', 'blue'];
    }
    function S() {
      F.call(this);
    }
    let s1 = new S();
    let s2 = new S();
    s1.colors.push('yellow');
    console.log(s1.colors);  // ['red', 'blue', 'yellow']
    console.log(s2.colors);  // ['red', 'blue']
    

    特点:

    • 属性不会共享。(上面说了)
    • 可以传参
    function F(name) {
      this.name = name;
      this.colors = ['red', 'blue'];
    }
    function S(name) {
      F.call(this, name);
    }
    let s1 = new S('ccc');
    
    • 缺点在于函数复用。

    其实和 JS构造模式 是一个道理。在原型链 中的 共享的函数还是很有必要的。一些属性也是应该共享的。


    3. 组合继承: 跟JS模式中的 组合构造模式 很像。

    • 可以传参。
    • 可以 选择 是否共享属性和方法。(非战争的年代,人们有权选择过自己的生活)
    function F(name) {
      this.name = name;
      this.colors = ['red'];
    }
    F.prototype.getF = function() {
      console.log(this.name);
    }
    function S(name, age) {
      F.call(this, name);
      this.age = age;
    }
    S.prototype = new F();
    S.prototype.constructor = S;
    S.prototype.getS = function() {
      console.log(this.age);
    }
    // 强行让 constructor 为子类,缺点是 constructor 变为可枚举。
    let s1 = new S('s1', 18);
    let s2 = new S('s2', 20);
    s1.colors.push('yellow');
    console.log(s1);    // S {name: 's1', colors: ['red', 'yellow'], age: 18}
    console.log(s2);    // S {name: 's2', colors: ['red'], age: 20}
    delete(s1.colors);      // 删掉了 子类实例中,借用父类构造函数继承的 colors
    console.log(s1.colors); // 子类实例中:父类原型中的 colors 还是存在的。
    // ---------------------------------------------------------------
    console.log(s1.constructor); // [Function: S]
    console.log(Object.keys(S.prototype)); // 其中包含 constructor 属性
    

    4. 原型式继承: 可以说是 ES3中对 ES5中 Object.create 的实现了。

    缺点很明显:共享属性。

    function Create(o) {
      function F(){};
      F.prototype = o;
      return new F();
    }
    let person = {
      name: '123',
      colors: ['red']
    }
    let f1 = Create(person);
    let f2 = Create(person);
    f1.colors.push('yellow');
    console.log(f2.colors);   // ['red', 'yellow']
    
    ES5中的 Object.create(用作新对象原型的对象,可选的定义额外属性的对象)
    • 若是没有第二个参数,和我们上方自己写的 Create 方法相同。
    let person = {
      name: '123',
      colors: ['red']
    }
    let f1 = Object.create(person);
    let f2 = Object.create(person);
    f1.colors.push('yellow');
    console.log(f2.colors);   // ['red', 'yellow']
    
    • 若是写了第二个参数,则会覆盖掉原型中的 属性。
    let person = {
      name: '123',
      colors: ['red']
    }
    let f1 = Object.create(person, {
      colors: {
        value: ['red']
      }
    });
    let f2 = Object.create(person, {
      colors: {
        value: ['red']
      }
    });
    f1.colors.push('yellow');
    console.log(f2.colors);   // ['red']
    

    在没有必要兴师动众地 创建构造函数,只是想让 一个对象 与 另外一个对象保持 相似的情况下,使用 原型式继承 就可以了。


    5. 寄生组合式继承。

    产生的原因是组合继承 的缺点:

    • 调用两次父类。
      • 1.创建子类原型的时候
      • 2.子类型构造函数内部的调用
    • 最后造成的结果。子类会包含 父类的全部 实例属性。并且在调用子类构造函数的时候会重写(覆盖)一些属性。
    function Create(o) {
      function F() {};
      F.prototype = o;
      return new F();
    }
    // 1. 这一步为了 子类继承 父类的原型
    function Inherit(subType, superType) {
      let prototype = Create(superType.prototype);
      // 只承包了 父类的原型。
      prototype.constructor = subType;
      // 增强对象,重写 被重写的 constructor
      subType.prototype = prototype;
    }
    
    function F(name) {
      this.name = name;
      this.colors = ['blue'];
    }
    F.prototype.say = function() {
      console.log(this.name);
    }
    // 2. 这一步为了 子类继承 父类的一些构造函数内部的东西
    function S(name, age) {
      F.call(this, name);
      this.age = age;
    }
    Inherit(S, F);
    

    疑问:其实本质上没有啥差别。主要的骚操作就要在于:子类的原型继承的地方改了下。


    complete.

  • 相关阅读:
    js获取当前页面url网址等信息
    jQuery 复选框全选/取消全选/反选
    js获取日期实例之昨天今天和明天、后天
    mysql不重启修改参数变量
    php 读取功能分割大文件实例详解
    php批量删除数据库下指定前缀的表
    PHP遍历目录返回统计目录大小实例
    PHP二维数组如何根据某个字段排序
    PHP 如何获取二维数组中某个key的集合(高性能查找)
    jQuery 隐藏与显示 input 默认值
  • 原文地址:https://www.cnblogs.com/can-i-do/p/8645390.html
Copyright © 2011-2022 走看看