一、理解对象
二、创建对象
     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高级程序设计》第六章【面向对象的程序设计】 包括对象、创建对象、继承的更多相关文章

  1. JavaScript高级程序设计-第六章面向对象的程序设计

    创建对象主要的两种形式,创建Object实例和创建对象字面量 对象包含属性和方法 数据 .属性有四个特性,特性是为了描述属性行为的,他们是: Configurable(可配置的)是否能删除或是否能修改 ...

  2. JAVASCRIPT高程笔记-------第六章 面向对象的程序设计

    理解对象的概念  js中的对象与其他 编程语言中的类不一样  ECMAscript 没有类的概念      ECMA-262 把对象定义为 “无序属性的集合,其属性可以包含基本值,对象或者函数”   ...

  3. 读书笔记 - js高级程序设计 - 第六章 面向对象的程序设计

      EcmaScript有两种属性 数据属性 和 访问器属性 数据属性有4个特性 Configurable Enumerable Writable Value   前三个值的默认值都为false   ...

  4. JavaScript高级程序设计 第六章 面向对象程序设计

    面向对象程序设计 ECMA-262将对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性和方法都有一个名字,而每个名字都 ...

  5. javascript高级程序设计第3版——第6章 面向对象的程序设计

    第六章——面向对象的程序设计 这一章主要讲述了:面向对象的语言由于没有类/接口情况下工作的几种模式以及面向对象语言的继承: 模式:工厂模式,构造函数模式,原型模式 继承:原型式继承,寄生式继承,以及寄 ...

  6. Python第六章 面向对象

    第六章 面向对象 1.面向对象初了解 ​ 面向对象的优点: ​ 1.对相似功能的函数,同一个业务下的函数进行归类,分类 ​ 2.类是一个公共的模板,对象就是从具体的模板中实例化出来的,得到对象就得到一 ...

  7. js高级程序设计(六)面向对象

    ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性或方法都有一个名字,而每个名字都映射到一个值.正 ...

  8. 精读《javascript高级程序设计》笔记三——面向对象的程序设计

    重点来了,我认为这一章值得好好地反复地看.看第一遍 还是懵懵懂懂,现在看第二遍,终于能看出点意思了. 创建对象 工厂模式 function createPerson(name, age, job){ ...

  9. 【笔记】javascript权威指南-第六章-对象

    对象 //本书是指:javascript权威指南    //以下内容摘记时间为:2013.7.28 对象的定义: 1.对象是一种复合值:将很多值(原始值或者对象)聚合在一起,可以通过名字访问这些值. ...

  10. JavaScript高级程序设计学习笔记第六章--面向对象程序设计

    1.ECMAScript没有类的概念,ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”,有点类似于散列表 2.ECMAScript 中有两种属性:数据属性和访问 ...

随机推荐

  1. 实验1:c++简单程序设计(1)

    //文中有格式错误请无视 //这个编辑器一言难尽 实验目的 1. 掌握c++中类c部分的编程知识: 数据类型,常量,变量,运算符,表达式,分支结构,循环结构 2. 掌握C++中数据输入和输出的基本方法 ...

  2. linux下安装memcached以及扩展(xampp环境)

    网上有很多相关的文章,就不具体写了.(假设这里文件都上传到更目录下的tmp文件夹下) 1.大致流程先装 libevent 和 memcache http://www.cnblogs.com/zgx/a ...

  3. mRemoteNG

    mRemoteNG 1.摆脱了mstsc那种一个程序一个界面的模式,采用了左边树+右边Tab页的显示形式,让你在一个mRemote界面中,可以连接多个远程桌面,再也不用为切来切去而烦恼了(如上图). ...

  4. TPM、read counts、RPKM/FPKM你选对了吗?

    TPM.read counts.RPKM/FPKM你选对了吗? 已有 3940 次阅读 2017-12-15 15:04 |个人分类:RNA-seq|系统分类:科普集锦|关键词:RNA-seq| RN ...

  5. vue cli+axios踩坑记录+拦截器使用,代理跨域proxy(更新)

    16319 1.首先axios不支持vue.use()方式声明使用,看了所有近乎相同的axios文档都没有提到这一点建议方式 在main.js中如下声明使用 import axios from 'ax ...

  6. LINUX网络编程 IO 复用

    参考<linux高性能服务器编程> LINUX下处理多个连接时候,仅仅使用多线程和原始socket函数,效率十分低下 于是就出现了selelct poll  epoll等IO复用函数. 这 ...

  7. springmvc 返回汉字乱码

    1.删除配置文件中的<mvc:annotation-driven  /> 2.添加如下配置 <bean class="org.springframework.web.ser ...

  8. Tomcat中的Web.xml和servlet.xml的学习

    Web.xml文件使用总结 作用: 存储项目相关的配置信息,保护servlet.解耦一些数据对程序的依赖 使用位置: 每个web项目中 Tomcat服务器中(在服务器目录conf目录中) 区别: We ...

  9. 2018.09.22 atcoder Snuke's Coloring 2(线段树+单调栈)

    传送门 就是给出一个矩形,上面有一些点,让你找出一个周长最大的矩形,满足没有一个点在矩形中. 这个题很有意思. 考虑到答案一定会穿过中线. 于是我们可以把点分到中线两边. 先想想暴力如何解决. 显然就 ...

  10. spark 与 hbase-server 集成版本问题

    今天在使用spark存储hbase的时候遇到异常Exception in thread "main" java.lang.NoSuchMethodError: io.netty.b ...