JS继承

继承的概念:子类可以使用父类共享的属性和方法,避免重复代码提高代码复用性。

原型链:子类可以共享父类的实例对象和实例对象的原型对象;父类继承object,直到找不到为null为止。

Javascript继承详解

1、原型链继承 2、构造函数式继承 3、组合式继承

4、原型式继承 5、寄生式继承 6、寄生组合式继承

1. 类式继承(原型链继承)

将父类对象的实例赋值给子类的原型,则子类的原型可以访问父类原型上的属性和方法,以及父类构造函数中复制的属性和方法。

注意:子类继承的是父类实例的属性方法和实例的原型对象,同时如果子类本身就有名称相同的属性和方法,优先自身的

优缺点简单归纳:

子类可以继承父类实例的属性以及实例原型对象的属性;但是如果属性或者方法是引用类型,那么多个实例在引用的时候,如果前一个修改了数据,会导致别的实例引用的是修改之后的。

js中的数据类型有以下几种:

Number   Boolean   undefined     Object   Function    String  Null

基本类型:Number Boolean  String  undefined null

引用类型:Object  Function

基本类型的数据是存放在栈内存中的,而引用类型的数据是存放在堆内存中的

例1:

//1.类式继承    //声明父类    function SuperClass() {

        this.superValue = true;

    }

    //为父类添加公有方法    SuperClass.prototype.getSuperValue = function () {

        return this.superValue;

    };

    //声明子类    function SubClass() {

        this.subValue = false;

    }

    //继承父类    SubClass.prototype = new  SuperClass();//将父类对象赋值给子类原型,子类原型可访问父类原型上的属性和方法--类式继承原理    //子类添加公有方法    SubClass.prototype.getSubValue = function() {

        return this.subValue;

    };

    //测试    var instance = new SubClass();

    console.log(instance.getSuperValue();//t    console.log(instance.getSubValue());//f    //    console.log(instance instanceof SuperClass);//t    console.log(instance instanceof SubClass);//t    console.log(SubClass instanceof SuperClass);//f    console.log(SubClass.prototype instanceof SuperClass);//t 案例解析function SuperClass(){

    this.superValue = true;

}  //父function SubClass(){}  //子SubClass.prototype = new SuperClass();  //subclass的原型对象指向Superclass的实例对象SubClass.prototype.getSubValue = function(){}  //再修改了sub的原型后,添加了一个新的原型方法

缺点:

1.子类通过其原型prototype对父类实例化,继承了父类。但当父类中的共有属性是引用类型时,会在子类中被所有的实例共用,如此在一个子类实例中更改从父类中继承过来的公有属性时,会影响到其他子类。

2.由于子类是通过原型prototype实例化父类实现继承的,所以在创建父类的时候,无法向父类传递参数,因而在实例化父类的时候无法对父类构造函数内的属性初始化。

例2:

    function SuperClass() {

        this.book = ['javascript','html','css'];

    }

    function SubClass() {}

    SubClass.prototype = new SuperClass();

    var ins1 = new SubClass();

    var ins2 = new SubClass();

    console.log(ins1.book);

    console.log(ins2.book);

    ins1.book.push("Node");

    //引用类型在子类中被所有实例共用    console.log(ins1.book);//[ 'javascript', 'html', 'css', 'Node' ]    console.log(ins2.book);//[ 'javascript', 'html', 'css', 'Node' ]

例3:

function SuperClass(){

this.name="老爹";

this.age=50;

this.jihe=[1,2,3];

}

SuperClass.prototype.sayHello=function(){

console.log("大家好,我是老爹");

}

function SubClass(){

this.name="儿子";

}

//原型链继承的写法:

SubClass.prototype=new  SuperClass();

var Sub1=new  SubClass();

var Sub2=new  SubClass();

Sub1.jihe.push("4");         

//如果子类本身就有名称相同的属性和方法,优先自身的

console.log(Sub1.name);

//子类继承父类,可以共享父类实例的属性方法以及实例的原型的属性和方法

console.log(Sub1.sayHello);

console.log(Sub1.age);

//当父类中的共有属性是引用类型时,会在子类中被所有的实例共用,如此在一个子类实例中更改从父类中继承过来的公有属性时,会影响到其他子类。

console.log(Sub1.jihe);      //[1, 2, 3, "4"]

console.log(Sub2.jihe);      //[1, 2, 3, "4"]

======================================================================================================================

2. 构造函数式继承

通过在子类的构造函数中执行一次父类的构造函数实现。

优缺点归纳:

不会像原型链继承那样出现引用类型属性互相占用的问题,但是无法共享实例的原型对象。

