javascript高级程序设计之面向对象的程序设计

1、概述

对象定义为无序属性的集合,其属性可以包含基本值、对象或者函数。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。可以把ECMAScript的对象想象成散列表,无非就是一组键值对,其中值可以是数据或函数。

2、理解对象

2.1 属性类型

2.1.1 数据属性

[[configuable]] 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否所属性修改为访问器属性。
[[enumerable]] 表示能否通过for-in循环返回属性。
[[writable]] 表示能否修改属性的值
[[Value]] 包含这个属性的数据值。读取属性值的时候,从这个位置读,写入属性值的时候,把新值保存在这个位置

要修改属性默认的特性,必须使用Object.defineProperty()方法,接收三个参数:属性所在的对象、属性的名字和一个描述符对象。

2.1.2 访问器属性

访问器属性不包含数据值,它们包含一对getter和setter函数。访问器属性不能直接定义,必须使用Object.defineProperty()来定义。访问器属性有以下特性

[[configuable]] 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否所属性修改为访问器属性。
[[enumerable]] 表示能否通过for-in循环返回属性。
[[Get]] 在读取属性时调用的函数,默认值为undefined
[[Set]] 在写入属性时调用的函数,默认值为undefined

2.2 定义多个属性

使用Object.defineProperties()方法。

接收两个参数,第一个是要添加或修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。

2.3 读取属性的特性

使用Object.getOwnPropertyDescriptor()方法。

接收两个参数,属性所在的对象和要读取其描述符的属性名称。返回值是一个对象。如果是访问器属性,这个对象的属性有configurable、enumberable、get和set;如果是数据属性,这个对象的属性有configurable、enumerable、writable和value

3、创建对象

3.1 工厂模式

这种模式抽象了创建具体对象的过程,使用函数来封装以特定接口创建对象的细节。但却没有解决对象识别的问题。

3.2 构造函数模式

函数名首字母大写。创建对象是使用new操作符。

对象的constructor属性用来标识对象类型。主要问题就是每个方法都要在每个实例上重新创建一遍。

3.3 原型模式

创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

3.3.1 理解原型对象

无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性是一个指向prototype所在函数的指针。创建了自定义的构造函数后,其原型对象默认只会取得constructor属性;至于其它方法,则都是从Object继承而来的。

可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。获取原型对象使用Object.getPrototypeOf()

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该 属性的值。

当为对象实例添加一个属性时,该 属性就会屏蔽原型对象中保存的同名属性。

使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。

3.3.2 原型与in操作符

在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

使用Object.keys(obj)方法来取得对象上所有可枚举的实例属性。

使用Object.getOwnPropertyNames(obj)来获得所有实例属性,无论它是否可枚举。

3.3.3 更简单的原型语法 

用一个包含所有属性和方法的对象字面量来重写整个原型对象。

3.3.4 原型的动态性

对原型对象所做的任何修改都能够立即从实例中反映出来。

3.3.5 原生对象的原型

通过原生对象的原型,不仅可以取得所有默认方法的引用,还可以定义新方法。可以修改自定义对象的原型一样修改原生对象的原型。

3.3.6 原生对象的问题

原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。但是对于包含引用类型值的属性来说,问题就比较突出。

3.4 组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。同时解决使用原型模式时存在的问题

3.5 动态原型模式

把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要 初始化原型。

3.6 寄生构造函数模式

基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

首先,返回的对象与构造函数或者构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。

3.7 稳妥构造函数模式

稳妥对象指的是没有公共属性,而且其方法也不引用this对象。稳妥构造函数遵循与寄生构造函数类似的模式,有两点不同:一是新创建对象的实例方法不引用this,二是不使用new操作符调用 构造函数。

4、继承

支持实现继承,主要是依靠原型链来实现

4.1 原型链

基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数 指针 ,而实例都包含一个指向原型对象的内部指针。

4.2 借用构造函数

在解决原型中包含引用类型值所带来问题的过程中,可以使用一种叫做借用构造函数的技术。在子类型构造函数的内部调用超类型构造函数。

问题就是函数无法复用。

4.3 组合继承

使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现函数复用,又能够保证每个实例都有它自己的属性。

4.4 原型式继承

要求必须有一个对象可以作为另一个对象的基础。

Object.create()方法规范化了原型式继承,接收两个参数:一个用作新对象原型的对象和(可选 的)一个为新对象定义额外属性的对象。

4.5 寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象。

4.6 寄生组合式继承

通过使用构造函数来继承属性,通过原型链的混合形式来继承方法。

猜你喜欢

转载自blog.csdn.net/wuli2496/article/details/115052074