长远解读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中,大家得以看重深切解读JavaScript面向对象编程实践,中的面向对象编程。原型对象来缓解这一个题材。
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(‘莉莉’, 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下边,那样就足以幸免属性和措施的再度定义。下边用一个例子和图来说惠氏(WYETH)下。
//这里我们使用原型来创建对象,所有对象共用的属性和方法就放在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
操作符删除实例上的性质。
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(); // 莉莉student1.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(‘莉莉’, 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);
};