Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS继承的几种实现方式 #5

Open
Aras-ax opened this issue Jan 18, 2019 · 0 comments
Open

JS继承的几种实现方式 #5

Aras-ax opened this issue Jan 18, 2019 · 0 comments

Comments

@Aras-ax
Copy link
Owner

Aras-ax commented Jan 18, 2019

JS继承的几种实现方式


ECMAScript只支持实现继承,实现继承主要的依靠原型链来实现的。下面介绍几种JS实现继承的方式,以及各自的优缺点。

原型链

实现原理:利用原型让一个引用类型继承另一个引用类型的属性和方法.本质为重写原型对象。

function Sup(){
    this.name = "sup";
    this.items = [1, 2, 3];
}

Sup.prototype.geName = function(){
    console.log(this.name);
}

function Sub(){
    this.age = 25;
} 

Sub.prototype = new Sup();

Sub.prototype.getAge =function(){
    console.log(this.age);
}

var instance = new Sub();
instance.getName();

存在问题
1.继承包含引用类型值的原型,引用类型的原型属性会被所以实例共享。耦合度较高。
2.创建子类型的实例时不能向父类的构造函数传递 参数。

验证demo

var a = new Sub();
console.log(a.items);   //[1, 2, 3]
a.items.push(4);
var b = new Sub();
console.log(b.items);   //[1, 2, 3, 4]

如何解决上述问题?


借用构造函数

function Sup(name){
    this.name = name;
    this.items = [1, 2, 3];
}

Sup.getItems = function(){
    console.log(this.items);
}

function Sub(name){
    Sup.call(this, name);
}

var a = new Sub();
a.items.push(4);
console.log(a.items);   //[1, 2, 3, 4]

var b = new Sub();
console.log(b.items);    //[1, 2, 3]

可以解决上诉的问题,但同时也存在缺陷。

父类原型链上定义的方法对子类是不可见的,若要继承方法,则父类的方法都应定义在构造函数内部,方法都是在构造函数中定义,不能复用

如何解决?


组合继承

function Sup(name){
    this.name = name;
    this.items = [1, 2, 3];
}

Sup.prototype.getItems = function(){
    console.log("items: ", this.items);
}

Sup.prototype.getName = function(){
    console.log("name: ", this.name);
}

function Sub(name, age){
    Sup.call(this, name);
    this.age = age;
}

Sub.prototype = new Sup();
Sub.prototype.constructor = Sub;
Sub.prototype.getAge = function(){
    console.log("age: ", this.age);
}


var a = new Sub("lilei", 25);
a.items.push(4);
a.getName();    //lilei
a.getAge();     //25
a.getItems();   //[1, 2, 3, 4]

var b = new Sub("hanmeimei", 24);
b.getName();    //hanmeimei
b.getAge();     //24
b.getItems();   //[1, 2, 3]

那么问题来了,又存在哪些缺陷呢?

构造函数和原型链上定义了重复的属性

为解决上述问题,先来看一下接下来介绍的一种模式


寄生式继承

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

function createObj(obj){
    var clone = object(obj);
    clone.getName = function(){
        console.log(this.name);
    }

    clone.getFriends = function(){
        console.log(this.friends);
    }
    return clone;
}

var obj = {
    name : 'lily',
    friends: ['lucy', 'lilei', 'hanmeimei']
}

var obj1 = createObj(obj),
    obj2 = createObj(obj);

    obj1.name = 'jim';
    obj1.friends.push('lady gaga');

//     obj2.friends = ['katy perry', 'rianna', 'adele'];

    obj1.getName();     //jim
    obj1.getFriends();  //['lucy', 'lilei', 'hanmeimei', 'lady gaga']
    obj2.getName();     //lily
    obj2.getFriends();  //['lucy', 'lilei', 'hanmeimei', 'lady gaga']

