这个章节我们要来调整一下原型链上的结构
在调整之前,我们先来看一下状况!
var a = [];// Object > Array > a(实体)
如果今天状况是一个阵列十字的宣告的话,那么原型链的关係就是阵列实体,再往上是阵列原型,再往上是物件原型。
这个透过前面几篇文章的介绍应该没甚么问题。也因此这个阵列的实体可以使用阵列原型以及物件原型原型链上的所有方法。
function Dog (name, color, size) { this.name = name; this.color = color; this.size = size;}var Bibi = new Dog('比比', '棕色', '小');console.log(Bibi);// Object > Dog > Bibi(实体)
那么前几个章节我们也透过 Dog
的建构函式建立了比比这只狗(实体),他是继承于狗,在上一层是物件的原型,同时比比可以调用 建构函式 Dog 以及 物件原型的所有方法。
好那么我们现在的状况回到造物主的身分,所有的狗其实都是属于动物的一环没错吧!
所以我们现在需要再物件原型以及建构函式 Dog 中间新增一层叫做 Animal 的关係的话,我们应该怎么做呢?
Object > Animal > Dog > Bibi(实体)
如果变成这样的话,我们就可以创造出其他的物种,例如 猫
Object > Animal > Cat
那我们就来试着做看看要怎么新增一个层级吧!
首先要先来介绍一个方法,叫做 Object.create()
这个方法的主要功用呢,就是把其他的物件作为原型使用喔!
直接来看可能比较有感觉,先从简单的例子的开始吧~
var Bibi = { name: '比比', color: '棕色', size: '小', bark: function () { console.log(this.name + '吠叫'); }};// Object.create()var Pupu = Object.create(Bibi);console.log(Pupu);
我们让 Pupu 继承 Bibi为原型,虽然 Pupu 现在是空物件,但是可以透过 Pupu.name
的方法来取得并且确认的确是继承了 Bibi 为原型
当然我们也可以直接赋予属性给 Pupu
var Pupu = Object.create(Bibi);Pupu.name = '噗噗';console.log(Pupu);
这就是 Object.create()
的用法。
而且在不改变属性的情况下,所有的值都可以以 Bibi 为预设值,并且调用 Bibi 的方法。
创造多层的原型链结构
function Animal (family) { this.kingdom = '动物界'; this.family = family || '人科';}
首先我们先创立动物的建构函式,并且允许可以传入这个动物的科别,如果都没有传入的话就预设是人科。
并且我们也可以在 Animal 的原型上加上动物的方法,也就是移动的方式。
Animal.prototype.move = function () { console.log(this.name + ' 移动');};
接下来呢我们把狗加回来,并且重点来了,我们要利用 Object.create()
的方式把狗的原型重新赋值给动物的原型
function Dog (name, color, size) { this.name = name; this.color = color || '白色'; this.size = size || '小';}Dog.prototype = Object.create(Animal.prototype);
透过这样的方式,就可以产生我们一开始所说的阶级,接下来再把狗会叫的行为给加回来
Dog.prototype.bark = function () { console.log(this.name + '吠叫');}
那因此呢,我们就可以再用建构式的方式,把 Bibi 这只狗给产生出来。
function Animal (family) { this.kingdom = '动物界'; this.family = family || '人科';}Animal.prototype.move = function () { console.log(this.name + ' 移动');};function Dog (name, color, size) { this.name = name; this.color = color || '白色'; this.size = size || '小';}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () { console.log(this.name + '吠叫');}var Bibi = new Dog('比比', '棕色', '小');console.log(Bibi);
可以看到 比比 的确继承了狗,并且上一层是 Animal 没错,那么接下来我们再来看看他是不是能够正常的吠叫以及移动。
Bibi.bark();Bibi.move();
好~看起来没甚么问题吗~都跟我们预期的结果差不多
可是其实还缺少了一些东西喔!!!!!!!
主要就是其实 Bibi 并没有继承到 Animal 定义的科别以及动物界的属性喔!!
也因此直接输入 Bibi.family 的话会是 undefined 喔~
为什么会这样呢,主要是因为其实 Dog 只有继承 Animal 的原型,并没有继承整个建构函式,所以我们得在狗的建构函式内补一些内容
function Dog (name, color, size) { // 新增了这里 Animal.call(this, '犬科'); // 新增了这里 this.name = name; this.color = color || '白色'; this.size = size || '小';}
我们透过 call 的方式,将 Dog 里面的 this 指定给 Animal 里面的 this,并且直接执行,就等于是帮 Dog 新增了 Animal 里面的属性喔!
并且也特别传入犬科的参数,让 this.family 的值变为犬科。
好~到这里其实程式码的运作已经没甚么问题,但可以加上最后一行让整个程式马的逻辑更为完善
Dog.prototype = Object.create(Animal.prototype);// 逻辑更为完善Dog.prototype.constructor = Dog;// 逻辑更为完善
因为呢我们利用 Object.create
的方式继承了 Animal,那么这个 constructor 也会被取代掉,所以透过这样的方式补回来。
那么这个 constructor 又是甚么呢?
var newAnimal = new Animal('新物种');console.log(newAnimal);
当我们使用建构式的方式来产生一个新的物种的时候,其中 proto 的属性 就会包含 constructor 这个属性,那么这个 constructor 的属性就会指向原本的建构函式。
所以可以看到这里 newAmimal 是由 Animal 作为建构函式所产生的物件实体,所以 newAmimal 的 constructor 就会指向 Animal。
又所以,Dog.prototype.constructor = Dog;
才可以透过 Bibi.constructor
找到狗的原型喔!!!
所以整个的程式码会如下所示:
function Animal (family) { this.kingdom = '动物界'; this.family = family || '人科';}Animal.prototype.move = function () { console.log(this.name + ' 移动');};function Dog (name, color, size) { // 新增了这里 Animal.call(this, '犬科'); // 新增了这里 this.name = name; this.color = color || '白色'; this.size = size || '小';}Dog.prototype = Object.create(Animal.prototype);// 逻辑更为完善Dog.prototype.constructor = Dog;// 逻辑更为完善Dog.prototype.bark = function () { console.log(this.name + '吠叫');}var Bibi = new Dog('比比', '棕色', '小');console.log(Bibi);Bibi.bark();Bibi.move();
那么接着我们就练习猫科的新增吧!
而猫不会吠叫只会喵喵叫,所以在猫的原型上要挂载的是喵喵叫的方法!
function Animal (family) { this.kingdom = '动物界'; this.family = family || '人科';}Animal.prototype.move = function () { console.log(this.name + ' 移动');};function Dog (name, color, size) { // 新增了这里 Animal.call(this, '犬科'); // 新增了这里 this.name = name; this.color = color || '白色'; this.size = size || '小';}Dog.prototype = Object.create(Animal.prototype);// 逻辑更为完善Dog.prototype.constructor = Dog;// 逻辑更为完善Dog.prototype.bark = function () { console.log(this.name + '吠叫');}var Bibi = new Dog('比比', '棕色', '小');console.log(Bibi);Bibi.bark();Bibi.move();// var newAnimal = new Animal('新物种');// console.log(newAnimal);function Cat (name, color, size) { Animal.call(this, '猫科'); this.name = name; this.color = color || '白色'; this.size = size || '小';}Cat.prototype = Object.create(Animal.prototype);Cat.prototype.constructor = Cat;Cat.prototype.meow = function () { console.log(this.name + '喵喵叫');}var Kity = new Cat('凯蒂');Kity.meow();Kity.move();Kity.bark();
那么依照这样的方式呢,凯蒂能够喵喵叫,也能够移动,但是没办法像狗一样吠叫。
好~那么这篇文章就先介绍到这边,各位可以想想 建构函式、建构出来的实体、建构函式的原型以及建构出来的实体的 proto 属性之间的关係喔,这个部分也会在下一篇文章更深入的彙整介绍。
这篇文章就先到这里噜!希望对各位有帮助~汪汪