• 重学前端-面向对象


    对象

    属性

    数据属性

    1.value 值

    2.writable 能否被赋值

    3.enumerable 能否被枚举 for in Object.keys

    4.configurable 能否被删除或者改变属性值

    var a ={a:1}
    Object.getOwnPropertyDescriptor(a,'a') //{configurable: true,enumerable: true,value: 1,writable: true}
    
    Object.defineProperty(a,'a',{value:5,configurable: false,enumerable: false,writable: false})
    
    • 1
    • 2
    • 3
    • 4

    访问器属性

    1.Getter 取值

    2.setter 赋值

    3.enumerable 能否被枚举 for in

    4.configurable 能否被删除或者改变属性值

    let o = {
      b: 1,
      get a() {
        return this.b;
      },
      set a(i) {
        this.b = i;
      },
    };
    
    console.log(o.a); // 1
    o.a = 5;
    console.log(o.a); // 5
    
    
    var o = { b: 1 };
    Object.defineProperty(o, 'a', {
      set: function (value) {
        this.b = value + 100;
      },
      get: function () {
        return this.b;
      },
    });
    
    console.warn(o.a); // 6
    o.a = 6;
    console.warn(o.a); // 106
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    创建对象

    对象的key只能是字符串 和 symbol

    对象字面量

    var id = Symbol();
    var o = { name: 'a', age: 18, [id]: 1 }; // 对象的key 键名是数值
    
    console.warn(o[id]); // 1
    // 获取key string
    console.log(Object.getOwnPropertyNames(o)); //[ 'name', 'age' ]
    console.log(Object.keys(o)); //[ 'name', 'age' ]
    // 获取key Symbol
    console.log(Object.getOwnPropertySymbols(o)); // [ Symbol() ]
    
    // 获取所以属性
    console.log(Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); // [ 'name', 'age', Symbol() ]
    console.log(Reflect.ownKeys(o)); // [ 'name', 'age', Symbol() ]
    
    // 获取属性
    console.log(Object.getOwnPropertyDescriptor(o, 'name')); // { value: 'a', writable: true, enumerable: true, configurable: true }
    console.log(Object.getOwnPropertyDescriptors(o));
    // {
    //   name: { value: 'a', writable: true, enumerable: true, configurable: true },
    //   age: { value: 18, writable: true, enumerable: true, configurable: true },
    //   [Symbol()]: { value: 1, writable: true, enumerable: true, configurable: true }
    // }
    
    for (let i in o) {
      console.log(i); // name age
    }
    
    console.log(o.hasOwnProperty('name')); // true
    console.log(o.hasOwnProperty(id)); // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    缺点:产生大量重复代码

    Object构造函数

    var id = Symbol();
    var o = new Object({ name: 'a', age: 18, [id]: 1 }); // 对象的key 键名是数值
    
    console.warn(o[id]); // 1
    // 获取key string
    console.log(Object.getOwnPropertyNames(o)); //[ 'name', 'age' ]
    console.log(Object.keys(o)); //[ 'name', 'age' ]
    // 获取key Symbol
    console.log(Object.getOwnPropertySymbols(o)); // [ Symbol() ]
    
    // 获取所以属性
    console.log(Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); // [ 'name', 'age', Symbol() ]
    console.log(Reflect.ownKeys(o)); // [ 'name', 'age', Symbol() ]
    
    // 获取属性
    console.log(Object.getOwnPropertyDescriptor(o, 'name')); // { value: 'a', writable: true, enumerable: true, configurable: true }
    console.log(Object.getOwnPropertyDescriptors(o));
    // {
    //   name: { value: 'a', writable: true, enumerable: true, configurable: true },
    //   age: { value: 18, writable: true, enumerable: true, configurable: true },
    //   [Symbol()]: { value: 1, writable: true, enumerable: true, configurable: true }
    // }
    
    for (let i in o) {
      console.log(i); // name age
    }
    
    console.log(o.hasOwnProperty('name')); // true
    console.log(o.hasOwnProperty(id)); // true
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    缺点:产生大量重复代码

    Object.create()

    使用现有的原型来创建对象

    var id = Symbol();
    var o = Object.create({ name: 'a', age: 18, [id]: 1 }); // 对象的key 键名是数值
    
    console.warn(o[id]); // 1
    // 获取key string
    console.log(Object.getOwnPropertyNames(o)); //[ ]
    console.log(Object.keys(o)); //[  ]
    // 获取key Symbol
    console.log(Object.getOwnPropertySymbols(o)); // [  ]
    
    // 获取所以属性
    console.log(Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); // [ ]
    console.log(Reflect.ownKeys(o)); // [ ]
    
    // 获取属性
    console.log(Object.getOwnPropertyDescriptor(o, 'name')); // undefined
    console.log(Object.getOwnPropertyDescriptors(o)); // {}
    
    for (let i in o) {
      console.log(i); // name age
    }
    
    console.log(o.hasOwnProperty('name')); // false
    console.log(o.hasOwnProperty(id)); // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    工厂模式

    function createPerson(name,age) {
        var o = new Object()
        o.name = name;
        o.age = age;
        o.sayName=function() {
            console.log(this.name)
        }
        return o
    }
    
    var p1 = createPerson('a',18)
    var p2 = createPerson('b',18)
    
    console.log(p1 instanceof Object) // true
    console.log(p2  instanceof Object) // true
    
    console.log(p2.__proto__ === p1.__proto__)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    优点:能一次创建多个对象

    缺点:创建的对象没法区别 所有实例的原型都指向同一个对象

    构造函数模式

    function Person(name,age) {
        this.name = name;
        this.age = age;
        this.sayName = function() {
            console.log(this.name)
        }
    }
    var p3 = new Person('a',18)
    p3.sayName() // q
    p3.name = 'aa'
    p3.sayName() // qq
    var p4 = new Person('b',18)
    p4.sayName() // b
    console.log(p3 instanceof Person) // true
    console.log(p4 instanceof Person) // true
    
    console.log(p4.__proto__ === p3.__proto__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    优点:显式的创建了对象 可以识别为一种特点类型
    缺点:每次创建对象时方法都要被创建一次

    原型模式

    function Person1() {
    
    }
    Person1.prototype.name = 'a'
    Person1.age = '18'
    Person1.sayName = function() {
        console.log(this.name)
    }
    var p5 = new Person('a',18)
    p5.sayName() // a
    p5.name = 'aa'
    p5.sayName() // aa
    var p6 = new Person('b',18)
    p6.sayName() // b
    console.log(p5 instanceof Person) // true
    console.log(p5 instanceof Person) // true
    
    console.log(p6.__proto__ === p6.__proto__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    优点:方法不会重建
    缺点:所有属性和方法都共享 不能初始化参数

    • 原型模式优化
      优点:方法不会重建 封装更好 更简单
      缺点:所有属性和方法都共享 不能初始化参数 完全重写了原型 ,导致constructor指向不对,要明确指向constructor
    /**
     * 原型模式优化
     * 优点:方法不会重建 更简单
     * 缺点:所有属性和方法都共享 不能初始化参数 完全重写了原型 ,导致constructor指向不对,要明确指向constructor
     * */
    function Person11() {
    
    }
    Person11.prototype = {
        constructor: Person11,  // 完全重写了原型 ,导致constructor指向不对,要明确指向constructor
        name : 'a',
        age: 18,
        sayName:function() {
            console.log(this.name)
        }
    }
    var p55 = new Person11()
    p55.sayName() // a
    p55.name = 'aa'
    p55.sayName() // aa
    var p66 = new Person1()
    p66.sayName() // a
    console.log(p55 instanceof Person11) // true
    console.log(p55 instanceof Person11) // true
    
    console.log(p66.__proto__ === p55.__proto__) // false
    console.log(Person11.prototype.constructor === Person11) // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    组合模式

    就是把构造模式和原型模式组合起来,私有变量放在构造函数里面,公用方法放在原型链里面

    function Person2(name,age) {
     this.name = name;
     this.age = age
    }
    Person2.prototype.sayName = function() {
        console.log(this.name)
    }
    var p7 = new Person2('a',18)
    p7.sayName() // a
    p7.name = 'aa'
    p7.sayName() // aa
    var p8 = new Person2('b',18)
    p8.sayName() // b
    console.log(p7 instanceof Person2) // true
    console.log(p7 instanceof Person2) // true
    
    console.log(p8.__proto__ === p7.__proto__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    优点:有私有变量 和 共有 方面

    动态原型模式

    就是把组合模式改变下 原型链定义的方法放在构造函数里面,如果实例没有这个方法 就定义这把这个方法放在原型里,
    相比组合模式 封装更多

    function Person3(name,age) {
        this.name = name;
        this.age = age
        if(typeof this.sayName !== 'function') {
            Person3.prototype.sayName = function() {
            console.log(this.name)
           }
            // 或者
            // Person3.prototype = {
            //     constructor: Person3, // 对象字面量会导致原型被覆盖 实例的原型指向错误 需要明确指向constructor
            //     sayName: function() {
            //         console.log(this.name)
            //     }
            // }
        }
    }
    var p7 = new Person3('a',18)
    p7.sayName() // a
    p7.name = 'aa'
    p7.sayName() // aa
    var p8 = new Person3('b',18)
    p8.sayName() // b
    console.log(p7 instanceof Person3) // true 改为对象字面量 变为 false
    console.log(p7 instanceof Person3) // true 改为对象字面量 变为 false
    console.log(p7.__proto__)
    
    console.log(p8.__proto__ === p7.__proto__) // true 改为对象字面量 变为 false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    寄生构造函数

    就是把工厂方法写在构造函数里面 和工厂方法的区别是实例通过new

    function Person4(name,age) {
        var o = new Object()
        o.name = name;
        o.age = age;
        o.sayName=function() {
            console.log(this.name)
        }
        return o
    }
    var p7 = new Person4('a',18)
    p7.sayName() // a
    p7.name = 'aa'
    p7.sayName() // aa
    var p8 = new Person4('b',18)
    p8.sayName() // b
    console.log(p7 instanceof Person4) //  false
    console.log(p7 instanceof Object) //  true
    
    console.log(p8.__proto__ === p7.__proto__) // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    用途:对某个类型进行属性增强时 出于安全考虑不能直接修改原型,可以考虑这种方式

    function SpecialArray() {
         var values = new Array();
         values.push.apply(values, arguments);
         values.toPipedString = function() {
             return this.join("|");
         };
    
         return values;
     }
     var colors = new SpecialArray("red", "blue", "green");
     console.log(colors.toPipedString());    //"red|blue|green"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    稳妥构造函数

    不用this 没有公共属性 不用new
    优点:安全

    function person9(name){
        var o = new Object();
        o.sayName = function(){
            console.log(name);
        };
        return o;
    }
    
    var person1 = person9('kevin');
    
    person1.sayName(); // kevin
    
    person1.name = "daisy";
    
    person1.sayName(); // kevin
    
    console.log(person1.name); // daisy
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    区别

    工厂模式构造函数模式原型模式组合模式动态模式寄生构造函数稳妥构造函数
    描述一个function 里面返回一个对象一个构造函数 this new()声明Prototype构造函数(属性)+原型模式(方法)组合模式改进 把方法定义放在构造函数里面就是工厂函数 只是用new声明就是工厂函数 只是不用this 没有共有属性 只有方法 返回传入的值
    优点简单 能一次创建多个对象实例显式的创建一种类型方法只创建一次既有共有方法也有私有属性封装更好
    缺点创建的对象没法区分类型 都指向object方法每次创建都要创建一次属性和方法公用 不能初始化属性
    用途类型属性增强安全场景

    方法

    • 获取属性

      • Object.getOwnPropertyDescriptor(o, ‘name’) 获取o.name属性 不能获取原型链上的

      • Object.getOwnPropertyDescriptors(o) 获取o全部属性 不能获取原型链上的

    • 枚举

      • Object.getOwnPropertyNames(o) 获取string 的键名 不能获取原型链上的
      • Object.keys(o) 获取string 的键名 不能获取原型链上的
      • Object.getOwnPropertySymbols(o) 获取symbol键名 不能获取原型链上的
      • For in能获取原型链上的键名 拿不到symbol
      • Reflect.ownKeys(o) 不包括原型链上的 包括Symbol
    • o.hasOwnProperty(‘name’) 判断是否是原生属性 不能获取到原型链上的

    var id = Symbol();
    var o = { name: 'a', age: 18, [id]: 1 }; // 对象的key 键名是数值
    co[id]; // 1
    // 获取key string
    Object.getOwnPropertyNames(o); //[ 'name', 'age' ]
    Object.keys(o); //[ 'name', 'age' ]
    // 获取key Symbol
    Object.getOwnPropertySymbols(o); // [ Symbol() ]
    
    // 获取所以属性
    Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o)); // [ 'name', 'age', Symbol() ]
    Reflect.ownKeys(o); // [ 'name', 'age', Symbol() ]
    
    // 获取属性
    Object.getOwnPropertyDescriptor(o, 'name'); // { value: 'a', writable: true, enumerable: true, configurable: true }
    Object.getOwnPropertyDescriptors(o); 
    // {
    //   name: { value: 'a', writable: true, enumerable: true, configurable: true },
    //   age: { value: 18, writable: true, enumerable: true, configurable: true },
    //   [Symbol()]: { value: 1, writable: true, enumerable: true, configurable: true }
    // }
    
    var b = Symbol('b');
    function person() {
      this.name = 'a';
      this.age = 18;
      this[b] = 2;
    }
    
    person.prototype[id] = 1;
    person.prototype.sex = 'girl';
    var p = new person();
    p[id]; // 1
    // 获取key string
    Object.getOwnPropertyNames(p); //[ 'name', 'age' ]
    Object.keys(p); //[ 'name', 'age' ]
    // 获取key Symbol
    Object.getOwnPropertySymbols(p); // [ Symbol(b) ]
    
    // 获取所以属性
    Object.getOwnPropertyNames(p).concat(Object.getOwnPropertySymbols(p)); // [ 'name', 'age', Symbol(b) ]
    Reflect.ownKeys(p); // [ 'name', 'age', Symbol(b) ]
    
    // 获取属性
    Object.getOwnPropertyDescriptor(p, 'name'); // { value: 'a', writable: true, enumerable: true, configurable: true }
    Object.getOwnPropertyDescriptors(p);
    // {
    //   name: { value: 'a', writable: true, enumerable: true, configurable: true },
    //   age: { value: 18, writable: true, enumerable: true, configurable: true }
    //    [Symbol(b)]: { value: 2, writable: true, enumerable: true, configurable: true }
    // }
    
    for (let i in o) {
      console.log(i); // name age
    }
    
    for (let i in p) {
      console.log(i); // name age sex
    }
    
    console.log(o.hasOwnProperty('name')); // true
    console.log(p.hasOwnProperty('name')); // true
    
    console.log(o.hasOwnProperty(id)); // true
    console.log(p.hasOwnProperty(id)); // false
    console.log(p.hasOwnProperty(b)); // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    原型 vs 原型链

    原型

    定义:每个对象创建时都会与之关联一个原型对象,每个对象都会从原型继承属性。

    对象是通过构造函数创建的 这个构造函数有一个属性prototype是一个对象,这个对象就是原型。原型包括了一些公用的属性和方法。构造函数创建的对象会从这个原型中继承这个属性。当找一个属性或者方法时在对象找不到会沿着原型链找。

    function Person(){
    
    }
    Person.prototype.name = 'a'  // 这里的protype就是原型
    var p = new Person()
    console.log(p.name) // a  // p对象会从Person.protype原型那里继承name属性
    
    
    console.log(Person.prototype) // {name:'a'} prototype指向原型对象
    console.log(Object.getPrototypeOf(p) ) // {name:'a'} Object.getPrototypeOf 获取原型对象
    console.log(p.__proto__ === Person.prototype) // true 对象的___proto__指向对象的原型
    console.log(Object.getPrototypeOf(p)=== Person.prototype ) //true
    console.log(Person === Person.prototype.constructor) // true 原型的constructor指向构造函数
    
    console.log(Person.prototype.__proto__) // [Object: null prototype] {} 原型的原型是object
    
    console.log(Person.prototype.__proto__.__proto__) // null 原型链的尽头是null
    
    console.log(Person.prototype.__proto__ === Object.prototype) // true 原型的原型是object的原型
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    构造函数 实例 原型 的关系如上图:

    • 构造函数的prototype指向原型对象
    • 原型对象的constrctor指向构造函数
    • 构造函数new 出来的实例 通过__proto__指向原型对象 Object.getPrototypeOf(实例) 获取实例的原型对象

    原型链

    定义:就是相互关联的原型 形成的一个链条 相互之间通过__proto__连接。原型对象是通过obejct构造函数创建的,原型的原型是object的原型对象, 原型链的尽头时null。
    在这里插入图片描述

    new的原理 手动实现

    1. 首先创建了一个空对象

    2. 然后设置空对象的__proto__作为构造函数的原型prototype

    3. 然后让构造函数的this指向这个对象,允许构造函数

    4. 判断构造函数的返回值 如果是引用类型就返回这个引用类型,如果不是返回创建的对象

      function createNew(fn) {
       const obj = {};
       obj.__proto__ = fn.prototype;
       const result = fn.apply(obj,[...arguments].slice(1));
       return typeof result === 'object' ? result:obj
      }
      
      
      function Person(name,age) {
         this.name = name;
         this.age = age;
      }
      
      Person.prototype.sayName = function() {
            console.log(this.name)
      }
      
      Person.prototype.sayAge = function() {
            console.log(this.age)
      }
      
      const p = createNew(Person,'aa',18)
      p.sayName() // aa
      p.sayAge() // 18xxxxxxxxxx funcfunction createNew(fn) { const obj = {}; obj.__proto__ = fn.prototype; const result = fn.apply(obj,[...arguments].slice(1)); return typeof result === 'object' ? result:obj}function Person(name,age) {   this.name = name;   this.age = age;}Person.prototype.sayName = function() {      console.log(this.name)}Person.prototype.sayAge = function() {      console.log(this.age)}const p = createNew(Person,'aa',18)p.sayName() // aap.sayAge() // 18
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24

    继承

    原型链继承

    利用原型链的特性 让child的prototype原型指向parent的实例 这样构造原型链 child就可以访问parent的元素

    缺点是 1.构造函数的属性共享, 如果是引用类型 一个子类的实例修改了这个属性 其他实例继承到的属性也会改变 2. 无法向父类传参

    /**
     * 原型链继承
     * */
    function Parent(firstName,lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.friend = {
            firstName:'li',
            lastName:'si'
        }
    }
    Parent.prototype.sayName = function () {
        console.log(this.lastName+this.firstName)
    }
    
    function Child(firstName) {
        this.firstName = firstName;
    }
    const parent = new Parent('san','zhang');
    Child.prototype = parent
    const child = new Child('si');
    
    const child1 = new Child('si');
    
    child.sayName() // zhangsi
    parent.sayName() // zhangsan
    console.log(child.friend) // { firstName: 'li', lastName: 'si' }
    
    // 缺点是 构造函数的属性共享, 如果是引用类型 一个子类的实例修改了这个属性 其他实例继承到的属性也会改变
    child1.friend.firstName = 'hua'
    console.log(child1.friend) // { firstName: 'hua', lastName: 'si' }
    console.log(child.friend) // { firstName: 'hua', lastName: 'si' }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    构造函数继承

    可以解决原型链 引用属性共享会导致被子类修改的问题 可以向父类传递参

    缺点:所有属性只能在构造函数创建 方法会被多次创建

    /*构造函数继承*/
    function Parent2(firstName,lastName) {
        this.firstName  = firstName;
        this.lastName = lastName;
        // 缺点是 只能在构造函数里面创建方法
        this.sayName = function () {
            console.log(this.lastName+this.firstName)
        }
        this.friend = {name:'li si'}
    }
    
    function Child2(name,age) {
        Parent2.call(this,...arguments)
    }
    const parent2 = new Parent2('san','zhang');
    const child2 = new Child2('si','zhang');
    
    const child21 = new Child2('wu','zhang');
    
    child2.sayName() // zhangsi
    parent2.sayName() // zhangsan
    console.log(child2.friend) // { name: 'li si' }
    
    
    // 可以解决原型链属性共享的问题,如果是引用类型 一个子类的实例修改了这个属性 其他实例继承到的属性不会改变
    child21.friend.name = 'hua hua'
    console.log(child21.friend) // { name: 'hua hua' }
    console.log(child2.friend) // { name: 'li si' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    组合继承

    利用构造函数+原型链 这样既可以在原型上定义方法,子类也可以继承,又可以让每一个子类有自己的属性 引用类型不会被子类改变 还可以向父类传参

    /**
     * 组合继承
     * */
    function Parent3(firstName,lastName) {
        this.firstName  = firstName;
        this.lastName = lastName;
        this.friend = {name:'li si'}
    }
    
    Parent3.prototype.sayName = function () {
        console.log(this.lastName+this.firstName)
    }
    
    function Child3(firstName,lastName) {
        Parent3.call(this,...arguments);
        this.firstName = firstName;
    }
    Child3.prototype = new Parent3() // 缺点是调用了两次父类构造函数
    const parent3 = new Parent3('san','zhang');
    const child3 = new Child3('si','zhang');
    
    const child31 = new Child3('wu','zhang');
    
    child3.sayName() // zhangsi
    parent3.sayName() // zhangsan
    console.log(child3.friend) // { name: 'li si' }
    
    child31.friend.name = 'hua hua'
    console.log(child31.friend) // { name: 'hua hua' }
    console.log(child3.friend) // { name: 'li si' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    原型式继承

    利用一个父类对象 作为原型 创建其他子类对象,子类对象就会继承父类对象

    缺点:无法传递参数 引用类型会被改

    /**
     * 原型式继承
     * */
    var o = {
        firstName:'san',
        lastName:'zhang',
        sayName:function () {
            console.log(this.lastName+this.firstName)
        },
        friend:{name:'li si'}
    }
    var o1 = Object.create(o)  // 以o为新对象的原型创建对象
    var o2 = Object.create(o)
    o2.firstName='si'
    o1.firstName = 'wu'
    o2.friend = 'haha'
    o1.sayName() // zhangwu
    o2.sayName() //zhangsi
    console.log(o2.friend) // haha
    console.log(o1.friend) //lisi
    
    // Object.create 自己实现 本质是内部有一个构造函数 把这个构造函数的原型指向传入的对象,返回构造函数的实例
    function object(o) {
        function fn() {}
        fn.prototype = o;
        return new fn();
    }
    var o11 = object(o)  // 以o为新对象的原型创建对象
    var o22 = object(o)
    o22.firstName='si'
    o11.firstName = 'wu'
    o22.friend = 'haha'
    o11.sayName() // zhangwu
    o22.sayName() //zhangsi
    console.log(o22.friend) // haha
    console.log(o11.friend) //lisi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    寄生式继承

    就是把 object.create 方法封装到了一个函数里面

    /**
     * 寄生式继承
     * */
    function object(obj) {
      function fn {}
      fn.prototype = obj
      return new fn();
    }
    
    function createAnother(parent) {
        var  o = object(parent)
        o.firstName='si'
        return o
    }
    const oo = {firstName:'san',lastName:'zhang',friend:{name:'lisi'},sayName:function (){console.log(this.lastName+this.firstName)}}
    var o3 = createAnother(oo)
    var o4 = createAnother(oo)
    o3.sayName() // zhangsi
    o3.friend.name = 'huahua'
    console.log(o4.friend) // huahua
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    缺点:无法传递参数 引用类型会被改

    寄生组合式继承

    组合继承+寄生式 能解决组合继承父类构造函数会被调用两次的问题

    function inherit(subType,superType) {
        var prototype = Object.create(superType.prototype);
        prototype.constructor = superType;
        subType.prototype = prototype;
    }
    
    function SuperType(firstName,lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.friend = {
            name:'lisi'
        }
    }
    SuperType.prototype.sayName= function () {
        console.log(this.lastName+this.firstName)
    }
    function SubType(firstName,lastName) {
       SuperType.call(this,...arguments)
        this.age = 18;
    }
    
    inherit(SubType,SuperType)
    const parent5 = new SuperType('san','zhang')
    parent5.sayName() // zhangsan
    const child5 = new SubType('si','zhang')
    const child6 = new SubType('wu','zhang')
    child5.sayName() // zhangsi
    child6.sayName() // zhangwu
    
    console.log(parent5.friend) // { name: 'lisi' }
    child5.friend.name ='haha'
    console.log(child5.friend) // { name: 'haha' }
    console.log(child6.friend) // { name: 'lisi' }
    console.log(parent5.friend) // { name: 'lisi' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    区别

    继承方式描述优点缺点
    原型链继承利用原型链上可以访问上一级属性,让子类的protype属性等于父类的实例 SubType.prototype=new SuperType()实现了继承1.引用属性共享 子类可以篡改父类的属性 2. 子类无法向父类传递参数
    构造函数继承在子类的构造函数调用父类的构造函数 改变this指向子类 Super.call(this,…arguments)1. 应用属性不会共享 2. 子类可以向父类传递参数1. 所有属性只能封装在构造函数里面 每次创建实例 方法都会被创建一次
    组合式继承原型式继承+构造函数继承 ,利用构造函数继承父类的属性,利用原型继承父类的方法1. 应用属性不会共享 2. 子类可以向父类传递参数3.方法也只会创建一遍父类构造函数会被调用两次
    原型式继承Object.create 会以传入的对象作为原型创造一个新对象 本质是内部声明了一个构造函数 把这个构造函数的prototype属性指向这个函数 最后返回这个函数的实例同原型链继承
    寄生式继承就是把原型式继承用一个函数封装起来 其实同原型式继承同上
    寄生组合式组合式+寄生式 利用寄生式继承的特点 解决组合式继承父类构造函数会被调用两次的问题 function inhret(subType,superType){ var prototype = Object.create(superType.prototype);prototype.constructor=SuperType;subType.prototype=protptype}1. 应用属性不会共享 2. 子类可以向父类传递参数3.方法也只会创建一遍 4.父类构造函数也只会调用一次
  • 相关阅读:
    云服务多语言 SDK
    合成孔径SAR雷达成像成(RDA和CSA)(Matlab代码实现)
    TVM神经编译器
    centos7系统下postgresql15离线安装,卸载
    Morefine 500+ AMD R9 5900HX Mini主机 安装TrueNas 开启WOL唤醒
    Python gRPC 生成代码参考
    sklearn基础篇(三)-- 鸢尾花(iris)数据集分析和分类
    FPGA之旅设计99例之第三例-----UART串口通信
    [NSSRound#4] 复现
    【接口幂等性】使用token,Redis保证接口幂等性
  • 原文地址:https://blog.csdn.net/zw52yany/article/details/125937314