第6章 面向对象的程序设计
理解对象:
ECMA-262把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。”
创建自定义对象的最简单方式就是创建一个Object的实例,然后再为它添加属性和方法。几年后,对象字面量成为创建这种对象的首选模式。
属性类型:数据属性和访问器属性。
要修改属性默认的特性,必须使用ECMAScript 5的Object.defineProperty()方法。
访问器属性不包含数据值,它们包含一对getter和setter函数。使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。
ECMAScript 5又定义了一个Object.defineProperties()方法,利用这个方法可以通过描述符一次性定义多个属性。
读取属性特性:使用ECMAScript 5的Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。
创建对象:
对象的constructor属性最初是用来标识对象类型的。但是,提到检测对象类型,还是instanceof操作符要更靠谱一些。
构造函数与其他函数的唯一区别,就在于调用它们的方式不同。任何函数,只要通过new操作符来调用,那它就可以作为构造函数。
不通过new操作符调用构造函数Person()会出现什么结果:属性和方法都被添加给了window对象了。
构造函数问题:使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。通过把函数定义转移到构造函数外部来解决这个问题。但又引起了一个新问题,就是没有了封装性可言,可通过原型模式来解决。
原型模式:我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,即原型对象。使用原型对象的好处是可以让所有对象实例共享它包含的属性和方法。
理解原型对象:
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型对象。ECMAScript 5中管这个指针叫[[prototype]]。虽然在脚本中没有标准的方式访问[[prototype]],但Firefox、Chrome在每个对象都支持一个__proto__属性。
虽然在所有实现中都无法访问到[[prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。
ECMAScript 5增加了一个新方法,叫Object.getPrototypeOf(),这个方法返回[[Prototype]]的值。
使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。
在使用in操作符、for-in时,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。
默认不可枚举的所有属性和方法,包括:hasOwnProperty()、propertyIsEnumerable()、toLocaleString()、toString()和valueOf()、constructor、prototype。
要取得对象上所有可枚举的实例属性,可以使用ECMAScript 5的Object.keys()方法。
如果你想要得到所有实例属性,无论是否可枚举,都可以使用Object.getOwnPropertyNames()。
重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,它们引用的仍然是最初的原型。
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
继承:
许多OO语言都支持两种继承方式:接口继承和实现继承。ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。
构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
给原型添加方法的代码一定要放在替换原型的语句之后。
原型链问题:
(1)最主要的问题来自包含引用类型值的原型,这类原型属性会被所有实例共享。这也是为什么要在构造函数中,而不是原型对象中定义属性的原因。
(2)第二个问题是在创建子类型的实例时,不能向超类型的构造函数中传递参数。
在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数的技术,即在子类型构造函数的内部调用超类型的构造函数。
小结:
在没有类的情况下,可以采用下列模式创建对象:
(1)工厂模式,使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这个模式后来被构造函数模式所取代。
(2)构造函数模式,可以创建自定义引用类型,使用new操作符。不过构造函数也有缺点,即它的每个成员都无法得到复用,包括函数。
(3)原型模式,使用构造函数的prototype属性来指定那些应该共享的属性和方法。
JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数内部调用超类型构造函数。
【手动广告】金句猫www.jinjumao.club