//2.构造函数式继承    //声明父类    function SuperClass(id) {

        this.book = ['javascript','html','css'];//引用类型共有属性        this.id = id;//值类型公有属性    }

    //父类声明原型方法    SuperClass.prototype.showBooks = function() {

        console.log(this.books);

    }

    //声明子类    function SubClass(id) {        //继承父类        SuperClass.call(this,id);        //使用调用call函数的内部结构而不使用它的原型    }    //测试    var ins1 = new SubClass(1);

    var ins2 = new SubClass(2);

    ins1.book.push("Node");

    console.log(ins1.id);//1    console.log(ins1.book);//['javascript', 'html', 'css', 'Node']    console.log(ins2.id);//2    console.log(ins2.book);//['javascript', 'html', 'css']    ins1.showBooks();//TypeError: ins1.showBooks is not a function

**SuperClass.call(this,id)**是构造函数式继承的中心。call方法可以改变函数的作用环境,在子类中调用这个方法就是将子类中的变量在父类中执行,父类中给this绑定属性,因而子类继承了父类的共有属性。

关键:

为什么继承方式1会出现bug?

SubClass.prototype = new SuperClass();

SuperClass的函数中声明了引用类型的数组Book,那么new SuperClass()作为SuperClass函数的实例,就具有了该属性

function SuperClass(){this.book=[1,2,3]}

SubClass.prototype =  new SuperClass();  //完成继承var a  = new SubClass();var b = new SubClass();Object { … }关于call()function A(){

    this.name="a";

    this.test = function(){alert(this.a)}

}function B(){

  A.call(this);//  this.name="a";

  this.test = function(){alert(this.a)}

}var b = new B();

b.test();

构造函数式继承的缺点:

这种类型的继承没有涉及原型prototype,只是复制构造函数实例里面的对象,所以父类的原型方法不会被子类继承。如想被子类继承就必须放在构造函数中,这样创造的每个实例都会单独拥有一份而不能共用,违背了代码复用原则。

例:

function SuperClass(){

this.name="老爹";

this.age=50;

this.jihe=[1,2,3];

}

SuperClass.prototype.sayHello=function(){

console.log("大家好,我是老爹");

}

function SubClass(){

this.name="儿子";

}

//构造函数式继承的方法:

function SubClass(){

SuperClass.call(this);

}

var Sub1=new SubClass();

//这种类型的继承没有涉及原型prototype,只是复制构造函数实例里面的对象,所以父类的原型方法不会被子类继承。如想被子类继承就必须放在构造函数中,这样创造的每个实例都会单独拥有一份而不能共用,违背了代码复用原则。

console.log(Sub1.age);  //50

console.log(Sub1.sayHello);  //underfined

====================================================================================================================================

3. 组合式继承

综合以上两种模式的优点,在子类原型上实例化父类,在子类构造函数中执行一遍父类的构造函数。这样融合了类式继承和构造式继承的优点,过滤了缺点。(解决了原型链继承的缺点。)

优点归纳:

//3.组合式继承    function SuperClass(name) {

        this.name = name;

        this.book = ['javascript','html','css'];

    }

    SuperClass.prototype.getName = function () {

        console.log(this.name);

    };

    function SubClass(name,time) {

        //构造函数式继承,继承父类name属性        SuperClass.call(this,name);

        this.time = time;

    }

    //类式继承,子类原型继承    SubClass.prototype = new SuperClass();

    //子类原型方法    SubClass.prototype.getTime = function () {

        console.log(this.time);

    };

    //测试    var ins1 = new SubClass('Node',2016);

    ins1.book.push("Node");

    console.log(ins1.book);

    ins1.getName();

    ins1.getTime();

    var ins2 = new SubClass('React',2015);

    console.log(ins2.book);

    ins2.getName();

    ins2.getTime();

组合继承的缺点:

父类的构造函数执行了两遍:一次在子类的构造函数中call方法执行一遍,一次在子类原型实例化父类的时候执行一遍。

例:

//组合继承=构造函数式继承+原型链继承

function SuperClass(){

this.name="老爹";

this.age=50;

this.jihe=[1,2,3];

}

SuperClass.prototype.sayHello="大家好,我是老爹";

//构造函数式继承

function SubClass(){

SuperClass.call(this);

}

//原型链继承

SubClass.prototype=new  SuperClass();

var Sub1=new  SubClass();

var Sub2=new  SubClass();

//解决了原型链继承的缺点:即使是引用类型,改变了Sub1,Sub2没有更改

Sub1.jihe.push("4");

console.log(Sub1.jihe);  //[1, 2, 3, "4"]

console.log(Sub2.jihe);   //[1, 2, 3]

//解决了构造函数式继承的缺点:可以共享原型

console.log(Sub1.sayHello);  //大家好,我是老爹

==================================================================

3种继承方式总结:

