原型

29 阅读6分钟

js原型继承的几种方式

  1. 原型链继承

2,构造函数继承(对象冒充继承)

3,组合继承(原型链继承+构造函数继承)

4,原型式继承

5, 寄生组合式继承

一。原型链继承


function Show(){
    this.name="run";
}

function Run(){
    this.age="20";
}
// Run继承了Show,通过原型,形成链条
Run.prototype=new Show();
var show=new Run();
alert(show.name)//结果:run

二。构造函数继承(对象冒充继承)

为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或 者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题

2.优缺点 可以实现多继承,不能继承原型属性/方法

function Box(age){
    this.name=['Lee','Jack','Hello']
    this.age=age;
}
function Desk(age){
    Box.call(this,age); //对象冒充,给超类型传参
}
var desk = new Desk(200);
alert(desk.age);//200
alert(desk.name);//['Lee','Jack','Hello']
desk.name.push('AAA'); //添加的新数据,只给 desk
alert(desk.name)//['Lee','Jack','Hello','AAA']

三。组合继承(原型链继承+构造函数继承)

借用构造函数虽然解决了刚才两种问题, 但没有原型, 复用则无从谈起。 所以, 我们需 要原型链+借用构造函数的模式,这种模式成为组合继承。

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Box(age) {
    this.name = ['Lee', 'Jack', 'Hello']
    this.age = age;
}
Box.prototype.run = function () {
    return this.name + this.age;
};
function Desk(age) {
    Box.call(this, age); //对象冒充
}
Desk.prototype = new Box(); //原型链继承
Desk.prototype.constructor = Desk; // 组合继承也是需要修复构造函数指向的。
var desk = new Desk(100);
alert(desk.run());

四。原型式继承

这种继承借助原型并基于已有的对象创建新对象, 同时还不必因此创建自定义类型

function obj(o) { //传递一个字面量函数
function F() {} //创建一个构造函数
F.prototype = o; //把字面量函数赋值给构造函数的原型
    return new F(); //最终返回出实例化的构造函数
}
var box = { //字面量对象
    name : 'Lee',
    arr : ['哥哥','妹妹','姐姐']
};
var box1 = obj(box); //传递
alert(box1.name);
box1.name = 'Jack';
alert(box1.name);
alert(box1.arr);
box1.arr.push('父母');
alert(box1.arr);
var box2 = obj(box); //传递
alert(box2.name);
alert(box2.arr); //引用类型共享了

五。寄生组合式继承

寄生组合式继承解决了两次调用的问题,组合式继承就会有两次调用的情况

基本模型如下:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function inheritPrototype(subType, superType) {
    var prototype = object(superType.prototype);  //创建对象
    prototype.constructor = subType;              //增强对象
    subType.prototype = prototype;                //指定对象
}

后面的寄生式继承和寄生组合模型式继承还搞不懂,哈哈哈,好尴尬

寄生组合继承,解决了两次调用的问题


Person.prototype.constructor == Person;
person1.__proto__ == Person.prototype;
person1.constructor == Person;

person1.__proto__ === Person.prototype
Person.__proto__ === Function.prototype
Person.prototype.__proto__ === Object.prototype
Object.__proto__
Object.prototype.__proto__ === null

var b = new Array();
b.constructor === Array;
b.__proto__ === Array.prototype;

var c = new Date();
c.constructor === Date;
c.__proto__ === Date.prototype;

var d = new Function();
d.constructor === Function;
d.__proto__ === Function.prototype;


// 所有函数对象的proto都指向Function.prototype,它是一个空函数(Empty function)
Number.__proto__ === Function.prototype  // true
Number.constructor == Function //true

Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true

String.__proto__ === Function.prototype  // true
String.constructor == Function //true

// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Object.__proto__ === Function.prototype  // true
Object.constructor == Function // true

// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true

Array.__proto__ === Function.prototype   // true
Array.constructor == Function //true

RegExp.__proto__ === Function.prototype  // true
RegExp.constructor == Function //true

Error.__proto__ === Function.prototype   // true
Error.constructor == Function //true

Date.__proto__ === Function.prototype    // true
Date.constructor == Function //true

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object

function Person(){}
var person1 = new Person();
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__) //null

Person.__proto__ == Function.prototype; //true
console.log(Function.prototype)// function(){} (空函数)

var num = new Array()
console.log(num.__proto__ == Array.prototype) // true
console.log( Array.prototype.__proto__ == Object.prototype) // true
console.log(Array.prototype) // [] (空数组)
console.log(Object.prototype.__proto__) //null

console.log(Array.__proto__ == Function.prototype)// true

var animal = function(){};
 var dog = function(){};

 animal.price = 2000;
 dog.prototype = animal;
 var tidy = new dog();
 console.log(dog.price) //undefined
 console.log(tidy.price) // 2000


原型和原型链是JS实现继承的一种模型。 原型链的形成是真正是靠__proto__ 而非prototype

JavaScript继承实现


function Animal() {
}
Animal.prototype.speak = function () {
    console.log('动物发声:');
}

function Dog(name) {
    this.name = name;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function () {
    //通过原型链找‘基本类’原型里的同名方法
    this.__proto__.__proto__.speak.call(this);
    console.log('汪汪, 我是', this.name);
}

function Cat(name) {
    this.name = name;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.speak = function () {
    //通过原型链找‘基本类’原型里的同名方法
    this.__proto__.__proto__.speak.call(this);
    console.log('喵喵, 我是', this.name);
}

//调用代码
function animalSpeak(animal) {
    animal.speak();
}

animalSpeak(new Dog('大黄'))
console.log()
animalSpeak(new Cat('小喵'))

//动物发声:
//汪汪, 我是 大黄

//动物发声:
//喵喵, 我是 小喵

Q: javascript里面的继承怎么实现,如何避免原型链上面的对象共享

我在写的时候,用了两种,一个是 ES5和 ES6的方案

ES5:寄生组合式继承:通过借用构造函数来继承属性和原型链来实现子继承父。


function ParentClass(name) {
    this.name = name;
}
ParentClass.prototype.sayHello = function () {
  console.log("I'm parent!" + this.name);
}
function SubClass(name, age) {
    //若是要多个参数可以用apply 结合 ...解构
    ParentClass.call(this, name);
    this.age = age;
}
SubClass.prototype = Object.create(ParentClass.prototype);
SubClass.prototype.constructor = SubClass;
SubClass.prototype.sayChildHello = function (name) {
    console.log("I'm child " + this.name)
}

let testA = new SubClass('CRPER')

// Object.create()的polyfill
    /*
    function pureObject(o){
        //定义了一个临时构造函数
         function F() {}
         //将这个临时构造函数的原型指向了传入进来的对象。
         F.prototype = obj;
         //返回这个构造函数的一个实例。该实例拥有obj的所有属性和方法。
         //因为该实例的原型是obj对象。
         return new F();
    }
    */
// ES6: 其实就是ES5的语法糖,不过可读性很强..
class ParentClass {
    constructor(name) {
    this.name = name;
    }
    sayHello() {
    console.log("I'm parent!" + this.name);
    }
}

class SubClass extends ParentClass {
    constructor(name) {
        super(name);
    }
    sayChildHello() {
        console.log("I'm child " + this.name)
    }
    // 重新声明父类同名方法会覆写,ES5的话就是直接操作自己的原型链上
    sayHello(){
        console.log("override parent method !,I'm sayHello Method")
    }
}

let testA = new SubClass('CRPER')

到这里就结束了么...不,这只是笔试,

问的时候你用过静态方法,静态属性,私有变量么?

这个静态方法是ES6之后才有这么个玩意,有这么些特点

方法不能给 this引用,可以给类直接引用 静态不可以给实例调用,比如 let a = new ParentClass => a.sayHello() 会抛出异常 父类静态方法,子类非 static 方法没法覆盖父类 静态方法可以给子类继承 静态属性可以继承也可以被修改 看下面的代码..

class ParentClass {
    constructor(name) {
        this.name = name;
    }
    static sayHello() {
        console.log("I'm parent!" + this.name);
    }

    static testFunc(){
        console.log('emm...Parent test static Func')
    }
}

class SubClass extends ParentClass {
    constructor(name) {
        super(name);
    }
    sayChildHello() {
        console.log("I'm child " + this.name)
    }
    static sayHello() {
        console.log("override parent method !,I'm sayHello Method")
    }

    static testFunc2() {
        console.log(super.testFunc() + 'fsdafasdf');
    }
}
ParentClass.sayHello(); // success print

let a = new ParentClass('test');
a.sayHello() // throw error

SubClass.sayHello(); // 同名 static 可以继承且覆盖

SubClass.testFunc2(); // 可以继承

let testA = new SubClass('CRPER');

私有变量这个我没答出来,只是说了下没有 private 这个关键字和基本用下划线的人为区分

所以回来只是找了下相关的资料,发现有一个比较好的模拟方案,就是 WeakMap ;

WeakMap 可以避免内存泄露,当没有被值引用的时候会自动给内存寄存器回收了.

const _ = new WeakMap(); // 实例化,value 必须为对象,有 delete,get,has,set四个方法,看名字都知道了

class TestWeakMap {
    constructor(id, barcode) {
        __.set(this, { id,barcode });
    }
    testFunc() {
        let { id,barcode } = __.get(this); // 获取对应的值
        return { id,barcode };
    }
}