js 继承

作者: Super曲江龙Kimi | 来源:发表于2019-08-24 16:18 被阅读0次

创建实例对象的方法

1. 工厂模式

类似于工厂内部去加工,一个黑盒过程看不到。

function creatPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        alert(this.name)
    }
    return o;
}
var person1 = createPerson('Nich', 29, 'software engineer');

特点:
看不到内部的过程。只需要传入参数即可

2. 构造函数模式

通过js的构造函数来创建对象

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name)
    }
}
var person1 = new Person('nich', 29, 'engineer');
alert(person1.constructor == Person) // true
alert(person1 instanceof Object) // true
alert(person1 instanceof Person) // true

特点:
(1)每次new出一个实例都和其他的不是同一个实例
(2)多个实例中的属性和方法都是不同的实例,互相不影响,起不到共用的作用。

3. 原型模式

function Person() {}
Person.prototype.name = 'Nich';
Person.prototype.age = 29;
Person.prototype.sayName = function() {};

特点:
定义在prototype上的属性和方法可以共用,修改后所有实例的方法和属性都会改变。

4. 原型对象

Person.prototype一个指针指向一个原型对象。原型对象会获得一个constructor指向构造函数Person

Person.prototype = person._proto_
person.constructor = Person.prototype.constructor

(1)判断是否为实例的原型对象

alert(Person.prototype.isPrototypeOf(person1)) //true

(2)获取实例的原型对象

alert(Object.getPrototypeOf(person) == Person.prototype) // true

(3)检测一个属性是否存在于实例中

alert(person.hasOwnProperty('name')) // false

(4)获取所有实例属性,包括不可枚举属性

alert(Object.getOwnPropertyNames(Person.prototype)); // 'constructor, name'  

在实例中可以重写原型中的方法,调用方法会先从自身顺着原型链找直到找到。
如果重写了原型方法后,即便设置为null,也只会在实例中设置,不会恢复指向原型的连接,只有通过使用delete操作符可以完全删除实例属性,从而访问到原型的属性。

如果需要重写原型?

function Person() {}
   Person.prototype = {
    constructor : Person, 
        // 如果重写了原型,则constructor不会自动添加也不会默认指会Person,需要手动添加
    name : 'Nich',
    age : 29
}

重写原型的方式有两个缺点
(1)没有办法传参来实例化不同的实例
(2)如果原型属性有引用值的话,修改了会改变其他的。因为他是指针指向,原型修改后所有实例会同步,所以很适合函数和基本类型的属性。不适合引用类型,而new构造函数的方式,则会脱离引用关系。自己单独实例化。修改自己实例中的值不会影响其他实例中的值。

function Person();
Person.prototype = {
    arr:[1,2]
}
let person = new Person();
person.arr.push(3) // 其他都会改变