1、原型链继承:通过修改子构造的原型对象指向父构造实例

缺点: 引用类型属性可能会存在互相占用的问题

优缺点简单归纳:

子类可以继承父类实例的属性以及实例原型对象的属性;但是如果属性或者方法是引用类型,那么多个实例在引用的时候,如果前一个修改了数据,后导致别的实例引用的是修改之后的。

SubClass.prototype=new SuperClass();

2、构造函数式继承:复制对象来实现继承:父类构造函数.call(子类实例)

缺点: 原型无法共享,因为是复制对象而不是直接引用对象故而原型对象无法完成全局共享

优缺点归纳:

不会像原型链继承那样出现引用类型属性互相占用的问题,但是无法共享实例的原型对象。

function SubClass(){

    SuperClass.call(this);

}

3、组合式继承: 将1和2组合在一起,既复制父类的具体实例,也引用父类的具体实例做为原型,从而产生原型链来实现原型继承

function SubClass(){

    SuperClass.call(this);  //实现构造函数的内部复制}

SubClass.prototype=new SuperClass();  //指定原型链

======================================================================================================================

4.原型式继承

对类式继承(原型链继承)的封装,过渡对象相当于子类。

问题也是一样的:

1、引用数据冲突

2、子类固定了

  //4.原型式继承

//首字母小写,不是构造函数

//工厂模式:生产对象的一种函数

    function inheritObject(o) {  //传的是父实例

        //声明过渡函数对象        function F() {} //匿名子类

        //过渡对象的原型继承父类        F.prototype = o;

        return new F();

    }

    //测试    var book = {

        name : "javascript",

        book : ['js','css']

    };

    var newbook = inheritObject(book);

    newbook.name = "ajax";

    newbook.book.push("Node");

    var otherbook = inheritObject(book);

    otherbook.name = "xml";

    otherbook.book.push("React");

    console.log(newbook.name);//ajax    console.log(newbook.book);//[ 'js', 'css', 'Node', 'React' ]    console.log(otherbook.name);//xml    console.log(otherbook.book);//[ 'js', 'css', 'Node', 'React' ]    console.log(book.name);//javascript    console.log(book.book);//[ 'js', 'css', 'Node', 'React' ]

5.寄生式继承

寄生式继承其实是对原型继承的第二次封装,并且在第二次封装的过程中对继承的对象进行了拓展。解决了子类自己对象拓展

//5.寄生式继承    function inheritObject(o) {

        //声明过渡函数对象        function F() {}

        //过渡对象的原型继承父类        F.prototype = o;

        return new F();

    }

    //声明基对象    var book = {

        name : "javascript",

        book : ['js','css']

    };

    function createBook(obj) {

        //通过原型继承方式创建新对象        var o = new inheritObject(obj);

        //拓展新对象        o.getName = function() {

            console.log(name);

        }

        //返回拓展后的新对象        return o;

    }

    var newbook = createBook(book);

    newbook.name = "ajax";

    newbook.book.push("Node");

    var otherbook = createBook(book);

    otherbook.name = "xml";

    otherbook.book.push("React");

    console.log(newbook.name);//ajax    console.log(newbook.book);//[ 'js', 'css', 'Node', 'React' ]    console.log(otherbook.name);//xml    console.log(otherbook.book);//[ 'js', 'css', 'Node', 'React' ]    console.log(book.name);//javascript    console.log(book.book);//[ 'js', 'css', 'Node', 'React' ]

6. 寄生式组合继承

//寄生组合式继承    function inheritObject(o) {

        //声明过渡函数对象        function F() {}

        //过渡对象的原型继承父类        F.prototype = o;

        return new F();

    }

    //寄生式继承 继承原型    function inheritPrototype(subClass,superClass) {

        //复制一份父类的原型副本保存在变量中        var p = inheritObject(superClass.prototype);

        //修正因为重写子类原型导致子类的constructor属性被修改        p.constructor = subClass;

        //设置子类的原型        subClass.prototype = p;

    }

    function SuperClass(name) {

        this.name = name;

        this.colors = ["red","blue","green"];

    }

    //定义父类原型方法    SuperClass.prototype.getName = function() {

        console.log(this.name);

    }

    function SubClass(name,time) {

        SuperClass.call(this,name);

        this.time = time;

    }

    //寄生式继承父类原型    inheritPrototype(SubClass,SuperClass);

    //子类新增原型方法    SubClass.prototype.getTime = function() {

        console.log(this.time);

    }

    //测试    var ins1 = new SubClass("js",2014);

    var ins2 = new SubClass("css",2015);

    ins1.colors.push("black");

    console.log(ins1.colors);

    console.log(ins2.colors);

    ins2.getName();

    ins2.getTime();

写组件,参考jquery  EasyUI

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容