JavaScript 继承!我有话要说


关于JavaScript继承这一块,其实困扰了我很久,更多的是强行记忆,没有真正的理解,我看了很多书籍,博客关于JavaScript继承的描述,但是百思不得其解,一直到看了《你不知道的JavaScript》,貌似对继承有了一点感觉,现写下,供以后复习


说到继承,首先想到的就是最直截了当的方法实现:

   function Foo() {}
   function Bar() {}
   Bar.prototype = new Foo()

但是发现,这样写了之后,Bar原型对象里面的引用类型的属性,方法会因为实例的修改而修改,就像这样:

    function Foo() {
        this.arr = [1, 2, 3]
    }
    function Bar() {
    }
    Bar.prototype = new Foo();
    var a = new Bar();
    var b = new Bar();
    console.log(a.arr);//1,2,3
    console.log(b.arr);//1,2,3
    //修改arr
    a.arr.push(4);
    console.log(a.arr);//1,2,3,4
    console.log(b.arr);//1,2,3,4
    console.log(Bar.prototype.arr);//1,2,3,4

很显然,这不是我们想要的结局。


所以,就有了第二种方法,思路很简单,继承,无非就是子类把父类的属性,方法,拿过来用,所以就在子类内部复制一份(或者说把父类的属性,方法,原原本本的在子类里作用一遍)。用的是函数自身附带的call(),apply()方法,具体实现如下:

    function Foo() {
        this.arr = [1, 2, 3]
    }
    function Bar() {
        Foo.apply(this)
    }
    var a = new Bar();
    var b = new Bar();
    console.log(a.arr);//1,2,3
    console.log(b.arr);//1,2,3
    //修改arr
    a.arr.push(4);
    console.log(a.arr);//1,2,3,4
    console.log(b.arr);//1,2,3
    console.log(Bar.prototype.arr);//undefined

为什么Bar.prototype.arr会是undefined?因为,用这种方法根本就没有用到原型这一概念,Foo和Bar之间也不存在真的继承关系。但是这种方式也是有显而易见的缺点,Bar的每一次实例化(new),其实都是创建了一个新的对象,完完全全新的对象,对象与对象之间没有一丁点关系,复用性这一点完完全全没有想到,内存消耗太大。


后来,人们就想到,把需要复用的(共有的)属性,方法,用第一种方式继承,把不需要复用的,每个实例不一样的(私有的)属性,方法,用第二种方式继承,就有了以下的方法:

    function Foo() {
        this.arr = [1, 2, 3]
    }
    Foo.prototype.showArr = function () {
        console.log(this.arr)
    };
    function Bar() {
        Foo.apply(this)
    }
    Bar.prototype = new Foo();
    var a = new Bar();
    var b = new Bar();
    a.showArr();//1,2,3
    b.showArr();//1,2,3
    //修改arr
    a.arr.push(4);
    a.showArr();//1,2,3,4
    b.showArr();//1,2,3

不得不说,堪称完美!把私有的属性写在Foo函数里面(或者说构造函数内部),把需要共有的属性写在原型里面,公私分明,妈妈再也不用担心我的继承问题了!这样其实已经很完美了,但是要说挑刺,还是能挑出来的,在我们使用Foo.apply(this)的时候,将Foo的内部属性作用了一遍,然后在new Foo()的时候,又将Foo整个作用了一遍,而且Bar.prototype = new Foo()我们需要的并不是Foo内部的属性,而是要Foo的原型,也就是Foo.prototype,所以这样就多了一次作用Foo的内部属性。


这个世界,总有强迫症的人,对吧?他们觉得多作用了一次Foo的内部属性不爽,不够完美。就想到一种更好的方法!不让Bar.prototype = new Foo(),而是让Bar.prototype = 一个过渡的函数,这个函数的prototype等于 Foo的prototype,并且过渡函数的内部属性为空。因为内部属性为空,所以作用的时候几乎不消耗内存,具体实现代码如下:

    function Foo() {
        this.arr = [1, 2, 3]
    }
    Foo.prototype.showArr = function () {
        console.log(this.arr)
    };
    function Bar() {
        Foo.apply(this)
    }
    function F() {//内部属性为空
    }
    F.prototype = Foo.prototype;
    Bar.prototype = new F();
    var a = new Bar();
    var b = new Bar();
    a.showArr();//1,2,3
    b.showArr();//1,2,3
    //修改arr
    a.arr.push(4);
    a.showArr();//1,2,3,4
    b.showArr();//1,2,3

原理就是这样,当然,具体操作肯定不会这样写的,因为过渡函数的用意是过渡,也就是一次性,用完就扔(释放)的存在,自然不会这样子写,最好包裹在一个函数内部,或者说我们自己写一个函数实现这样子的过渡继承,具体如下:

 //过渡函数
    function inherit(prototype) {
        function F() {//内部属性为空
        }
        F.prototype = prototype;
        return new F()
    }
    
    function Foo() {
        this.arr = [1, 2, 3]
    }
    Foo.prototype.showArr = function () {
        console.log(this.arr)
    };
    function Bar() {
        Foo.apply(this)
    }
    Bar.prototype = inherit(Foo.prototype);
    var a = new Bar();
    var b = new Bar();
    a.showArr();//1,2,3
    b.showArr();//1,2,3
    //修改arr
    a.arr.push(4);
    a.showArr();//1,2,3,4
    b.showArr();//1,2,3

这就很舒服了,没有一点点浪费,实现了继承。


真的如此吗?##

我一直在纠结,这种继承的方式,我认为有很多地方都十分怪异,或者说奇怪,明明都是对象,为什么相互之间还要如此‘折腾’,我不能理解。在《你不知道的JavaScript》中,有提到一种新的模式,用来实现‘继承’这样的关系,其实这种模式不是一种继承概念,而是对象与对象之间的一种关系,可以通过这种方式来清楚的模拟,或者说取代这种怪异的继承。
需要用到的仅仅是Object.create()
这个是es5提出的一个API,它的作用和我们之前过渡函数完全一样(如果不需要深入理解它的第二个参数的话)如果需要兼容,可以用我们写过的过渡函数。
这种基于委托的模式,具体的实现如下:

    Foo = {
        init:function(){this.arr = [1,2,3]},
        showArr: function () {
            console.log(this.arr)
        }
    };
    Bar =Object.create(Foo);
    var a = Object.create(Bar);
    var b = Object.create(Bar);
    a.init();//初始化
    b.init();//初始化
    a.showArr();//1,2,3
    b.showArr();//1,2,3
    //修改arr
    a.arr.push(4);
    a.showArr();//1,2,3,4
    b.showArr();//1,2,3

没有蹩脚的new了!没有复杂的继承关系了!整个世界都清爽了!有木有!
用这种方式更能体现JavaScript对象与对象之间的关系,而不是用JavaScript来模拟各种类之间的继承,搞的不伦不类。

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

推荐阅读更多精彩内容