修复缺点最好的方式是使用构造函数和原型混合的模式

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["shelby", "court"];
}
Person.prototype = {
    constructor : Person,
    sayName : function() {
        alert(this.name);
    }
}
var person1 = new Person("Nich", 29, "engineer");
var person2 = new Person("greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); // shelby court van
alert(person2.friends); // shelby court
alert(person1.sayName === person2.sayName); // true

5. 继承

原型链:让实例的原型对象等于另一个类型的实例,这个实例的原型再指向另一个实例。层层递进就构成原型链。所有的对象又继承于Object

<1> 原型链继承方式

function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function() {
    return this.property;
}
function SubType() {
    this.subproperty = false;
}
SubType.prototype = new SuperType(); // 关键语句 SubType继承于SuperType
SubType.prototype.getSubValue = function() {
    return this.subproperty;
}
let instance = new SubType();
alert(instance.getSuperValue()); // true

创建SuperType的实例,并赋给SubType.prototype,重写原型对象,原来存在于SuperType的实例中的属性方法,现在也存在于SubType.prototype中了(因为是实例,会将构造函数中的属性直接拿来一份,而原型方法则还在原型中)instance.constructor现在指向的是SuperType,因为原型被重写了,constructor也被重写到了SuperType

原型链继承方式的问题
1:如果有引用类型-- 原型被一个实例替代,实例如果从构造函数中继承的属性则变成了原型属性,原型属性是所有实例共用的,一个实例修改,所有都会改变
2: 创建子实例时,没办法向超类中传参

<2> 组合继承

function SuperType(name) {
    this.name = name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function() {
    alert(this.name);
}
function SubType(name, age) {
    // 执行一下继承的构造函数,可以继承属性
        // 让自己的构造函数中也有一份属性,这样实例中的属性,修改就不会相互影响了。
    SuperType.call(this, name);
    // 在执行完构造函数后,再添加自己的属性。这样不会被覆盖。
    this.age = age;
}
SubType.prototype = new SuperType(); 
// 其中还是有超类的构造函数中的属性,不过已经被覆盖
SubType.prototype.sayAge = function() {
    alert(this.age);
}
// 可以自己传参给超构造函数
let instance1 = new SubType("Nich", 29);
instance1.colors.push("black");
alert(instance1.colors); // "red, blue, green, black"
instance1.sayName(); // "Nich"
instance1.sayAge();  // 29

let instance2 = new SubType("greg", 27);
alert(instance2.colors); // "red, blue, green"
instance2.sayName(); // "greg"
instance2.sayAge();  // 27

组合继承的缺点

都会调用两次超类型构造函数, 原型中仍然还有超类构造函数中的属性,不过已经被覆盖。但是会在新对象上创建多余的实例属性

<3> 寄生组合继承

function inheritPrototype(subType, superType) {
    var prototype = object(superType.prototype);  // 创建对象副本
    prototype.constructor = subType;     // 重写了构造函数,需要重新指向构造函数
    subType.prototype = prototype;  // 指定对象
}
function SuperType(name) {
    this.name = name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function() {
    alert(this.name);
}
function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
// 相当于只把超原型中的方法拷贝一份,不拷贝超构造函数中的属性。
inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function() {
    alert(this.age);
}

拷贝原型上的方法上述例子中使用inheritPrototype(),有更常用的两种方法。

  1. SubType.prototype = Object.create(SuperType.prototype);
    Object.create()内部实现:
function(o) {
    let F = new Function();
    F.prototype = o;
    return new F()
}

实际上是进行了浅拷贝。创建了一个新对象,让他的原型等于传入的对象。和他的构造函数就脱离了关系,只拷贝原型方法,继承其原型方法,构造函数指向Object

  1. Object.setPrototypeOf(subType.prototype, superType.prototype)
    Object.create内部需要new一个中介函数,再用垃圾回收机制回收。性能不高。Object.setPrototypeOf(subType.prototype, superType.prototype)可以和object.create一样的效果,性能更好

这样只调用一次SuperType的构造函数,避免在SubType.prototype上面创建多余的属性,原型链保持不变还可以正常使用instanceof和isPrototypeOf()

小结

创建对象方式:
1 工厂模式:使用简单的函数创建对象,添加属性方法,返回对象。
2 构造函数模式: 可以创建自定义引用类型,可以使用new,缺点:每个成员没办法复用,尤其函数不可以共享。new出实例后各自实例不会互相影响
3 原型模式:使用构造函数的prototype属性指定共享的方法和属性(一般是方法);

继承的方式:
1 原型链继承方式:可以达到继承父类的方法效果,但是没办法像父类传承。并且如果父类构造函数中有引用类型,就会被当成原型属性共用。修改都会改变
2 组合继承方式: 相比原型链继承可以向父类传参。但是构造函数需要执行两次,并且有多余的属性
3 寄生组合继承方式: 只是去做原型的复制。不会多次去执行父类的构造函数。推荐使用

相关文章

  • Js的继承

    js的继承 @(js)[继承, js, 前端] 组合继承是原性链继承和构造函数继承的合体,它汲取了二者各自的有点,...

  • JS继承

    JS中的继承 许多OO语言都支持两种继承方式:接口继承和实现继承; 因为JS中没有类和接口的概念 , 所以JS不支...

  • #js继承

    js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承)类式继承(构造函数间的继承) 类式继承是...

  • js继承遇到的小问题

    这两天在看js继承方面,它不像OC那种传统的类继承。js继承方式还是挺多的。比如:原型继承、原型冒充、复制继承 原...

  • JS中继承的实现

    JS中继承的实现 #prototype (js原型(prototype)实现继承) 全局的Function对象没有...

  • js继承

    js继承js 继承-简书 原型链实现集继承 上面的代码实现原型链继承最重要的son.prototype=new f...

  • JavaScript 10

    js继承的概念 1.通过原型链方式实现继承(对象间的继承) 2.类式继承(构造函数间的继承) 由于js不像Java...

  • JS继承的实现的几种方式

    前言 JS作为面向对象的弱类型语言,继承也是非常强大的特性之一,那么如何在JS实现继承呢? JS继承的实现方式 既...

  • 2019-03-25 继承

    js中通过原型来实现继承 组合继承:原型继承+借用构造函数继承

  • 继承方式(6种)1.7

    JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。 JS继承...

网友评论

      本文标题:js 继承

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