美文网首页程序员
JS对象继承相关

JS对象继承相关

作者: Ekolia | 来源:发表于2019-03-04 15:56 被阅读0次

    propertype

    在JavaScript中通过构造函数来构造对象
    每个函数有他的propertype指向该构造函数构造的原型
    每个propertype都有一个constructor方法来构造具体对象
    需要在每次修改propertype的时候,注意修正constructor
    构造的对象没有propertype(es6中对象有),这个属性是构造函数独有的
    构造生成的对象会有一个__ proto __指向当前构造函数的propertype

    属于同一个构造函数生成的obj.__ proto __ 相同
    而每个prototype也可以有它的__ proto __ 构造函数继承另一个对象
    实例化的对象也会顺着prototype生成__ proto __ .__ proto __ .__ proto __ ...
    实现继承,也就是原型链
    而Object.propertype就是原型链的顶端

    继承的具体实现

    构造函数绑定

    function Man (){
        People.call(this, arguments) // apply
    }
    

    Prototype模式

    function Man(){}
    Man.prototype = new People()
    Man.prototype.constructor = Man
    

    如果不修改constructor的话,后辈构造函数会依然使用父辈的constructor而不是新定义的构造函数。

    直接继承prototype

    function Man(){}
    Man.prototype = People.prototype
    Man.prototype.constructor = Man
    

    这样写执行效率会高很多,但之后修改prototype会直接修改到父辈的prototype中
    第二行扭转constructor也会对父辈生效

    空对象做媒介

    const f = function(){}
    f.prototype = People.prototype
    Man.prototype = new f()
    Man.prototype.constructor = Man
    // Man.uber = People.prototype 备用
    

    es5 Object

    es5新增部分方法可以更灵活的创建对象和继承

    Object.create()
    Object.getPrototypeOf()
    Object.setPrototypeOf()
    // example
    // Object.defineProperty
    var Man = function(name) {
      Object.defineProperty(this, 'name', {
        writable: false,
        value: name
      });
    };
    // Object.create
    Man.prototype = Object.create(People.prototype, {
      run: {
        writable: false,
        value: function() { console.log('running...'); }
      }
    });
    Man.prototype.constructor = Man
    

    es6 class语法

    语法糖居多

    class People{
        constructor(){}
        toString(){}
    }
    class Man extends People {}
    

    将类所有方法写入propertype
    子类constructor必须写super,否则报错(功能相当于之前的call,apply),而且必须在super函数后才可以写子类的构造逻辑
    super函数只能在constructor函数中调用

    类同时有propertype和 __ proto __ 两条继承链
    前者指向父类的__ proto __ 总是指向父类的__ proto __
    后者总是指向父类

    多重继承

    javascript中不存在多重继承

    es6提出可以用mix的模式实现多重继承

    const a = {
      a: 'a'
    };
    const b = {
      b: 'b'
    };
    const c = {...a, ...b}; // {a: 'a', b: 'b'}
    class d extends c {}
    

    更复杂的具体实现

    function mix(...mixins) {
      class Mix {
        constructor() {
          for (let mixin of mixins) {
            copyProperties(this, new mixin()); // 拷贝实例属性
          }
        }
      }
      for (let mixin of mixins) {
        copyProperties(Mix, mixin); // 拷贝静态属性
        copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性
      }
      return Mix;
    }
    
    function copyProperties(target, source) {
      for (let key of Reflect.ownKeys(source)) {
        if ( key !== 'constructor'
          && key !== 'prototype'
          && key !== 'name'
        ) {
          let desc = Object.getOwnPropertyDescriptor(source, key);
          Object.defineProperty(target, key, desc);
        }
      }
    }
    
    class d extends mix(a, b) {}
    

    proxy & reflect

    顾名思义,为对象架空一层代理,相当于python中的Decorater

    const p = new proxy(targe, handler)
    // handler为空时直接访问对象
    

    Reflect可以将Object相关的操作变成函数式行为,将target作为对象传输进去,而且也修正了一些返回结果,使其更加合理。

    // example
    Proxy(target, {
      set: function(target, name, value, receiver) {
        if (Reflect.set(target,name, value, receiver)) {
          console.log('property ' + name + ' on ' + target + ' set to ' + value);
        }
        return success;
      }
    });
    // 确保对象操作正确执行
    // receiver 默认指向target, 是最后
    if (Reflect.set(target, name, value, receiver)) {
        // do something
    }
    // 相当于
    try {
        Object.defineProperty(receiver || target, name, value)
        // do something
    } catch (e) {
      // failure
    }
    

    相关文章

      网友评论

        本文标题:JS对象继承相关

        本文链接:https://www.haomeiwen.com/subject/elyiuqtx.html