深刻解读JavaScript面向对象编程实践
2016/03/14 · JavaScript
· 4 评论 ·
面向对象
原文出处:
景庄(@ali景庄)
面向对象编程是用抽象格局创建基于实际世界模型的一种编程形式,首要概括模块化、多态、和包装两种技术。对JavaScript而言,其主干是永葆面向对象的,同时它也提供了强大灵活的按照原型的面向对象编程能力。
正文将会深深的追究关于使用JavaScript举行面向对象编程的有的中央基础知识,包含对象的成立,继承机制,最终还会简单的牵线如何借助ES6提供的新的类机制重写传统的JavaScript面向对象代码。
实际要总括这多少个概念已经很久了,只是以前平素都以为温馨还不算完全控制,而且知识点还不够系统,所以直接拖着,然则目前又再一次看了几篇小说,自己也测试了一晃,觉得始于有点清晰了,所以想在此处给自己做个总计吧,也冀望在学的你们可以在这边学到一点东西。不要躁动,逐步看,一边看一边做测试,那也是自我近年的觉悟。看了不自然会,要真正自己下手去测试一下。
长远解读 JavaScript 中的面向对象编程
2017/07/07 · JavaScript
·
面向对象
原文出处: 景庄
面向对象编程是用抽象形式创造基于具体世界模型的一种编程情势,首要概括模块化、多态、和包装二种技术。
对 JavaScript
而言,其主题是永葆面向对象的,同时它也提供了强压灵活的依照原型的面向对象编程能力。
本文将会深远的追究关于使用 JavaScript
进行面向对象编程的有的中坚基础知识,包罗对象的创办,继承机制,
末尾还会简单的介绍怎么样依靠 ES6
提供的新的类机制重写传统的JavaScript面向对象代码。
1.js创造对象的两种办法
面向对象的多少个概念
在进入正题前,先通晓传统的面向对象编程(例如Java)中常会涉嫌到的概念,大约能够概括:
- 类:定义对象的性状。它是目的的性质和章程的模板定义。
- 目的(或称实例):类的一个实例。
- 属性:对象的表征,比如颜色、尺寸等。
- 艺术:对象的一言一动,比如行走、说话等。
- 构造函数:对象初叶化的刹这被调用的不二法门。
- 继续:子类可以持续父类的特征。例如,猫继承了动物的形似特性。
- 卷入:一种把数据和相关的法门绑定在联名使用的章程。
- 空洞:结合复杂的接轨、方法、属性的目的可以模拟现实的模型。
- 多态:差别的类可以定义相同的办法或性能。
在JavaScript的面向对象编程中大约也囊括这几个。不过在称为上或许稍有两样,例如,JavaScript中并未原生的“类”的定义,
而只有对象的概念。由此,随着你认识的无时或忘,大家会混用对象、实例、构造函数等概念。
怎么着是目的?
我的接头就是那是一个存储灌,你能够在其中储存任何东西,那些事物就是大家事先学的种种js里面的数据类型,然后给每一个名字贴上一个名字,方便大家未来找到。
例子:
//这个myFirstObject里面有两个属性,分别是firstName和 favoriteAuthor
var myFirstObject = {firstName: "Richard", favoriteAuthor: "Conrad"};
面向对象的多少个概念
在进入正题前,先精通传统的面向对象编程(例如Java)中常会提到到的概念,大约可以概括:
- 类:定义对象的性状。它是目的的特性和格局的模版定义。
- 目的(或称实例):类的一个实例。
- 特性:对象的表征,比如颜色、尺寸等。
- 方法:对象的行为,比如行走、说话等。
- 构造函数:对象开首化的须臾被调用的不二法门。
- 接轨:子类能够持续父类的性状。例如,猫继承了动物的相似特性。
- 打包:一种把数量和血脉相通的法门绑定在一齐利用的法门。
- 架空:结合复杂的接轨、方法、属性的靶子可以模拟现实的模子。
- 多态:分歧的类可以定义相同的法子或性能。
在 JavaScript
的面向对象编程中几乎也包蕴这几个。可是在叫做上也许稍有分歧,例如,JavaScript
中从不原生的“类”的概念,
而只有对象的定义。因而,随着你认识的深切,大家会混用对象、实例、构造函数等概念。
工厂形式
对象(类)的创建
在JavaScript中,大家司空眼惯可以接纳构造函数来创设特定类型的目的。诸如Object和Array那样的原生构造函数,在运作时会自动出现在实践环境中。
此外,大家也可以创立自定义的构造函数。例如:
function Person(name, age, job) { this.name = name; this.age = age;
this.job = job; } var person1 = new Person(‘Weiwei’, 27, ‘Student’); var
person2 = new Person(‘Lily’, 25, ‘Doctor’);
1
2
3
4
5
6
7
8
|
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var person2 = new Person(‘Lily’, 25, ‘Doctor’);
|
依照规矩,构造函数始终都应有以一个大写字母起首(和Java中定义的类一样),普通函数则小写字母开首。
要创建Person
的新实例,必须运用new
操作符。以那种方法调用构造函数实际上会经历以下4个步骤:
- 创设一个新对象(实例)
- 将构造函数的成效域赋给新对象(也就是重设了
this
的指向,this
就本着了那个新目的) - 施行构造函数中的代码(为那个新对象添加属性)
- 回去新对象
有关new
操作符的越多内容请参考那篇文档。
在地点的例证中,我们创立了Person
的四个实例person1
和person2
。
那两个目的默许都有一个constructor
性能,该属性指向它们的构造函数Person
,也就是说:
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
1
2
|
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
|
怎样定义一个对象?
- 对象字面量
- 构造函数成立
- 原型格局开创
对象(类)的创建
在JavaScript中,大家平时可以利用构造函数来创制特定类型的靶子。诸如
Object 和 Array
那样的原生构造函数,在运转时会自动出现在举行环境中。别的,大家也得以创立自定义的构造函数。例如:
JavaScript
function Person(name, age, job) { this.name = name; this.age = age;
this.job = job; } var person1 = new Person(‘Weiwei’, 27, ‘Student’); var
person2 = new Person(‘Lily’, 25, ‘Doctor’);
1
2
3
4
5
6
7
|
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var person2 = new Person(‘Lily’, 25, ‘Doctor’);
|
按部就班规矩,构造函数始终都应有以一个大写字母早先(和Java中定义的类一样),普通函数则小写字母初始。
要创建 Person
的新实例,必须运用
new
操作符。
以那种方法调用构造函数实际上会经历以下4个步骤:
- 成立一个新对象(实例)
- 将构造函数的职能域赋给新对象(也就是重设了
this
的指向,this
就对准了那么些新目的) - 实践构造函数中的代码(为那几个新目的添加属性)
- 回去新对象
在地点的例证中,大家创立了 Person
的四个实例 person1
和 person2
。
那七个对象默许都有一个 constructor
属性,该属性指向它们的结构函数
Person
,也就是说:
JavaScript
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
1
2
|
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
|
为何会发生工厂方式,原因是应用同一个接口成立很多对象,会发出大量的重复代码,为驾驭决那一个标题,爆发了工厂情势。
自定义对象的序列检测
大家可以应用instanceof
操作符举行项目检测。大家创设的兼具指标既是Object
的实例,同时也是Person
的实例。
因为具备的对象都一而再自Object
。
console.log(person1 instanceof Object); //true console.log(person1
instanceof Person); //true console.log(person2 instanceof Object);
//true console.log(person2 instanceof Person); //true
1
2
3
4
|
console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true
console.log(person2 instanceof Object); //true
console.log(person2 instanceof Person); //true
|
目的字面量创制对象
那是最原始的不二法门,然则也不便利前边的五个目的的制造。
//这是一个mango对象,这个对象里面有color shape sweetness属性以及一个howSweetAmI的方法
var mango = {
color: "yellow",
shape: "round",
sweetness: 8,
howSweetAmI: function () {
console.log("Hmm Hmm Good");
}
}
自定义对象的档次检测
大家得以应用instanceof
操作符举行项目检测。大家创设的享有目标既是Object
的实例,同时也是Person
的实例。
因为具有的靶子都蝉联自Object
。
JavaScript
console.log(person1 instanceof Object); //true console.log(person1
instanceof Person); //true console.log(person2 instanceof Object);
//true console.log(person2 instanceof Person); //true
1
2
3
4
|
console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true
console.log(person2 instanceof Object); //true
console.log(person2 instanceof Person); //true
|
function createPerson(name,age,job){
构造函数的题材
我们不提出在构造函数中一贯定义方法,如若如此做的话,每个方法都要在每个实例上再也创建三回,那将万分损耗性能。
——不要忘了,ECMAScript中的函数是目的,每定义一个函数,也就实例化了一个对象。
有幸的是,在ECMAScript中,大家可以依靠原型对象来解决这些题材。
缺点:那种方法即便不难明了,但是试想一下,倘若大家要定义各个各种的水果对象,每一个水果都有color shape sweetnees的特性,大家都要一个个定义是还是不是会稍稍麻烦呢?
那看看下边那种构造函数的开创方法
构造函数的题材
我们不提出在构造函数中一直定义方法,倘使这么做的话,每个方法都要在各种实例上重复创造一回,那将不胜损耗性能。
——不要忘了,ECMAScript中的函数是目的,每定义一个函数,也就实例化了一个目的。
有幸的是,在ECMAScript中,大家得以看重原型对象来缓解那几个难点。
var o=new Object();
凭借原型格局定义对象的法门
大家创制的各种函数都有一个prototype
特性,那几个特性是一个指针,指向该函数的原型对象,
该目标涵盖了由特定类型的拥有实例共享的习性和办法。也就是说,大家可以行使原型对象来让所有目的实例共享它所包蕴的特性和方法。
function Person(name, age, job) { this.name = name; this.age = age;
this.job = job; } // 通过原型形式来添加所有实例共享的法子 // sayName()
方法将会被Person的具有实例共享,而幸免了双重创造Person.prototype.sayName = function () { console.log(this.name); }; var
person1 = new Person(‘Weiwei’, 27, ‘Student’); var person2 = new
Person(‘Lily’, 25, ‘Doctor’); console.log(person1.sayName ===
person2.sayName); // true person1.sayName(); // Weiwei
person2.sayName(); // Lily
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 通过原型模式来添加所有实例共享的方法
// sayName() 方法将会被Person的所有实例共享,而避免了重复创建
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var person2 = new Person(‘Lily’, 25, ‘Doctor’);
console.log(person1.sayName === person2.sayName); // true
person1.sayName(); // Weiwei
person2.sayName(); // Lily
|
正如上面的代码所示,通过原型方式定义的格局sayName()
为所有的实例所共享。也就是,
person1
和person2
走访的是同一个sayName()
函数。同样的,公共属性也得以选用原型格局开展定义。例如:
function Chinese (name) { this.name = name; } Chinese.prototype.country
= ‘China’; // 公共属性,所有实例共享
1
2
3
4
5
|
function Chinese (name) {
this.name = name;
}
Chinese.prototype.country = ‘China’; // 公共属性,所有实例共享
|
考虑用构造函数的开创方法
构造函数创造方法,就是概念一个构造函数,然后在里边安装属性和方法值,然后再用new去实例化对象,所有实例化的目的都会有构造函数里面的性能和章程。
//在这里定义一个构造函数,在构造函数里面定义属性和方法,注意这里需要用this,后面就可以通过new来实例化对象,使用new的时候,就会将this指向这个实例化的对象。
function Fruit (theColor, theSweetness, theFruitName, theNativeToLand) {
this.type = "水果"
this.color = theColor;
this.sweetness = theSweetness;
this.fruitName = theFruitName;
this.nativeToLand = theNativeToLand;
this.showName = function () {
console.log("This is a " + this.fruitName);
}
this.nativeTo = function () {
this.nativeToLand.forEach(function (eachCountry) {
console.log("Grown in:" + eachCountry);
});
}
}
凭借原型情势定义对象的格局
大家成立的每个函数都有一个prototype
特性,那些特性是一个指针,指向该函数的原型对象,
该目标涵盖了由特定类型的具备实例共享的性能和艺术。也就是说,大家得以行使原型对象来让具有目的实例共享它所蕴涵的属性和章程。
JavaScript
function Person(name, age, job) { this.name = name; this.age = age;
this.job = job; } // 通过原型情势来添加所有实例共享的办法 // sayName()
方法将会被Person的拥有实例共享,而防止了再一次成立Person.prototype.sayName = function () { console.log(this.name); }; var
person1 = new Person(‘Weiwei’, 27, ‘Student’); var person2 = new
Person(‘Lily’, 25, ‘Doctor’); console.log(person1.sayName ===
person2.sayName); // true person1.sayName(); // Weiwei
person2.sayName(); // 莉莉
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 通过原型模式来添加所有实例共享的方法
// sayName() 方法将会被Person的所有实例共享,而避免了重复创建
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var person2 = new Person(‘Lily’, 25, ‘Doctor’);
console.log(person1.sayName === person2.sayName); // true
person1.sayName(); // Weiwei
person2.sayName(); // Lily
|
正如上边的代码所示,通过原型方式定义的法子sayName()
为富有的实例所共享。也就是,
person1
和person2
做客的是同一个sayName()
函数。同样的,公共属性也足以应用原型形式举办定义。例如:
JavaScript
function Chinese (name) { this.name = name; } Chinese.prototype.country
= ‘China’; // 公共属性,所有实例共享
1
2
3
4
|
function Chinese (name) {
this.name = name;
}
Chinese.prototype.country = ‘China’; // 公共属性,所有实例共享
|
当我们new Person()
时,返回的Person
实例会构成构造函数中定义的属性、行为和原型中定义的性能、行为,
变更最后属于Person
实例的属性和行事。
构造函数中定义的习性和作为的先期级要比原型中定义的特性和行事的先行级高,若是构造函数和原型中定义了同名的性能或行为,
构造函数中的属性或作为会覆盖原型中的同名的属性或行为。
o.name=name;
原型对象
现行大家来长远的接头一下怎么着是原型对象。
如若创立了一个新函数,就会根据一组特定的平整为该函数创造一个prototype
特性,那一个特性指向函数的原型对象。
在默许景况下,所有原型对象都会自行获得一个constructor
属性,这几个特性包括一个对准prototype
特性所在函数的指针。
也就是说:Person.prototype.constructor
指向Person
构造函数。
开创了自定义的构造函数之后,其原型对象默许只会得到constructor
属性;至于其它办法,则都是从Object
持续而来的。
当调用构造函数成立一个新实例后,该实例之中将含有一个指南针(内部属性),指向构造函数的原型对象。ES5中称这一个指针为[[Prototype]]
,
在Firefox、Safari和Chrome在各种对象上都接济一个属性__proto__
(近年来已被打消);而在其他完结中,那么些特性对剧本则是全然不可知的。
要注意,其一链接存在于实例与构造函数的原型对象时期,而不是实例与构造函数之间。
那三者关系的示意图如下:
上图展现了Person
构造函数、Person
的原型对象以及Person
现有的多个实例之间的涉嫌。
Person.prototype
针对了原型对象Person.prototype.constructor
又指回了Person
构造函数Person
的种种实例person1
和person2
都包括一个中间属性(日常为__proto__
),person1.__proto__
和person2.__proto__
针对了原型对象
接下去,大家就可以直接用new的方法来创立各种各种的瓜果对象了。
//创建一个芒果的对象。
var mangoFruit = new Fruit ("Yellow", 8, "Mango", ["South America", "Central America", "West Africa"]);
mangoFruit.showName(); // This is a Mango.
mangoFruit.nativeTo();
//Grown in:South America
// Grown in:Central America
// Grown in:West Africa
//创建一个pineappleFruit的对象。
var pineappleFruit = new Fruit ("Brown", 5, "Pineapple", ["United States"]);
pineappleFruit.showName(); // This is a Pineapple.
是否很有益于,可以把构造函数想象成一个大工厂,然后你一旦使用new的措施去调用这些工厂,就一定于告诉这些工厂给我生养一个事物出来,那么那一个工厂就会用所有自己有些设备,把它抱有的事物能添丁的都生产出来。所以只要在那些工厂上的装备能生育出来的都会被生产。
再来思考一个标题,这个实例化对象之间是否实在都是有相似性的,就是你可以提炼出其中同样的特性和措施。像上面相当例子,所有水果的type属性和showName方法是否都是均等的啊?那大家是还是不是可以用原型来写?
原型对象
当今我们来深远的精晓一下怎样是原型对象。
如果创制了一个新函数,就会根据一组特定的平整为该函数创设一个prototype
特性,那些特性指向函数的原型对象。
在默许情形下,所有原型对象都会活动获得一个constructor
性能,这么些特性包罗一个针对prototype
性能所在函数的指针。
也就是说:Person.prototype.constructor
指向Person
构造函数。
始建了自定义的构造函数之后,其原型对象默许只会得到constructor
性能;至于此外措施,则都是从Object
后续而来的。
当调用构造函数创造一个新实例后,该实例之少将富含一个指南针(内部属性),指向构造函数的原型对象。ES5中称这么些指针为[[Prototype]]
,
在Firefox、Safari和Chrome在各样对象上都扶助一个性质__proto__
(近期已被撇下);而在别的完成中,这一个特性对剧本则是一心不可知的。
要注意,本条链接存在于实例与构造函数的原型对象时期,而不是实例与构造函数之间。
那三者关系的示意图如下:
上图体现了Person
构造函数、Person
的原型对象以及Person
幸存的三个实例之间的关系。
Person.prototype
本着了原型对象Person.prototype.constructor
又指回了Person
构造函数Person
的每个实例person1
和person2
都包括一个里面属性(经常为__proto__
),person1.__proto__
和person2.__proto__
针对了原型对象
o.age=age;
摸索对象属性
从上图大家发现,即使Person
的三个实例都不带有属性和格局,但大家却能够调用person1.sayName()
。
那是通过寻找对象属性的进程来兑现的。
- 摸索首先从目的实例自己初始(实例
person1
有sayName
属性吗?——没有) - 比方没找到,则继续寻找指针指向的原型对象(
person1.__proto__
有sayName
属性吗?——有)
那也是多少个目的实例共享原型所保存的特性和章程的基本原理。
小心,即使我们在目的的实例中重写了某个原型中已存在的特性,则该实例属性会屏蔽原型中的那些属性。
那儿,可以使用delete
操作符删除实例上的特性。
什么是原型?prototype
js中每一个函数都会有和好的一个原型对象,这几个原型对象叫做prototype.而颇具通过这些构造函数实例化的对象都会指向这些原型。其实你可以设想一下,构造函数是工厂来说,原型其实是还是不是可以是仓库,所有实例化的对象就足以从仓库里面拿东西。所以大家能够把富有目的公用的特性和措施给放在prototype上面,那样就能够幸免属性和艺术的双重定义。上面用一个例证和图来说爱他美下。
//这里我们使用原型来创建对象,所有对象共用的属性和方法就放在prototype上。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 通过原型模式来添加所有实例共享的方法
// sayName() 方法将会被Person的所有实例共享,而避免了重复创建
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person('Weiwei', 27, 'Student');
var person2 = new Person('Lily', 25, 'Doctor');
person1.sayName(); // Weiwei
person2.sayName(); // Lily
实例化的目标中的name age
job属性是从构造函数那获得的,而实例化的靶子的原型指向了构造函数的原型对象,所以也会有sayName方法。
image.png
//注意,那里是出口true,所以实际person1和person2的sayName方法都是同一个,来自同一个地方。
console.log(person1.sayName === person2.sayName); // true
搜索对象属性
从上图大家发现,纵然Person
的五个实例都不带有属性和方法,但大家却可以调用person1.sayName()
。
这是经过搜索对象属性的进程来贯彻的。
- 搜索首先从目的实例本人早先(实例
person1
有sayName
属性吗?——没有) - 设若没找到,则延续搜寻指针指向的原型对象(
person1.__proto__
有sayName
属性吗?——有)
那也是五个目的实例共享原型所保存的特性和章程的基本原理。
只顾,要是大家在目标的实例中重写了某个原型中已存在的特性,则该实例属性会屏蔽原型中的这多少个属性。
此刻,可以选拔delete
操作符删除实例上的习性。
中的面向对象编程,深刻解读JavaScript面向对象编程实践。 o.job=job;
Object.getPrototypeOf()
根据ECMAScript标准,someObject.[[Prototype]]
符号是用于指派
someObject
的原型。
那些等同于 JavaScript 的 __proto__
属性(现已弃用)。
从ECMAScript 5开始, [[Prototype]]
可以用Object.getPrototypeOf()
和Object.setPrototypeOf()
做客器来访问。
其中Object.getPrototypeOf()
在享有匡助的兑现中,那一个措施重回[[Prototype]]
的值。例如:
person1.__proto__ === Object.getPrototypeOf(person1); // true
Object.getPrototypeOf(person1) === Person.prototype; // true
1
2
|
person1.__proto__ === Object.getPrototypeOf(person1); // true
Object.getPrototypeOf(person1) === Person.prototype; // true
|
也就是说,Object.getPrototypeOf(p1)
回来的指标实际就是其一目的的原型。
以此办法的包容性请参见该链接)。
微小计算一下:
目的有两种不相同的创制格局,对象字面量,构造函数,结合原型来创设,最得力的也就是第三种创设格局了,避免同一属性和章程的重复创建,所以可以将目的公用
的性能和艺术定义在prototype上。
Object.getPrototypeOf()
根据ECMAScript标准,someObject.[[Prototype]]
符号是用于指派
someObject
的原型。
那个等同于 JavaScript 的 __proto__
属性(现已弃用,因为它不是明媒正娶)。
从ECMAScript 5开始, [[Prototype]]
可以用Object.getPrototypeOf()
和Object.setPrototypeOf()
做客器来访问。
其中Object.getPrototypeOf()
在所有辅助的贯彻中,这一个艺术重临[[Prototype]]
的值。例如:
JavaScript
person1.__proto__ === Object.getPrototypeOf(person1); // true
Object.getPrototypeOf(person1) === Person.prototype; // true
1
2
|
person1.__proto__ === Object.getPrototypeOf(person1); // true
Object.getPrototypeOf(person1) === Person.prototype; // true
|
也就是说,Object.getPrototypeOf(p1)
回来的靶子实际就是以此目的的原型。
本条点子的包容性请参考该链接)。
o.sayName=function(){
Object.keys()
要取得对象上所有可枚举的实例属性,能够动用ES5中的Object.keys()
方法。例如:
Object.keys(p1); // [“name”, “age”, “job”]
1
|
Object.keys(p1); // ["name", "age", "job"]
|
别的,假使你想要得到所有实例属性,无论它是还是不是可枚举,都足以利用Object.getOwnPropertyName()
方法。
!!!!注意!!!!
一经采用原型继承的话,若是有多少个对象和性质要同时一头定义的话,必要专注将原型prototype的constructor属性重新赋值,是否听不懂了,别急,先看率先个例证,再看大家前边立异的。
例子1
//这是我们定义水果的属性和方法
function Fruit () {
}
//一个一个使用Fruit.prototype来一一定义各个属性和方法。
Fruit.prototype.color = "Yellow";
Fruit.prototype.sweetness = 7;
Fruit.prototype.fruitName = "Generic Fruit";
Fruit.prototype.nativeToLand = "USA";
Fruit.prototype.showName = function () {
console.log("This is a " + this.fruitName);
}
Fruit.prototype.nativeTo = function () {
console.log("Grown in:" + this.nativeToLand);
}
地点的方法即使也是可行的,可是一旦属性和章程太多的话,是否太低效了。
更简便易行的原型创设方法:
function Fruit () {
}
//一个一个使用Fruit.prototype来一一定义各个属性和方法。
Fruit.prototype= {
//这里一定要将prototype的constructor属性重新指向Fruit。因为我们这样相当于是重写了prototype的值。
constructor: Fruit,
color = "Yellow";
sweetness = 7;
fruitName = "Generic Fruit";
showName = function () {
console.log("This is a " + this.fruitName);
}
nativeTo = function () {
console.log("Grown in:" + this.nativeToLand);
}
}
地点的事例看懂了呢?就是每一个构造函数的prototype属性都会自带有一个constructor属性,这么些constructor属性又针对了构造函数,所以大家像上面那样定义的时候,也要将这一个constructor属性给重新指向构造函数。(可以重新看一下上面我付出的可怜图)
Object.keys()
要赢得对象上所有可枚举的实例属性,可以行使ES5中的Object.keys()
方法。例如:
JavaScript
Object.keys(p1); // [“name”, “age”, “job”]
1
|
Object.keys(p1); // ["name", "age", "job"]
|
此外,如若你想要获得所有实例属性,无论它是不是可枚举,都可以应用Object.getOwnPropertyName()
方法。
console.log(this.name);
更简便易行的原型语法
在上头的代码中,假若我们要添加原型属性和章程,就要重新的敲五次Person.prototype
。为了削减那一个重复的进程,
更广阔的做法是用一个带有所有属性和章程的靶子字面量来重写整个原型对象。
参考资料。
function Person(name, age, job) { this.name = name; this.age = age;
this.job = job; } Person.prototype = { //
那里不可不要双重将构造函数指回Person构造函数,否则会指向那一个新创造的目标constructor: Person, // Attention! sayName: function () {
console.log(this.name); } }; var person1 = new Person(‘Weiwei’, 27,
‘Student’); var person2 = new Person(‘Lily’, 25, ‘Doctor’);
console.log(person1.sayName === person2.sayName); // true
person1.sayName(); // Weiwei person2.sayName(); // Lily
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
Person.prototype = {
// 这里务必要重新将构造函数指回Person构造函数,否则会指向这个新创建的对象
constructor: Person, // Attention!
sayName: function () {
console.log(this.name);
}
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var person2 = new Person(‘Lily’, 25, ‘Doctor’);
console.log(person1.sayName === person2.sayName); // true
person1.sayName(); // Weiwei
person2.sayName(); // Lily
|
在下面的代码中尤其包蕴了一个constructor
性能,并将它的值设置为Person
,从而保障了经过该属性可以访问到合适的值。
瞩目,以那种方法重设constructor
性能会造成它的[[Enumerable]]
特征设置为true
。默许意况下,原生的constructor
属性是恒河沙数的。
你可以运用Object.defineProperty()
:
// 重设构造函数,只适用于ES5金童玉女的浏览器
Object.defineProperty(Person.prototype, “constructor”, { enumerable:
false, value: Person });
1
2
3
4
5
|
// 重设构造函数,只适用于ES5兼容的浏览器
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
|
怎么着读取对象的性能:
// We have been using dot notation so far in the examples above, here is another example again:
var book = {title: "Ways to Go", pages: 280, bookMark1:"Page 20"};
// To access the properties of the book object with dot notation, you do this:
console.log ( book.title); // Ways to Go
console.log ( book.pages); // 280
//当然,也可以用方括号来写:
console.log ( book["title"]); //Ways to Go
console.log ( book["pages"]); // 280
更简明的原型语法
在上边的代码中,如若大家要添加原型属性和办法,就要重新的敲四遍Person.prototype
。为了削减这一个重复的进度,
更广泛的做法是用一个暗含所有属性和办法的目标字面量来重写整个原型对象。
参考资料。
JavaScript
function Person(name, age, job) { this.name = name; this.age = age;
this.job = job; } // 重写整个原型对象 Person.prototype = { //
那里不可不要重新将构造函数指回Person构造函数,否则会指向那个新创设的目的constructor: Person, // Attention! sayName: function () {
console.log(this.name); } }; var person1 = new Person(‘Weiwei’, 27,
‘Student’); var person2 = new Person(‘Lily’, 25, ‘Doctor’);
console.log(person1.sayName === person2.sayName); // true
person1.sayName(); // Weiwei person2.sayName(); // Lily
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 重写整个原型对象
Person.prototype = {
// 这里务必要重新将构造函数指回Person构造函数,否则会指向这个新创建的对象
constructor: Person, // Attention!
sayName: function () {
console.log(this.name);
}
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var person2 = new Person(‘Lily’, 25, ‘Doctor’);
console.log(person1.sayName === person2.sayName); // true
person1.sayName(); // Weiwei
person2.sayName(); // Lily
|
在上边的代码中特地包蕴了一个constructor
特性,并将它的值设置为Person
,从而确保了经过该属性可以访问到适合的值。
只顾,以这种艺术重设constructor
特性会招致它的[[Enumerable]]
特性设置为true
。默认情形下,原生的constructor
属性是成千上万的。
您能够选取Object.defineProperty()
:
JavaScript
// 重设构造函数,只适用于ES5非常的浏览器
Object.defineProperty(Person.prototype, “constructor”, { enumerable:
false, value: Person });
1
2
3
4
5
|
// 重设构造函数,只适用于ES5兼容的浏览器
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
|
}
整合使用构造函数方式和原型形式
开创自定义类型的最广泛方法,就是构成使用构造函数情势与原型格局。构造函数格局用于定义实例属性,
而原型情势用于定义方法和共享的性能。结果,每个实例都会有协调的一份实例属性的副本,但与此同时又共享着对方的引用,
最大限度的节约了内存。
什么落成目标的继续:
- 原型继承
- 构造函数继承
- 原型和构造函数继承
- 创办空对象方法
原型继承:
- 构造函数都有一个对准原型对象的指针
- 原型对象都有一个针对构造函数的constructor
- 实例化对象都有一个针对性原型的[[prototype]]属性
function Father () {
this.fatherValue = true;
}
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue);
};
function Child () {
this.childValue = false;
}
// 实现继承:继承自Father
Child.prototype = new Father();
Child.prototype.getChildValue = function () {
console.log(this.childValue);
};
var instance = new Child();
instance.getFatherValue(); // true
instance.getChildValue(); // false
上边的关键点就是用“`Child.prototype = new Father();

可以看一下这一个原型链的一个搜索的过程:
var instance = new Child();
instance.getFatherValue(); // true
instance.getChildValue(); // false
当我们查找```instance.getFatherValue(); ```的时候,是如何一个查找的过程呢?
- 先看一下instance 实例上有没有,没有则继续
- Chile prototype上查找有没有,也没有该方法,则继续向上查找
- 向上查找的是Father prototype的属性和方法,查找到了,则输出。
>这种原型继承的方法,其实就相当于延长了Child的原型链,因为其原型现在又可以再向上查找到Father的原型,相当于延长原型链之后可以继续再向上去查找到Father原型上的属性和方法。
#####思考一下:这其实也给了我们一个提示,如果实例,原型上有相同的方法的话,我们一般读取该属性的时候,也是直接读取到了实例上的属性和方法,除非实例本身没有,才会继续往上查找。
####缺点:
这个方法其实也是有缺点的,因为Child的实例化对象的一些属性和方法都是在该原型链上查找的,所以一些引用值得修改也会影响到所有实例化对象的属性,先看个例子。
function father(name,age) {
this.name = name
this.age = age
this.friends = [“lili”,”koko”]
}
father.prototype.sayname = function () {
console.log(this.name)
}
function children(school) {
this.school = school
}
children.prototype = new father()
children.prototype.sayname = function () {
console.log(“我就是不说自己的名字”)
}
var instance = new children(“幼儿园”)
var instance2 = new children(“幼儿园”)
//那里大家修改了instance的friends的值
instance.friends.push(“yoyo”)
//大家输出children的八个实例对象试一下,看看多个的属性值的差异
console.log(instance)
console.log(instance2)


其实从上面两个图也可以发现,一旦修改了一个实例对象上的一个引用值,其他实例化对象的属性值也跟着变化了。因为这里的friends是引用类型的数据,所有的实例都会共享这个属性值,一旦修改其他也跟着修改了。
####构造函数继承
function Animal(){
this.species = “动物”;
}
Animal.prototype.say = function(){console.log(“hahaha”)}
function Cat(name,color){
//那里运用的是构造函数的持续,调用Animal构造函数,再用apply将this指向Cat本身
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
var cat1 = new Cat(“大毛”,”黄色”);
alert(cat1.species); // 动物
//那样的话Cat的实例化对象就都有Animal的属性了。
>//Cat这个实例化对象就有Animal的属性,但是不会继承来自于Animal原型上的方法。

>构造函数的好处是可以在调用的时候输入参数,```Animal.apply(this, arguments);
```这里可以重新将Cat的参数赋值给Animal中的构造函数。但是这样其实还是有不好之处就是每次新生成一个实例化对象的时候,就会调用一次构造函数。除此之外,Cat并不能继承来自于Animal原型上的方法,这不能实现方法上的复用。
所以,我们可以考虑结合原型方法和构造函数方法。
刚刚是不是说到,只使用原型方法的话,继承父类的所有属性和方法,但是所有实例没有自己的属性,可能会因为一个实例的属性的更改而影响到其他实例;而构造函数的方法只能实现构造函数内的属性方法继承,不能实现父类原型上的继承;;
那就结合这两种方法来实现以下;
// 父类构造函数
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 父类方法
Person.prototype.sayName = function () {
console.log(this.name);
};
// ————–
// 子类构造函数
function Student (name, age, job, school) {
// 继承父类的有着实例属性(得到父类构造函数中的属性)
Person.call(this, name, age, job);
this.school = school; // 添加新的子类属性
}
// 继承父类的原型方法(获得父类原型链上的性能和章程)
Student.prototype = new Person();
// 新增的子类方法
Student.prototype.saySchool = function () {
console.log(this.school);
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var student1 = new Student(‘Lily’, 25, ‘Doctor’, “Southeast
University”);
console.log(person1.sayName === student1.sayName); // true
person1.sayName(); // Weiwei
student1.sayName(); // Lily
student1.saySchool(); // Southeast University

这个就是比较好的继承方法,将父类的属性继承过来,所有的实例都有自己的属性,同时将原型上的方法也继承过来,实现所有实例都有公共的属性和方法。当然,细心的你也许已经发现了,就是这个Student子类的原型上除了有saySchool方法之外,还有父类构造函数内的那些name job age属性,那是因为我们是使用```Student.prototype = new Person();```来实现继承的,所以该原型实际上就是Person的实例;
所以其实这个方法虽然是好,但是也会出现这样一个情况,属性的覆盖,原型上还有对应父类的属性。这也不是我们最初想要的结果。
所以,我们又引入了另外一个方法
####利用中间空对象的方法继承。
>什么意思呢?我们上面的结合原型和构造函数的方法之所以会出现原型上还有相同的属性的问题是因为,我们用```Student.prototype = new Person();```来实现继承,相当于把Student.prototype重新赋值成Person的实例了,我们就肯定会有Person 构造函数上的属性和原型上的方法。那么我们要的最理想的状态就是用```Student.prototype = new Person();```的时候,Person的构造函数上没有属性,但是这显然不够理智,那么我们就可以引入一个中间的空对象,来实现继承。
啊啊啊,还是看例子吧。
//若是这样子的话,是或不是很周到,Child的原型是F的一个实例,而F的构造函数大家是安装成空的。
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
>所以我们可以用这样的方式来封装起来以后可以使用‘
//那几个就是Child继承Parent的方法。
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
我们再来写个例子吧;
// 父类构造函数
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 父类方法
Person.prototype.sayName = function () {
console.log(this.name);
};
// ————–
// 子类构造函数
function Student (name, age, job, school) {
// 继承父类的享有实例属性(得到父类构造函数中的属性)
Person.call(this, name, age, job);
this.school = school; // 添加新的子类属性
}
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
extend( Student,Person);
//调用该办法,达成持续父类原型链上的属性和艺术;
// 新增的子类方法
Student.prototype.saySchool = function () {
console.log(this.school);
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var student1 = new Student(‘Lily’, 25, ‘Doctor’, “Southeast
University”);
console.log(person1.sayName === student1.sayName); // true
person1.sayName(); // Weiwei
student1.sayName(); // Lily
student1.saySchool(); // Southeast University
console.log(student1)

>这样继承是不是好多了,至少跟前面的例子相比,我们的原型链上不会再继承来自父类上的属性;
>后面还有方法会继续总结的,今天先写到这里好了,感觉自己写的过程真的会发现很不一样,也算是了解多了一些。
参考链接:
http://javascriptissexy.com/javascript-objects-in-detail/#
http://javascriptissexy.com/javascript-prototype-in-plain-detailed-language/#
http://javascriptissexy.com/oop-in-javascript-what-you-need-to-know/#
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
结缘使用构造函数格局和原型方式
始建自定义类型的最广泛方法,就是结合使用构造函数格局与原型格局。构造函数情势用于定义实例属性,
而原型格局用于定义方法和共享的性质。结果,每个实例都会有谈得来的一份实例属性的副本,但还要又共享着对方的引用,
最大限度的节约了内存。
return o;
继承
大多的面向对象语言都协助三种持续格局:接口继承和落到实处持续。ECMAScript只协助落到实处三番五次,而且其达成一连主要依赖原型链来达成。
继承
基本上的面向对象语言都援救三种持续格局:接口继承和促成持续。ECMAScript只帮忙落到实处延续,而且其完毕持续主要器重原型链来落成。
前边大家精晓,JavaScript中实例的性质和作为是由构造函数和原型两有些共同构成的。如若我们想让Child
继承Father
,
那么我们就须求把Father
构造函数和原型中属性和行事全体传给Child
的构造函数和原型。
}
原型链继承
选取原型链作为贯彻一而再的主导思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。首先大家先想起一些基本概念:
- 每个构造函数都有一个原型对象(
prototype
) - 原型对象涵盖一个针对性构造函数的指针(
constructor
) - 实例都包括一个对准原型对象的里边指针(
[[Prototype]]
)
若果我们让原型对象等于另一个门类的兑现,结果会怎样?分明,那时候的原型对象将涵盖一个对准另一个原型的指针,
相应的,另一个原型中也含有着一个针对性另一个构造函数的指针。假若另一个原型又是另一个项目的实例,那么上述提到依旧成立,
诸如此类罕见推进,就结成了实例与原型的链条。
更详细的内容能够参照那一个链接。
先看一个概括的例子,它以身作则了接纳原型链完毕持续的骨干框架:
function Father () { this.fatherValue = true; }
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue); }; function Child () { this.childValue =
false; } // 达成持续:继承自Father Child.prototype = new Father();
Child.prototype.getChildValue = function () {
console.log(this.childValue); }; var instance = new Child();
instance.getFatherValue(); // true instance.getChildValue(); // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
function Father () {
this.fatherValue = true;
}
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue);
};
function Child () {
this.childValue = false;
}
// 实现继承:继承自Father
Child.prototype = new Father();
Child.prototype.getChildValue = function () {
console.log(this.childValue);
};
var instance = new Child();
instance.getFatherValue(); // true
instance.getChildValue(); // false
|
在上头的代码中,原型链继承的着力语句是Child.prototype = new Father()
,它已毕了Child
对Father
的继承,
而一而再是透过成立Father
的实例,并将该实例赋给Child.prototype
实现的。
已毕的原形是重写原型对象,代之以一个新品类的实例。也就是说,原来存在于Father
的实例中的所有属性和措施,
当今也存在于Child.prototype
中了。
其一例子中的实例以及构造函数和原型之间的关系如下图所示:
在上头的代码中,我们从没行使Child
默认提供的原型,而是给它换了一个新原型;那个新原型就是Father
的实例。
于是乎,新原型不仅有着了作为一个Father
的实例所负有的整个性质和办法。而且其里面还有一个指南针[[Prototype]]
,指向了Father
的原型。
instance
指向Child
的原型对象Child
的原型对象指向Father
的原型对象getFatherValue()
方法如故还在Father.prototype
中- 但是,
fatherValue
则位于Child.prototype
中 instance.constructor
今日针对的是Father
因为fatherValue
是一个实例属性,而getFatherValue()
则是一个原型方法。既然Child.prototype
现在是Father
的实例,
那么fatherValue
自然就坐落该实例中。
因而完成原型链,本质上增加了本章后边介绍的原型搜索机制。例如,instance.getFatherValue()
会经历多个搜索步骤:
- 搜寻实例
- 搜索
Child.prototype
- 搜索
Father.prototype
原型链继承
采取原型链作为落到实处持续的要旨情维是:利用原型让一个引用类型继承另一个引用类型的性能和办法。首先大家先想起一些基本概念:
- 各种构造函数都有一个原型对象(
prototype
) - 原型对象涵盖一个针对构造函数的指针(
constructor
) - 实例都包含一个针对性原型对象的中间指针(
[[Prototype]]
)
假若大家让原型对象等于另一个系列的完毕,结果会如何?分明,此时的原型对象将涵盖一个针对另一个原型的指针,
相应的,另一个原型中也蕴藏着一个对准另一个构造函数的指针。假若另一个原型又是另一个体系的实例,那么上述提到照旧创立,
那般罕见递进,就组成了实例与原型的链子。
更详实的始末可以参考本条链接。
先看一个大概的事例,它以身作则了运用原型链完毕一而再的着力框架:
JavaScript
function Father () { this.fatherValue = true; }
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue); }; function Child () { this.childValue =
false; } // 完毕持续:继承自Father Child.prototype = new Father();
Child.prototype.getChildValue = function () {
console.log(this.childValue); }; var instance = new Child();
instance.getFatherValue(); // true instance.getChildValue(); // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function Father () {
this.fatherValue = true;
}
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue);
};
function Child () {
this.childValue = false;
}
// 实现继承:继承自Father
Child.prototype = new Father();
Child.prototype.getChildValue = function () {
console.log(this.childValue);
};
var instance = new Child();
instance.getFatherValue(); // true
instance.getChildValue(); // false
|
在下边的代码中,原型链继承的中央语句是Child.prototype = new Father()
,它落成了Child
对Father
的继承,
而持续是透过创办Father
的实例,并将该实例赋给Child.prototype
实现的。
贯彻的真面目是重写原型对象,代之以一个新品类的实例。也就是说,原来存在于Father
的实例中的所有属性和格局,
现今也存在于Child.prototype
中了。
这么些事例中的实例以及构造函数和原型之间的关联如下图所示:
在地点的代码中,大家向来不选拔Child
默许提供的原型,而是给它换了一个新原型;那几个新原型就是Father
的实例。
于是,新原型不仅有着了作为一个Father
的实例所具备的满贯性能和办法。而且其内部还有一个指针[[Prototype]]
,指向了Father
的原型。
instance
指向Child
的原型对象Child
的原型对象指向Father
的原型对象getFatherValue()
办法依旧还在Father.prototype
中- 但是,
fatherValue
则位于Child.prototype
中 instance.constructor
后天本着的是Father
因为fatherValue
是一个实例属性,而getFatherValue()
则是一个原型方法。既然Child.prototype
现在是Father
的实例,
那么fatherValue
理所当然就放在该实例中。
经过兑现原型链,本质上扩充了本章前边介绍的原型搜索机制。例如,instance.getFatherValue()
会经历多个搜索步骤:
- 搜寻实例
- 搜索
Child.prototype
- 搜索
Father.prototype
var person1=createPerson(“kobe”,”34″,”player”);
别忘了Object
所有的函数都默许原型都是Object
的实例,因而默许原型都会包涵一个里头指针[[Prototype]]
,指向Object.prototype
。
那也多亏拥有自定义类型都会一而再toString()
、valueOf()
等默许方法的根本原因。所以,
大家说地点例子显示的原型链中还相应包罗此外一个持续层次。关于Object
的更加多内容,可以参考这篇博客。
也就是说,Child
继承了Father
,而Father
继承了Object
。当调用了instance.toString()
时,
实质上调用的是保存在Object.prototype
中的那多少个格局。
别忘了Object
怀有的函数都默认原型都是Object
的实例,由此默认原型都会包括一个里面指针[[Prototype]]
,指向Object.prototype
。
那也正是拥有自定义类型都会一连toString()
、valueOf()
等默许方法的根本原因。所以,
我们说地点例子展示的原型链中还相应包蕴此外一个接续层次。关于Object
的越来越多内容,可以参照那篇博客。
也就是说,Child
继承了Father
,而Father
继承了Object
。当调用了instance.toString()
时,
实则调用的是保存在Object.prototype
中的那些格局。
var person2=createPerosn(“patty”,”32″,”singer”);
原型链继承的难题
第一是逐一,一定要先再而三父类,然后为子类添加新格局。
其次,应用原型链完结三番五次时,不能接纳对象字面量创设原型方法。因为这么做就会重写原型链,如上面的例证所示:
function Father () { this.fatherValue = true; }
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue); }; function Child () { this.childValue =
false; } // 继承了Father // 此时的原型链为 Child -> Father ->
Object Child.prototype = new Father(); //
使用字面量添加新格局,会造成上一行代码无效 //
此时大家考虑的原型链被割裂,而是变成 Child -> Object Child.prototype
= { getChildValue: function () { console.log(this.childValue); } }; var
instance = new Child(); instance.getChildValue(); // false
instance.getFatherValue(); // error!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
function Father () {
this.fatherValue = true;
}
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue);
};
function Child () {
this.childValue = false;
}
// 继承了Father
// 此时的原型链为 Child -> Father -> Object
Child.prototype = new Father();
// 使用字面量添加新方法,会导致上一行代码无效
// 此时我们设想的原型链被切断,而是变成 Child -> Object
Child.prototype = {
getChildValue: function () {
console.log(this.childValue);
}
};
var instance = new Child();
instance.getChildValue(); // false
instance.getFatherValue(); // error!
|
在上头的代码中,我们连年四次修改了Child.prototype
的值。由于现在的原型包涵的是一个Object
的实例,
而非Father
的实例,因而大家考虑中的原型链已经被切断——Child
和Father
以内一度远非提到了。
说到底,在创立子类型的实例时,不可以向超类型的构造函数中传送参数。实际上,应该就是没有章程在不影响所有目的实例的情形下,
给超类型的构造函数传递参数。由此,大家很少单独选择原型链。
原型链继承的题材
率先是各类,一定要先一连父类,然后为子类添加新章程。
其次,运用原型链完成一连时,不可能运用对象字面量成立原型方法。因为如此做就会重写原型链,如上边的事例所示:
JavaScript
function Father () { this.fatherValue = true; }
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue); }; function Child () { this.childValue =
false; } // 继承了Father // 此时的原型链为 Child -> Father ->
Object Child.prototype = new Father(); //
使用字面量添加新措施,会促成上一行代码无效 //
此时我们考虑的原型链被切断,而是成为 Child -> Object //
所以大家不推荐这么写了 Child.prototype = { getChildValue: function () {
console.log(this.childValue); } }; var instance = new Child();
instance.getChildValue(); // false instance.getFatherValue(); // error!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function Father () {
this.fatherValue = true;
}
Father.prototype.getFatherValue = function () {
console.log(this.fatherValue);
};
function Child () {
this.childValue = false;
}
// 继承了Father
// 此时的原型链为 Child -> Father -> Object
Child.prototype = new Father();
// 使用字面量添加新方法,会导致上一行代码无效
// 此时我们设想的原型链被切断,而是变成 Child -> Object
// 所以我们不推荐这么写了
Child.prototype = {
getChildValue: function () {
console.log(this.childValue);
}
};
var instance = new Child();
instance.getChildValue(); // false
instance.getFatherValue(); // error!
|
在上边的代码中,大家总是三次修改了Child.prototype
的值。由于现在的原型包罗的是一个Object
的实例,
而非Father
的实例,由此大家考虑中的原型链已经被割裂——Child
和Father
里面业已远非提到了。
末尾,在开创子类型的实例时,无法向超类型的构造函数中传送参数。实际上,应该就是没有章程在不影响所有目标实例的情事下,
给超类型的构造函数传递参数。因而,我们很少单独行使原型链。
构造函数方式
借用构造函数继承
借用构造函数(constructor
stealing)的基本思想如下:即在子类构造函数的其中调用超类型构造函数。
function Father (name) { this.name = name; this.colors = [‘red’,
‘blue’, ‘green’]; } function Child (name) { //
继承了Father,同时传递了参数 Father.call(this, name); } var instance1 =
new Child(“weiwei”); instance1.colors.push(‘black’);
console.log(instance1.colors); // [ ‘red’, ‘blue’, ‘green’, ‘black’ ]
console.log(instance1.name); // weiwei var instance2 = new
Child(“lily”); console.log(instance2.colors); // [ ‘red’, ‘blue’,
‘green’ ] console.log(instance2.name); // lily
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function Father (name) {
this.name = name;
this.colors = [‘red’, ‘blue’, ‘green’];
}
function Child (name) {
// 继承了Father,同时传递了参数
Father.call(this, name);
}
var instance1 = new Child("weiwei");
instance1.colors.push(‘black’);
console.log(instance1.colors); // [ ‘red’, ‘blue’, ‘green’, ‘black’ ]
console.log(instance1.name); // weiwei
var instance2 = new Child("lily");
console.log(instance2.colors); // [ ‘red’, ‘blue’, ‘green’ ]
console.log(instance2.name); // lily
|
为了保障Father
构造函数不会重写子类型的性能,可以在调用超类型构造函数后,再添加应该在子类型中定义的性质。
借用构造函数继承
借用构造函数(constructor
stealing)的骨干考虑如下:即在子类构造函数的中间调用超类型构造函数。
JavaScript
function Father (name) { this.name = name; this.colors = [‘red’,
‘blue’, ‘green’]; } function Child (name) { //
继承了Father,同时传递了参数 //
之所以那样做,是为了取得Father构造函数中的所有属性和艺术 //
之所以用call,是为着校订Father内部this的对准 Father.call(this, name); }
var instance1 = new Child(“weiwei”); instance1.colors.push(‘black’);
console.log(instance1.colors); // [ ‘red’, ‘blue’, ‘green’, ‘black’ ]
console.log(instance1.name); // weiwei var instance2 = new
Child(“lily”); console.log(instance2.colors); // [ ‘red’, ‘blue’,
‘green’ ] console.log(instance2.name); // lily
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function Father (name) {
this.name = name;
this.colors = [‘red’, ‘blue’, ‘green’];
}
function Child (name) {
// 继承了Father,同时传递了参数
// 之所以这么做,是为了获得Father构造函数中的所有属性和方法
// 之所以用call,是为了修正Father内部this的指向
Father.call(this, name);
}
var instance1 = new Child("weiwei");
instance1.colors.push(‘black’);
console.log(instance1.colors); // [ ‘red’, ‘blue’, ‘green’, ‘black’ ]
console.log(instance1.name); // weiwei
var instance2 = new Child("lily");
console.log(instance2.colors); // [ ‘red’, ‘blue’, ‘green’ ]
console.log(instance2.name); // lily
|
为了有限扶助Father
构造函数不会重写子类型的性能,可以在调用超类型构造函数后,再添加应该在子类型中定义的习性。
与工厂格局不一样的是,没有显示的创制对象,直接将性能和格局赋值this对象,没有return语句。
借用构造函数的短处
同构造函数一样,不能落成方式的复用。
借用构造函数的症结
同构造函数一样,无法兑现情势的复用(所有的法门会被另行创制一份)。
function Person(name,age,job){
结缘使用原型链和借用构造函数
平时,大家会构成使用原型链继承和借用构造函数来达成一而再。也就是说,使用原型链实现对原型属性和措施的后续,
而通过借用构造函数来完结对实例属性的一而再。那样,既通过在原型上定义方法完结了函数复用,又可以确保每个实例都有它自己的属性。
俺们改造最初的事例如下:
// 父类构造函数 function Person (name, age, job) { this.name = name;
this.age = age; this.job = job; } // 父类方法 Person.prototype.sayName =
function () { console.log(this.name); }; // ————– //
子类构造函数 function Student (name, age, job, school) { //
继承父类的具备实例属性 Person.call(this, name, age, job); this.school =
school; // 添加新的子类属性 } // 继承父类的原型方法 Student.prototype =
new Person(); // 新增的子类方法 Student.prototype.saySchool = function
() { console.log(this.school); }; var person1 = new Person(‘Weiwei’, 27,
‘Student’); var student1 = new Student(‘Lily’, 25, ‘Doctor’, “Southeast
University”); console.log(person1.sayName === student1.sayName); // true
person1.sayName(); // Weiwei student1.sayName(); // Lilystudent1.saySchool(); // Southeast University
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
// 父类构造函数
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 父类方法
Person.prototype.sayName = function () {
console.log(this.name);
};
// ————–
// 子类构造函数
function Student (name, age, job, school) {
// 继承父类的所有实例属性
Person.call(this, name, age, job);
this.school = school; // 添加新的子类属性
}
// 继承父类的原型方法
Student.prototype = new Person();
// 新增的子类方法
Student.prototype.saySchool = function () {
console.log(this.school);
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var student1 = new Student(‘Lily’, 25, ‘Doctor’, "Southeast University");
console.log(person1.sayName === student1.sayName); // true
person1.sayName(); // Weiwei
student1.sayName(); // Lily
student1.saySchool(); // Southeast University
|
重组集成防止了原型链和借用构造函数的败笔,融合了它们的助益,成为了JavaScript中最常用的继承情势。
而且,instanceof
和isPropertyOf()
也能够用于识别基于组合继承创设的目的。
重组使用原型链和借用构造函数
平时,大家会结合使用原型链继承和借用构造函数来促成一连。也就是说,使用原型链完毕对原型属性和格局的持续,
而通过借用构造函数来促成对实例属性的继续。那样,既通过在原型上定义方法达成了函数复用,又可以确保每个实例都有它自己的特性。
俺们改造最初的例证如下:
JavaScript
// 父类构造函数 function Person (name, age, job) { this.name = name;
this.age = age; this.job = job; } // 父类方法 Person.prototype.sayName =
function () { console.log(this.name); }; // ————– //
子类构造函数 function Student (name, age, job, school) { //
继承父类的所有实例属性(得到父类构造函数中的属性) Person.call(this,
name, age, job); this.school = school; // 添加新的子类属性 } //
继承父类的原型方法(得到父类原型链上的性能和方法) Student.prototype =
new Person(); // 新增的子类方法 Student.prototype.saySchool = function
() { console.log(this.school); }; var person1 = new Person(‘Weiwei’, 27,
‘Student’); var student1 = new Student(‘Lily’, 25, ‘Doctor’, “Southeast
University”); console.log(person1.sayName === student1.sayName); // true
person1.sayName(); // Weiwei student1.sayName(); // Lilystudent1.saySchool(); // Southeast University
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// 父类构造函数
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
// 父类方法
Person.prototype.sayName = function () {
console.log(this.name);
};
// ————–
// 子类构造函数
function Student (name, age, job, school) {
// 继承父类的所有实例属性(获得父类构造函数中的属性)
Person.call(this, name, age, job);
this.school = school; // 添加新的子类属性
}
// 继承父类的原型方法(获得父类原型链上的属性和方法)
Student.prototype = new Person();
// 新增的子类方法
Student.prototype.saySchool = function () {
console.log(this.school);
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var student1 = new Student(‘Lily’, 25, ‘Doctor’, "Southeast University");
console.log(person1.sayName === student1.sayName); // true
person1.sayName(); // Weiwei
student1.sayName(); // Lily
student1.saySchool(); // Southeast University
|
组成集成幸免了原型链和借用构造函数的毛病,融合了它们的优点,成为了JavaScript中最常用的持续格局。
而且,instanceof
和isPropertyOf()
也能够用于识别基于组合继承制造的靶子。
this.name=name;
组成继承的立异版:使用Object.create()
在下面,我们继续父类的原型方法运用的是Student.prototype = new Person()
。
这样做有广大的题材。
革新措施是利用ES5中新增的Object.create()
。可以调用这几个点子来创制一个新目的。新对象的原型就是调用create()
艺术传入的率先个参数:
Student.prototype = Object.create(Person.prototype);
console.log(Student.prototype.constructor); // [Function: Person] //
设置 constructor 属性指向 Student Student.prototype.constructor =
Student;
1
2
3
4
5
6
|
Student.prototype = Object.create(Person.prototype);
console.log(Student.prototype.constructor); // [Function: Person]
// 设置 constructor 属性指向 Student
Student.prototype.constructor = Student;
|
详尽用法可以参考文档。
关于Object.create()
的已毕,大家得以参考一个粗略的polyfill:
function createObject(proto) { function F() { } F.prototype = proto;
return new F(); } // Usage: Student.prototype =
createObject(Person.prototype);
1
2
3
4
5
6
7
8
|
function createObject(proto) {
function F() { }
F.prototype = proto;
return new F();
}
// Usage:
Student.prototype = createObject(Person.prototype);
|
从精神上讲,createObject()
对传播其中的对象进行了一回浅复制。
结合继承的创新版:使用Object.create()
在上头,大家两次三番父类的原型方法运用的是Student.prototype = new Person()
。
这样做有好多的题材。
修正措施是使用ES5中新增的Object.create()
。可以调用这一个措施来创制一个新目的。新对象的原型就是调用create()
方法传入的首先个参数:
JavaScript
Student.prototype = Object.create(Person.prototype);
console.log(Student.prototype.constructor); // [Function: Person] //
设置 constructor 属性指向 Student Student.prototype.constructor =
Student;
1
2
3
4
|
Student.prototype = Object.create(Person.prototype);
console.log(Student.prototype.constructor); // [Function: Person]
// 设置 constructor 属性指向 Student
Student.prototype.constructor = Student;
|
详尽用法可以参考文档。
关于Object.create()
的落到实处,大家得以参照一个简约的polyfill:
JavaScript
function createObject(proto) { function F() { } F.prototype = proto;
return new F(); } // Usage: Student.prototype =
createObject(Person.prototype);
1
2
3
4
5
6
7
|
function createObject(proto) {
function F() { }
F.prototype = proto;
return new F();
}
// Usage:
Student.prototype = createObject(Person.prototype);
|
从实质上讲,createObject()
对传播其中的靶子实施了两次浅复制。
this.age=age;
ES6中的面向对象语法
ES6中引入了一套新的最紧要字用来完成class。
JavaScript照旧是根据原型的,这几个新的基本点字概括class、
constructor、
static、
extends、
和super。
对眼前的代码修改如下:
‘use strict’; class Person { constructor (name, age, job) { this.name =
name; this.age = age; this.job = job; } sayName () {
console.log(this.name); } } class Student extends Person { constructor
(name, age, school) { super(name, age, ‘Student’); this.school = school;
} saySchool () { console.log(this.school); } } var stu1 = new
Student(‘weiwei’, 20, ‘Southeast University’); var stu2 = new
Student(‘lily’, 22, ‘Nanjing University’); stu1.sayName(); // weiwei
stu1.saySchool(); // Southeast University stu2.sayName(); // lily
stu2.saySchool(); // Nanjing University
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
‘use strict’;
class Person {
constructor (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
sayName () {
console.log(this.name);
}
}
class Student extends Person {
constructor (name, age, school) {
super(name, age, ‘Student’);
this.school = school;
}
saySchool () {
console.log(this.school);
}
}
var stu1 = new Student(‘weiwei’, 20, ‘Southeast University’);
var stu2 = new Student(‘lily’, 22, ‘Nanjing University’);
stu1.sayName(); // weiwei
stu1.saySchool(); // Southeast University
stu2.sayName(); // lily
stu2.saySchool(); // Nanjing University
|
ES6中的面向对象语法
ES6中引入了一套新的第一字用来促成class。
但它并不是映入了一种新的面向对象继承方式。JavaScript照旧是依照原型的,那一个新的根本字概括class、
constructor、
static、
extends、
和super。
class
重在字可是是提供了一种在本文中所研商的按照原型方式和构造器情势的面向对象的继续方式的语法糖(syntactic
sugar)。
对眼前的代码修改如下:
JavaScript
‘use strict’; class Person { constructor (name, age, job) { this.name =
name; this.age = age; this.job = job; } sayName () {
console.log(this.name); } } class Student extends Person { constructor
(name, age, school) { super(name, age, ‘Student’); this.school = school;
} saySchool () { console.log(this.school); } } var stu1 = new
Student(‘weiwei’, 20, ‘Southeast University’); var stu2 = new
Student(‘lily’, 22, ‘Nanjing University’); stu1.sayName(); // weiwei
stu1.saySchool(); // Southeast University stu2.sayName(); // lily
stu2.saySchool(); // Nanjing University
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
‘use strict’;
class Person {
constructor (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
sayName () {
console.log(this.name);
}
}
class Student extends Person {
constructor (name, age, school) {
super(name, age, ‘Student’);
this.school = school;
}
saySchool () {
console.log(this.school);
}
}
var stu1 = new Student(‘weiwei’, 20, ‘Southeast University’);
var stu2 = new Student(‘lily’, 22, ‘Nanjing University’);
stu1.sayName(); // weiwei
stu1.saySchool(); // Southeast University
stu2.sayName(); // lily
stu2.saySchool(); // Nanjing University
|
this.job=job;
类:class
是JavaScript中幸存基于原型的持续的语法糖。ES6中的类并不是一种新的成立对象的方法,只不过是一种“特殊的函数”,
因而也席卷类表明式和类声明,
但要求小心的是,与函数表明不一样的是,类阐明不会被提升。
参考链接
类:class
是JavaScript中幸存基于原型的继续的语法糖。ES6中的类并不是一种新的制造对象的办法,只但是是一种“特殊的函数”,
故而也包蕴类表明式和类声明,
但要求注意的是,与函数评释差别的是,类评释不会被提升。
参照链接
this.sayName=function(){
类构造器:constructor
constructor()
方式是有一种相当的和class
一同用于创设和开首化对象的法门。注意,在ES6类中只好有一个称呼为constructor
的方法,
不然会报错。在constructor()
方法中可以调用super
驷不及舌字调用父类构造器。要是你从未点名一个构造器方法,
类会自动使用一个默认的构造器。参照链接
类构造器:constructor
constructor()
主意是有一种极度的和class
一道用于创制和早先化对象的不二法门。注意,在ES6类中只能有一个称呼为constructor
的方法,
不然会报错。在constructor()
措施中得以调用super
爱惜字调用父类构造器。倘若你没有点名一个构造器方法,
类会自动使用一个默许的构造器。参考链接
console.log(this.name);
类的静态方法:static
静态方法就是足以一向利用类名调用的法子,而不用对类举行实例化,当然实例化后的类也无从调用静态方法。
静态方法常被用来创立应用的工具函数。参照链接
类的静态方法:static
静态方法就是可以直接行使类名调用的形式,而不必对类进行实例化,当然实例化后的类也无能为力调用静态方法。
静态方法常被用于创制应用的工具函数。参照链接
};
延续父类:extends
extends
关键字可以用于后续父类。使用extends
能够扩充一个内置的对象(如Date
),也足以是自定义对象,或者是null
。
继续父类:extends
extends
主要字能够用于后续父类。使用extends
可以增添一个松手的目的(如Date
),也可以是自定义对象,或者是null
。
}
关键字:super
super
关键字用于调用父对象上的函数。
super.prop
和super[expr]
表达式在类和目的字面量中的任何主意定义中都有效。
super([arguments]); // 调用父类构造器
super.functionOnParent([arguments]); // 调用父类中的方法
1
2
|
super([arguments]); // 调用父类构造器
super.functionOnParent([arguments]); // 调用父类中的方法
|
假即使在类的构造器中,需要在this
根本字从前使用。参考链接
关键字:super
super
重在字用于调用父对象上的函数。
super.prop
和super[expr]
表达式在类和目的字面量中的任何方法定义中都有效。
JavaScript
super([arguments]); // 调用父类构造器
super.functionOnParent([arguments]); // 调用父类中的方法
1
2
|
super([arguments]); // 调用父类构造器
super.functionOnParent([arguments]); // 调用父类中的方法
|
如假使在类的构造器中,须求在this
驷不及舌字以前使用。参考链接
var person1=new Person();
小结
正文对JavaScript的面向对象机制进行了比较深入的解读,越发是构造函数和原型链格局完成目标的创办、继承、以及实例化。
别的,本文还简要介绍了如在ES6中编辑面向对象代码。
小结
正文对JavaScript的面向对象机制举行了相比较深切的解读,尤其是构造函数和原型链形式达成目的的创设、继承、以及实例化。
除此以外,本文还简要介绍了如在ES6中编辑面向对象代码。
var person2=new Person();
References
- 详解Javascript中的Object对象
new
操作符- JavaScript面向对象简介
- Object.create()
-
延续与原型链
2 赞 7 收藏 4
评论
References
- 详解Javascript中的Object对象
new
操作符- JavaScript面向对象简介
- Object.create()
- 继承与原型链
-
Understanding the prototype property in
JavaScript1 赞 8 收藏
评论
console.log(person1.sayName==person2.sayName)//false
评释不一样实例的同名函数是不对等的
万一大家想要的结果是多头对等,可以如此落成
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=sayName;
}
function sayName(){
console.log(this.name);
}
var person1=new Person();
var person2=new Person();
console.log(person1.sayName==person2.sayName);//true
创立Person的新实例,须要选取new操作符,那种措施下调用构造函数会经历七个等级,分别是:
开创一个新目的
将构造函数的成效域赋值给那些新的目标
施行构造函数中的代码
再次来到新对象
person1和person2那七个对象都有一个constructor属性,该属性指向Person
console.log(person1.constructor==Person);//true
console.log(person2.constructor==Person);//true
原型格局
特点:新对象的那一个属性和艺术是兼备实例共享的
function Person(){
}
Person.prototype.name=”kobe”;
Person.prototype.age=38;
Person.prototype.sayName=function(){
console.log(this.name);
}
var person1=new Person();
var person2=new Person();
console.log(person1.sayName==person2.sayName);//true
偶尔大家想明白该属性到底是存在对象中要么存在原型中,可以动用以下方式
大家运用in操作符和hasOwnProperty结合判断
“name” in object无论该属性到底存在原型中如故对象中,都会再次来到true
而hasOwnProperty唯有存在实例中才回去true
so:唯有in操作符再次回到true,而hasOwnProperty重返false,能确定属性是原型中的属性。
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
原型对象存在难题,牵一发而动全身
function Person(){
}
Perosn.prototype=function(){
constructor;Person,
name:”kobe”,
age:”29″,
job:”player”,
friends:[“shely”,”count”],
sayName:function(){
亚洲必赢官网 , console.log(this.name);
}
};
var person1=new Person();
var person2=new Person();
person1.friends.push(“ann”);
console.log(person1.friends===person2.friends);//true
焚薮而田的主意:是运用构造函数形式和原型格局
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=[“she”,”ya”];
}
Person.prototype={
constructor:Person,
sayName:function(){
console.log(this.name);
}
};
var person1=new Person();
var person2=new Person();
person1.friends.push(“VAN”);
console.log(person1.friends===person2.friends);//false
动态原型情势
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
if(typeof this.sayName!=”function”){
Person.prototype.sayName=function(){
console.log(this.name);
}
};
}
寄生构造函数格局
function Person(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
console.log(this.name);
};
return o;
}
var friend=new Person();//此形式与工厂格局尤其接近
2.js兑现持续的两种办法
原型链继承:原型对象属性共享
function Parent2(){
this.name=”kobe”;
this.play=[1,2,3];
}
function Child2(){
this.type=”children”;
}
Child2.prototype=new Parent2();
var say1=new Child2();
var say2=new Child2();
say1.play.push(“van”);
console.log(say1.play==say2.play);//true
借用构造函数完成持续:不可能落到实处持续原型对象
function Parent1(){
this.name=”kobe”;
}
Parent1.prototype.age=90;
function Child(){
Parent1.call(this);
this.type=”service”;
}
var say=new Child();
console.log();//error
组合式继承
function Parent4(name){
this.name=”kobe”;
this.play=[1,2,3];
}
Parent4.prototype.sayName=function(){
}
function Child4(name,age){
Parent3.call(this,name);
this.age=age;
}
Child4.prototype=new Parent4();
Child4.prototype.constructor=Child4;
Child4.prototype.sayAge=function(){
console.log(this.age);
};
var ins1=new Child4();
var ins2=new Child4();
ins1.push.push(4);
console.log(ins1.play==ins2.play);//false
原型式继承
function object(){
function F(){}
F.prototype=o;
return new F();
}
var person={
name:”kobe”,
friends;[“yang”,”du”,”geng”]
};
var onePerson=object(person);
var twoPerson=object(person);
寄生式继承
function object(o){
function F(){}
F.prototype=o;
return new F();
}
function create(o){
var clone=object(o);
clone.sayHi=function(){
console.log(“hi”);
};
return clone;
}
var person={
name:”kobe”,
friends:[“james”,”waston”,”sun”]
};
var anotherPerson=creat(person);
anotherPerson.sayHi();//hi
寄生式组合继承
function inheritPrototype(Child5,Parent5){
var prototype=Object(Parent5.prototype);
prototype.constructor=Child5;
Child5.prototype=prototype;
}
function Parent5(name){
this.name=name;
this.colors=[“red”,”blue”,”green”];
}
Parent5.prototype.sayName=function(){
console.log(this.name);
};
function Child5(name,age){
Parent5.call(this.name);
this.age=age;
}
inheritPrototype(Child5,Parent5);
Child5.prototype.sayAge=function(){
console.log(this.age);
};