《JavaScript高级程序设计》第六章【面向对象的程序设计】 包括对象、创建对象、继承
- 一、理解对象
- 二、创建对象
- 1. 工厂模式
- 2. 构造函数模式
- 3. 原型模式
- 4. 组合使用构造函数模式和原型模式【使用最广泛】
- 5. 动态原型模式
- 6. 寄生构造函数模式
- 7. 稳妥构造函数模式
- 三、继承
- 1. 原型链
- 2. 借用构造函数
- 3. 组合继承【最常用】
- 4. 原型式继承
- 5. 寄生式继承
- 6. 寄生组合式继承
一、理解对象
ECMAScript中有两种属性:数据属性和访问器属性。
二、创建对象
1. 工厂模式
使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这种方法后来被构造函数模式所取代。
2. 构造函数模式
可以创建自定义引用类型,可以像创建内置对象实例一样使用new操作符。但是它的每个成员都无法得到复用,包括函数。
但是这样说好像也不准确——如果是通过一个指针指向构造函数外部的函数的话,应该算是复用?
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = sayName; //一个指向函数的指针,所以所有的实例共享同一函数
this.sayAge = function(){
console.log(this.age);
}
}
function sayName(){
console.log(this.name);
}
var person1 = new Person('Jack');
var person2 = new Person('Amy');
person1.sayName();
person2.sayName();
console.log(person1.sayName === person2.sayName); //true
console.log(person1.sayAge == person2.sayAge); //false
但是这种方法:1)sayName函数在全局作用域中定义,但实际只被某个对象调用,名不副实
2)没有封装性
3. 原型模式
使用构造函数的prototype属性来指定那些应该共享的属性和方法。
4. 组合使用构造函数模式和原型模式【使用最广泛】
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的熟悉。且这种模式还支持构造函数传递参数
function Person(name){
this.name = name;
}
Person.prototype = {
constructor : Person, //因为这里通过对象字面量重写了整个原型对象,但constructor会指向Object。所以要特意设置
sayName : function(){
console.log(this.name);
}
}
var person1 = new Person('Nick');
var person2 = new Person('Amy');
console.log(person1.sayName == person2.sayName); //true
5. 动态原型模式
function Person(name){
this.name = name;
if(typeof this.sayName != "function"){ //这段代码只会在在初次调用构造函数时才会执行
Person.prototype.sayName = function(){
console.log(this.name);
}
}
}
var person = new Person('Jack');
可以使用instanceof操作符来确定实例类型
注意:使用动态函数模型时,不能使用对象字面量重写原型。
在已经创建了实例的情况下重写原型,就会切断现有实例和新原型之间的关系。
举个栗子:
function Person(name){
this.name = name;
if(typeof this.sayName != "function"){ //这段代码只会在在初次调用构造函数时才会执行
Person.prototype.sayName = function(){
console.log(this.name);
}
}
}
var person = new Person('Jack');
Person.prototype = {
sayHi : function(){
console.log("hi");
}
}
person.sayName(); //Jack
//person.sayHi(); //Uncaught TypeError: person.sayHi is not a function var person2 = new Person('Amy');
person2.sayHi(); //hi
person2.sayName(); //Amy
打印出person和person2:
这里我的理解是:调用构造函数会为实例增加一个指向最初原型的[[prototype]]指针,而把原型修改为另一个对象,则会切断构造函数与最初原型之间的关系。因为实例中的指针只会指向原型,而不指向构造函数,此时修改的是构造函数的原型,而之前创建的实例仍然指向之前的原型对象。故其仍然用于sayName()函数。然而,为什么后来创建的实例person2也会拥有sayName()函数呢?不是之前的构建已经切断了吗?这里我猜测原因是因为在创建实例person2时,if逻辑检测到Person中没有sayName()函数,于是又增加了这样一个函数。为了验证猜想,我在if逻辑里打印一句话,这样只要进入循环就会打印出来这句话,果然打印了两次,猜想得证。
6. 寄生构造函数模式
基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
常用于为对象创建构造函数。
一个栗子:创建一个具有额外方法的特殊数组
function SpecialArray(){
//创建数组
var values = new Array(); //添加值
values.push.apply(values, arguments); //这里比较疑惑的是为什么要用apply,既然values是一个数组,那么直接调用push不可以吗?
//values.push(arguments); //[object Arguments] //添加方法
values.toPipedString = function(){
return this.join("|");
}; //返回数组
return values;
}
var color = new SpecialArray("red", "blue", "green");
console.log(color.toPipedString()); //red|blue|green
上述代码中遇到了一个问题:如第6.7行所示,为什么要用apply而不能直接使values.push(arguments)呢?
——换成直接使用push后输出是[object Arguments]。然后查了下发现arguments果然是一个对象,那为什么apply中可以直接用?猜想应当是apply内部实现对arguments进行了解析。
arguments并不是一个数组,而是一个伪数组,具有length属性. 这里也可以直接用[].push来代替.
关于寄生构造函数模式,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没什么不同。为此,不能依赖instanceof操作符来确定对象类型。
所以,可以用其他,不要用这个方法。
7. 稳妥构造函数模式
稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象适用在一些安全的环境中(禁止this和now),或在防止数据被其他应用程序改动时使用。
function Person(name){
var o = new Object(); //在这里定义私有变量和函数 o.sayName = function(){
console.log(name);
}; return o;
}
var person = Person("nick");
person.sayName(); //nick
这种方法与上一个方法的区别在于:新创建对象的实例方法不引用this,不使用new操作符调用构造函数。
这种方法除了调用sayName()函数外,没有别的办法可以访问到其数据成员,所以具有安全性。
instanceof操作符对这种方法也无效。
三、继承
两种继承方式:接口继承和实现继承。ECMAScript只支持实现继承,且其实现继承主要是依靠原型链来实现的。
1. 原型链
原型链实现继承的基本思想:用原型链让一个引用类型继承另一个引用类型的属性和方法。
构造函数,原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,如果让原型对象等于另一个对象的实例,则原型对象将包含一个指向另一个原型的指针,另一个原型中也包含一个指向另一个构造函数的指针。(这都是什么鬼!)
实现原型链模式:
function Parent(){
this.name = "parent";
}
Parent.prototype.getName = function(){
return this.name;
}
function Child(){
this.subName = 'child';
}
//继承了Parent
Child.prototype = new Parent();
Child.prototype.getSubName = function(){
return this.subName;
};
var child = new Child();
console.log(child.getName()); //parent
console.log(child.getSubName()); //child //两种确定原型和实例的关系的方法
//1)instanceof
console.log(child instanceof Object); //true
console.log(child instanceof Parent); //true
console.log(child instanceof Child); //true //isPrototypeOf()
console.log(Object.prototype.isPrototypeOf(child)); //true
console.log(Parent.prototype.isPrototypeOf(child)); //true
console.log(Child.prototype.isPrototypeOf(child)); //true
子类型有时候需要重写超类中的某个方法,或者需要添加超类中不存在的某个方法,给原型添加方法的代码一定要放到替换原型的语句之后。否则,子类的方法会被覆盖。因为原型指针指向了父类的原型。
function Parent(name){
this.name = name;
}
Parent.prototype.sayName = function(){
console.log("Parent: my name is " + this.name);
}
function Child(name,age){
this.name = name;
}
Child.prototype.sayName = function(){
console.log("Child: my name is "+ this.name);
};
Child.prototype = new Parent();
var child = new Child('Amy'); child.sayName(); //Parent: my name is Amy
原型链继承的问题:原先的实例属性也会变成原型属性。且不能向构造函数传递参数
2. 借用构造函数
apply()和call()
3. 组合继承【最常用】
使用原型链实现对原型属性和方法的继承,而通过构造函数实现对实例属性的继承。
instanceof和isPrototype()也能够识别基于组合继承创建的对象
function Parent(name){
this.name = name;
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
function Child(name,age){
Parent.call(this,name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.sayAge = function(){
console.log(this.age);
}
var child = new Child('Jack',27);
child.sayName(); //Jack
child.sayAge(); //
4. 原型式继承
思想:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。如下:
function object(o){
function F(){} //创建一个临时的构造函数
F.prototype = o; //将传入的对象作为这个构造函数的原型
return new F(); //返回这个临时类型的一个实例
}
这种方法要求必须有一个对象可以作为另一个对象的基础。
ECMAScript5中新增了一个函数实现了原型式继承:Object.create()。
Object.create()函数接收两个参数:一个用于新对象原型,一个为新对象定义额外属性的对象。
这种方法也可能会使引用类型值的属性共享
var Person = {
name: "Nick",
friends: [1,2,3,4]
};
var anotherPerson = Object.create(Person);
anotherPerson.name = "Joe";
anotherPerson.friends.push(5); var yetAnotherPerson = Object.create(Person);
yetAnotherPerson.name = "Amy";
yetAnotherPerson.friends.push(6); console.log(Person.friends); //[1,2,3,4,5,6] var person2 = Object.create(Person, {
name: {
value: "Lee"
}
});
console.log(Person.name); //Nick
console.log(person2.name); //Lee
console.log(person2); //name:Nick在其原型之中。原型链会先找实例属性
5. 寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,然后返回对象
function createAnothor(o){
var clone = Object.create(o);
clone.sayHi = function(){
console.log("hi");
};
return clone;
}
var person = {
name : "Nick",
}
var anotherPerson = createAnothor(person);
anotherPerson.sayHi();
这种模式用于主要考虑对象不是自定义类型和构造函数的情况下。
这种方法来为对象添加函数,也不能做到函数复用。
6. 寄生组合式继承
之前提到的组合函数的不足在于:无论什么情况下,都会调用两次超类型构造函数。一次是在创建子类原型的时候,一次是在子类构造函数内部。
所谓寄生式组合继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
思路:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(Child, Parent){
var prototype = Object.create(Parent.prototype); //创建对象
prototype.constructor = Child; //增强对象
Child.prototype = prototype; //指定对象
}
function Parent(name){
this.name = name;
}
Parent.prototype.sayName = function(){
console.log(this.name);
};
function Child(name,age){
Parent.call(this,name);
this.age = age;
}
inheritPrototype(Child,Parent);
Child.prototype.sayAge = function(){
console.log(this.age);
}
var child = new Child('Jack',29);
child.sayName();
child.sayAge();
console.log(child); //这时,child的__proto__中就不会有从父类继承来的name和age属性了
用寄生组合式继承打印出来的child:
很干净。而如果用组合继承的话:
可以看到其原型中有一个多余的name属性。
《JavaScript高级程序设计》第六章【面向对象的程序设计】 包括对象、创建对象、继承的更多相关文章
- JavaScript高级程序设计-第六章面向对象的程序设计
创建对象主要的两种形式,创建Object实例和创建对象字面量 对象包含属性和方法 数据 .属性有四个特性,特性是为了描述属性行为的,他们是: Configurable(可配置的)是否能删除或是否能修改 ...
- JAVASCRIPT高程笔记-------第六章 面向对象的程序设计
理解对象的概念 js中的对象与其他 编程语言中的类不一样 ECMAscript 没有类的概念 ECMA-262 把对象定义为 “无序属性的集合,其属性可以包含基本值,对象或者函数” ...
- 读书笔记 - js高级程序设计 - 第六章 面向对象的程序设计
EcmaScript有两种属性 数据属性 和 访问器属性 数据属性有4个特性 Configurable Enumerable Writable Value 前三个值的默认值都为false ...
- JavaScript高级程序设计 第六章 面向对象程序设计
面向对象程序设计 ECMA-262将对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性和方法都有一个名字,而每个名字都 ...
- javascript高级程序设计第3版——第6章 面向对象的程序设计
第六章——面向对象的程序设计 这一章主要讲述了:面向对象的语言由于没有类/接口情况下工作的几种模式以及面向对象语言的继承: 模式:工厂模式,构造函数模式,原型模式 继承:原型式继承,寄生式继承,以及寄 ...
- Python第六章 面向对象
第六章 面向对象 1.面向对象初了解 面向对象的优点: 1.对相似功能的函数,同一个业务下的函数进行归类,分类 2.类是一个公共的模板,对象就是从具体的模板中实例化出来的,得到对象就得到一 ...
- js高级程序设计(六)面向对象
ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性或方法都有一个名字,而每个名字都映射到一个值.正 ...
- 精读《javascript高级程序设计》笔记三——面向对象的程序设计
重点来了,我认为这一章值得好好地反复地看.看第一遍 还是懵懵懂懂,现在看第二遍,终于能看出点意思了. 创建对象 工厂模式 function createPerson(name, age, job){ ...
- 【笔记】javascript权威指南-第六章-对象
对象 //本书是指:javascript权威指南 //以下内容摘记时间为:2013.7.28 对象的定义: 1.对象是一种复合值:将很多值(原始值或者对象)聚合在一起,可以通过名字访问这些值. ...
- JavaScript高级程序设计学习笔记第六章--面向对象程序设计
1.ECMAScript没有类的概念,ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”,有点类似于散列表 2.ECMAScript 中有两种属性:数据属性和访问 ...
随机推荐
- 实验1:c++简单程序设计(1)
//文中有格式错误请无视 //这个编辑器一言难尽 实验目的 1. 掌握c++中类c部分的编程知识: 数据类型,常量,变量,运算符,表达式,分支结构,循环结构 2. 掌握C++中数据输入和输出的基本方法 ...
- linux下安装memcached以及扩展(xampp环境)
网上有很多相关的文章,就不具体写了.(假设这里文件都上传到更目录下的tmp文件夹下) 1.大致流程先装 libevent 和 memcache http://www.cnblogs.com/zgx/a ...
- mRemoteNG
mRemoteNG 1.摆脱了mstsc那种一个程序一个界面的模式,采用了左边树+右边Tab页的显示形式,让你在一个mRemote界面中,可以连接多个远程桌面,再也不用为切来切去而烦恼了(如上图). ...
- TPM、read counts、RPKM/FPKM你选对了吗?
TPM.read counts.RPKM/FPKM你选对了吗? 已有 3940 次阅读 2017-12-15 15:04 |个人分类:RNA-seq|系统分类:科普集锦|关键词:RNA-seq| RN ...
- vue cli+axios踩坑记录+拦截器使用,代理跨域proxy(更新)
16319 1.首先axios不支持vue.use()方式声明使用,看了所有近乎相同的axios文档都没有提到这一点建议方式 在main.js中如下声明使用 import axios from 'ax ...
- LINUX网络编程 IO 复用
参考<linux高性能服务器编程> LINUX下处理多个连接时候,仅仅使用多线程和原始socket函数,效率十分低下 于是就出现了selelct poll epoll等IO复用函数. 这 ...
- springmvc 返回汉字乱码
1.删除配置文件中的<mvc:annotation-driven /> 2.添加如下配置 <bean class="org.springframework.web.ser ...
- Tomcat中的Web.xml和servlet.xml的学习
Web.xml文件使用总结 作用: 存储项目相关的配置信息,保护servlet.解耦一些数据对程序的依赖 使用位置: 每个web项目中 Tomcat服务器中(在服务器目录conf目录中) 区别: We ...
- 2018.09.22 atcoder Snuke's Coloring 2(线段树+单调栈)
传送门 就是给出一个矩形,上面有一些点,让你找出一个周长最大的矩形,满足没有一个点在矩形中. 这个题很有意思. 考虑到答案一定会穿过中线. 于是我们可以把点分到中线两边. 先想想暴力如何解决. 显然就 ...
- spark 与 hbase-server 集成版本问题
今天在使用spark存储hbase的时候遇到异常Exception in thread "main" java.lang.NoSuchMethodError: io.netty.b ...