javascript面向对象(笔记)系列: 构造函数继承

继承

原型链

构造函数被new 操作符实例化一个对象之后,对象中会有一个秘密链接。(非IE内核浏览器这个链接叫__proto__),通过这个链接我们可以从实例对象中查找属性和方法。而原型对象中本身也包含了指向其原型的链接,由此形成的一个链条,既原型链。

456.png

比如像下面这样查询一个属性的过程,
function A() { this.hairColor = "blue"; } function B() { this.height = "fat"; } function C() { this.name = "Child"; } B.prototype = new A(); C.prototype = new B(); var d = new C(); d.hairColor //blue
首先查询d这个实例对象中的所有属性,没有找到,继而去查找d.__proto__指向的对象,即new出来的B实例对象。重复这个过程在A中找到了,立即打印在浏览器中。

继承模式

实现继承的模式,可以分为俩类

  • 基于构造函数的继承
  • 基于对象的继承

一、 构造函数继承

1. 原型链继承

ECMAScript 默认的继承方法。
function Cat() { this.color = "blue"; } Cat.prototype.getColor = function() { console.log (this.color); };
function Dog() { this.height = "150cm"; } //继承 Dog.prototype = new Cat(); Dog.prototype.consctructor = Dog; Dog.prototype.getHeight = function() { console.log (this.height); };

var animal = new Dog();
animal.getColor();
animal.getHeight();

给原型添加方法的代码一定要放在替换原先的语句之后。
缺点:

  • 当原型对象中有引用类型的值时,属性会被所有实例所共享。修改其中一个实例对象,会在所有实例中反应出来。
  • 给父类型构造函数传递参数会影响所有实例。
    不推荐单独使用原型链继承模式。
2. 借用构造函数(伪造对象或经典继承)

利用函数的apply()或者call方法,在子类型构造函数的内部调用超类型构造函数。
function Cat() { this.color = ["red", "blue", "green"]; } function Dog() { Cat.call(this); }

    var animal1 = new Dog();
    animal1.color.push("yellow");
    console.log(animal1.color);    // ["red", "blue", "green", "yellow"]
  

    var animal2 = new Dog();
    console.log(animal2.color);  // ["red", "blue", "green"]

这种方式通过call()apply()在实例化对象的时候,调用父类构造函数Cat()来初始化实例的属性和方法,这样每一个实例都能拥有自己的引用类型的副本了。而不在是实例共享一个引用对象。

function Cat(name) { this.name = name; }

function Dog() { Cat.call(this, "张三"); this.age = "29"; }

    var animal3 = new Dog();
    animal3.name;    //"张三"
    animal3.age      //"29"

优点: 可以再子类型构造函数中向父类构造函数传递参数。
缺点: 方法都在构造函数中定义,函数无法复用。
不推荐单独使用原型链继承模式。

3. 组合继承(伪经典继承)

使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。

定义父类构造函数 function Man(name) { this.name = name; this.friends = ["mark", "david", "july"]; }

在父类原型对象中定义方法 Man.prototype.sayName = function() { console.log(this.name); };

function Woman(name, age) { Man.call(this, name); //继承属性 this.age = age; //定义自有属性 }

Woman.prototype = new Man(); //继承再原型中定义的方法 Woman.prototype.constructor = Woman; //让consctructor指向正确 Woman.prototype.sayAge = function() { //定义自己的原型方法 console.log(this.age); };

这样不同的实例拥有自己的属性, 又可以使用共享的方法
var person = new Woman("marry", 24); person.friends.push("Joe"); console.log(person.friends); // ["mark", "david", "july", "Joe"] person.sayName(); // marry person.sayAge(); // 24

var person1 = new Woman("Nip", 30); console.log(person1.friends); // ["mark", "david", "july"] person1.sayName(); // Nip person1.sayAge(); // 30

4. 只继承于原型

由于原型中的所有代码都是共享的,这意味这Woman.prototype = Man.prototype继承于原型,比 Woman.prototype = new Man();继承于构造函数的实例,更加效率,因为中间省去了到new Man()实例对象查找这一步。.也不用实例化父类对象了。

function Man() {} Man.prototype = { constructor: Man, friends: ["mark", "david", "july"], };

function Woman(name, age) { Man.call(this, name); this.age = age; }
Woman.prototype = Man.prototype;
Woman.prototype.getAge= function() { console.log(this.age); };

缺点: 由于引用的是一个同一个原型,对子对象的原型经行修改,父对象也会跟着改变,所有有用继承关系的都是如此。
var person1 = new Woman(“bob", 25); console.log(person.friends) // ["mark", "david", "july"]

var person2 = new Woman(“marry", 28); console.log(person.friends) // ["mark", "david", "july"]

并且对象的conctructor属性的值也是一样的。
Man.prototype.constructor === WoMan.prototype.constructor //true

5. 临时构造器 newF()

这种模式是对第三种模式的一种改进,既然所有属性都指向了一个相同的对象,父对象就会受到子对象属性的影响。要避免这种缺点,可以用一个临时的构造器来替代。
var F = function() {};

F.prototype = Man.prototype;

Woman.prototype = new F();

Woman.prototype.constructor = Woman;
通过这种方法,我们可以再保持原型链的基础上使父对象的属性摆脱对子对象的影响了;

我们可以把之前的这个临时构造器封装起来,方便使用:
function extend(Child, Parent) { var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; }
可以像下面这样使用:
extend(Woman, Man);

var person = new Woman("mark", 25);

console.log(person.friends) // ["mark", "david", "july"]

这也是Yui库的实现方法

Child.uber = Parent.prototype;

这句话的意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。可以直接调用父对象的方法。

6. 属性拷贝

在构建可重用的继承代码之前,我们可以见简单的将父对象的属性拷贝给子对象。根据之前的extend函数可以创建一个函数,该函数接受2个构造器函数。将父类的原型属性全部拷贝 给子类原型。
function extend2(Child, Parent) {
var p = Parent.prototype;

var c = Child.prototype;

for(var i in p) { c[i] = p[i]; }
c.uber = p;
}
这个方法与之前的相比,效率较低,这里执行的是子对象原型的逐一拷贝,而非简单的查询。这种方式仅适用于简单类型值的对象。引用类型的值不可复制,只支持复制指针。指向的还是原来的对象。

确定原型和实例关系的方法
  1. instanceof(),用来确定实例是不是某个原型对象的实例。可以像这样使用
    console.log (animal instanceof(Dog)); //true console.log (animal instanceof(Cat)); //true

  2. isPrototypeOf() 在原型链中出现过的原型,都会返回true

``
console.log (Dog.prototype.isPrototypeOf(animal)); //true
console.log (Cat.prototype.isPrototypeOf(animal)); //true
console.log (Object.prototype.isPrototypeOf(animal)); //true

``

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

推荐阅读更多精彩内容