存在问题:含有应用类型的值始终会共享
具体原因上节课领导已经分析过,好好回忆一下下,是不是忘了(´・・)ノ(._.`)

在主要考虑对象而不是自定义类型和构造函数的情况下是有用的


寄生组合式继承

function inherit(Sup, Sub){
    function F(){}
    F.prototype = Sup.prototype;
    var obj = new F();
    obj.constructor = Sub;
    Sub.prototype = obj;
}
function Sup(name){
    this.name = name;
    this.items = [1, 2, 3];
}

Sup.prototype.getItems = function(){
    console.log("items: ", this.items);
}

Sup.prototype.getName = function(){
    console.log("name: ", this.name);
}

function Sub(name, age){
    Sup.call(this, name);
    this.age = age;
}

inherit(Sup, Sub);
Sub.prototype.getAge = function(){
    console.log("age: ", this.age);
}

Sub.prototype.getName = function(){
    console.log("nameForSub: ", this.name);
}

function Sub1(name, age){
    Sup.call(this, name);
    this.age = age;
}

inherit(Sup, Sub1);
Sub1.prototype.getAge = function(){
    console.log("age: ", this.age);
}

var a = new Sub("lilei", 25);
a.items.push(4);
a.getName();    //nameForSub:  lilei
a.getAge();     //age:  25
a.getItems();   //items:  [1, 2, 3, 4]

var b = new Sub("hanmeimei", 24);
b.getName();    //nameForSub:  hanmeimei
b.getAge();     //age:  24
b.getItems();   //items:  [1, 2, 3]

var c = new Sub1("jim", 23);
c.getName();    //name:  jim
c.getAge();     //age:  23
c.getItems();   //items:  [1, 2, 3]

思考:

inherit函数能不能换成如下写法,有什么区别?

function inherit(Sup, Sub){
    Sub.prototype = Sup.prototype;
}

对比

继承方式 描述 好处 缺陷
原型链 重写子类原型链为基类的实例对象 易理解 1.无法给基类构造函数传递参数 2. 所有实例共享引用类型的属性
借用构造函数 子类构造函数中调用基类构造函数,作用域指向当前对象 解决原型链存在的问题 基类原型链上定义的方法属性对子类是不可见的
组合继承 原型链和借用构造函数组合 以上缺陷可以解决 存在重复定义的属性和方法
寄生式继承 构建临时构造函数,重写原型,返回新的对象 没想到,应用场景不多(~ o ~)~zZ 不适用于自定义类型,引用类型属性会共享
寄生式继承 是否必填 解决综上问题 不易理解,待消化

以上内容仅个人理解,如有错误还请谅解 (〃'▽'〃)


//代码附录
//1.
function inherit(Sup, Sub){
    function F(){}
    F.prototype = Sup.prototype;
    var obj = new F();
    obj.constructor = Sub;
    Sub.prototype = obj;
}

function Sup(name){
    this.name = name;
    this.items = [1, 2, 3];
}

Sup.prototype.getItems = function(){
    console.log("items: ", this.items);
}

Sup.prototype.getName = function(){
    console.log("name: ", this.name);
}

function Sub(name, age){
    Sup.call(this, name);
    this.age = age;
}

inherit(Sup, Sub);
Sub.prototype.getAge = function(){
    console.log("age: ", this.age);
}

Sub.prototype.getName = function(){
    console.log("nameForSub: ", this.name);
}

function Sub1(name, age){
    Sup.call(this, name);
    this.age = age;
}

inherit(Sup, Sub1);
Sub1.prototype.getAge = function(){
    console.log("age: ", this.age);
}

var a = new Sub("lilei", 25);
a.items.push(4);
a.getName();
a.getAge();
a.getItems();

var b = new Sub("hanmeimei", 24);
b.getName();
b.getAge();
b.getItems();

var c = new Sub1("jim", 23);
c.getName();
c.getAge();
c.getItems();

//2.
function inherit(Sup, Sub){
    Sub.prototype = Sup.prototype;
}

function Sup(name){
    this.name = name;
    this.items = [1, 2, 3];
}

Sup.prototype.getItems = function(){
    console.log("items: ", this.items);
}

Sup.prototype.getName = function(){
    console.log("name: ", this.name);
}

function Sub(name, age){
    Sup.call(this, name);
    this.age = age;
}

inherit(Sup, Sub);
Sub.prototype.getAge = function(){
    console.log("age: ", this.age);
}

Sub.prototype.getName = function(){
    console.log("nameForSub: ", this.name);
}

function Sub1(name, age){
    Sup.call(this, name);
    this.age = age;
}

inherit(Sup, Sub1);
Sub1.prototype.getAge = function(){
    console.log("age: ", this.age);
}

var a = new Sub("lilei", 25);
a.items.push(4);
a.getName();
a.getAge();
a.getItems();

var b = new Sub("hanmeimei", 24);
b.getName();
b.getAge();
b.getItems();

var c = new Sub1("jim", 23);
c.getName();
c.getAge();
c.getItems();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant