一、理解对象
二、创建对象
     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. python之三级菜单作业

    作业需求如下 1.根据用户的输入打印相应的省.市.县的信息 2.每次只要用户输入b,则返回上一级菜单 3.每次只要用户输入q,则直接退出 4.用户输错需要有提示 homework_dict = {'内 ...

  2. 安装Python3后,centos使用yum报错

    题记 在之前的文章中我自定义安装了Python3,并且修改了默认的 Python软链,今天想搭建一个 ftp 服务器,使用命令的时候出现了一个错误: 问题 1.使用 yum 安装 ftp工具 yum ...

  3. php Pthread 多线程 (五) 线程同步

    有些时候我们不希望线程调用start()后就立刻执行,在处理完我们的业务逻辑后在需要的时候让线程执行. <?php class Sync extends Thread { private $na ...

  4. Spring框架的AOP技术(注解方式)

    1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包 * 先引入Spring框架开发的基本开发包 * 再引入Spring框架的AOP的开发包 * spring的传统AOP的开发的包 * sp ...

  5. 开发中经典sql总结

    1.说明:显示文章.提交人和最后回复时间 select a.title,a.username,b.adddate ,(select max(adddate) from table where tabl ...

  6. 现代编译原理--第二章(语法分析之LR(1))

    (转载请表明出处  http://www.cnblogs.com/BlackWalnut/p/4472772.html) 前面已经介绍过LL(1),以及如何使用LL(1)文法.但是LL(K)文法要求在 ...

  7. Nginx详解(正向代理、反向代理、负载均衡原理)

    Nginx配置详解 nginx概述 nginx是一款自由的.开源的.高性能的HTTP服务器和反向代理服务器:同时也是一个IMAP.POP3.SMTP代理服务器:nginx可以作为一个HTTP服务器进行 ...

  8. Ubuntu 配置网卡信息

    关于图形界面的配置,我这里就不多介绍了,这个很简单.这里介绍的是如何通过修改配置文件来实现虚拟网卡. 首先介绍ubuntu(我这里使用的是ubuntu-16.04)下虚拟网卡的配置 1.先用ifcon ...

  9. [Selenium] CSS3 选择器

    在 CSS 中,选择器是一种模式,用于选择需要添加样式的元素. "CSS" 列指示该属性是在哪个 CSS 版本中定义的.(CSS1.CSS2 还是 CSS3.) 选择器 例子 例子 ...

  10. android触控,先了解MotionEvent(一)

    http://my.oschina.net/banxi/blog/56421 这是我个人的看法,要学好android触控,了解MotionEvent是必要,对所用的MotionEvent常用的API要 ...