6面向对象的程序设计

ECMA_262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。

6.1理解对象

特性(attribute)是内部值,描述了属性(property)的各种特性。ECMAScript中有两种属性:数据属性和访问器属性。数据属性包含一个数据值的位置,在这个位置可以读取和写入值,数据属性有4个描述其行为的特性,[[Configurable]],[[Enumerable]],[[Writable]],[[Value]].要改变属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法,这个方法接收3个参数:属性所在的对象、属性的名字和一个描述符对象,一旦把属性定义为不可配置的,就不能再把它变回可配置了。访问器属性不包含数据值,它们包含一对getter和setter函数,访问器属性有4个特性:[[Configurable]],[[Enumerable]],[[Get]],[[Set]].

Object.defineProperties()方法可以一次定义多个属性。

Object.getOwnProperyDescriptor()方法,可以取得给定属性的描述符,这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。

6.2创建对象

工厂模式用函数来封装以特定接口创建对象的细节。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

使用构造函数模式可以定义自定义对象类型的属性和方法。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,而这正是构造函数模式胜过工厂模式的地方。构造函数与其他函数的唯一区别,就在于调用它们的方式不同,任何函数,只要通过new操作符来调用,那它就可以作为构造函数。使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。连接存在于实例和构造函数的原型对象之间,而不是存在于实例和构造函数之间。Object.getPrototypeOf()方法返回[[Prototype]]的值。可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值,当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,使用delete操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。使用hasOwnProperty()方法可以检测一个属性是否存在于实例中,还是存在于原型中(在对象实例中,才会返回true)。有两种方式使用in操作符:单独使用和在for-in循环中使用。单独使用时,in操作符会通过对象能够访问给定属性是返回true,不论该属性存在于实例中还是原型中。在使用for-in循环时,返回的是所有能够通过对象访问、可枚举的(enumerated)属性,其中即包括存在于实例中的属性,也包括存在于原型中的属性。Object.keys()方法可以取得对象上所有可枚举的实例属性。Object.getOwnPropertyNames()方法可以得到所有实例属性,无论是否可枚举。用对象字面量来重写原型对象,constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系。原型模式省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都取得相同的属性值,原型模型最大问题是由其共享的本性多导致的。

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

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

寄生构造函数模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后在返回新创建的对象。返回的对象与构造函数或者与构造函数的原型属性之间没有关系。

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境,或者在防止数据被其他应用程序改动时使用。

6.3继承

ECMAScript中描述了原型链概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。使用instanceof操作符和isPrototypeOf()方法可以确定原型和实例之间的关系,只要是实例和原型链中出现过的构造函数,结果就会返回true。再通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做就会重写原型链。原型链最主要的问题来自包含引用类型值的原型,第二个问题是在创建子类型的实例时,不能向超类型的构造函数中传递函数,实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

借用构造函数的基本思想是在子类型构造函数的内部调用超类型构造函数。相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数。如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了。

组合继承值得是将原型链和借用构造函数的技术组合到一起,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承,这样,既通过在原型上定义方法实现了函数复用,有能保证每个实例都有它自己的属性。

原型式继承要求必须有一个对象可以作为另外一个对象的基础。如果有这么一个对象的话,可以把它传递给object()函数,然后在根据具体需求对得到的对象加以修改即可。Object.create()方法规范化了原型式继承,这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。包含引用类型值的属性时钟都会共享相应的值,就像使用原型模式一样。

寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。在主要考虑对象而不是自定义类型和构造函数的情况下,寄生继承也是一种有用的模式。

原文地址:https://www.cnblogs.com/dingzibetter/p/6296908.html