JavaScript第六章——面向对象
一、理解对象
1.创建对象
使用new操作符和使用对象字面量两种方法
2.属性类型
特性是内部值,把它放在两对方括号“[[Enumerable]]”
数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。
定义了属性(赋值后确定属性)之后只能通过Object.defineProperty()方法修改属性默认得特性
该方法接受三个参数:属性所在的对象、属性的名字和一个描述符对象
描述符(descriptor)对象的属性一定是configurable、enumerable、writable 和 value(可修改多个特性值)
特性值如图进行设定
该特性在严格模式下进行修改属性的值会抛出错误
IE8是第一个实现 Object.defineProperty()方法的浏览器版本。然而,这个版本的实现存在诸多限制:只能在 DOM对象上使用这个方法,而且只能创建访问器 属性。由于实现不彻底,建议读者不要在 IE8 中使用 Object.defineProperty() 方法。
访问器属性
访问器不含数据值,包含一堆getter和setter函数
getter返回有效值,setter传入新值
不能直接定义,必须使用Object.defineProperty来定义
严格模式下只指定setter函数或getter函数会抛出错误
在之前创建访问器属性的方法:用两个非标准的方法: *defineGetter()和*defineSetter__()
3.定义多个属性
Object.definePro- perties()方法
通过描述符可一次性定义多个属性
4.读取属性的特性
Object.getOwnPropertyDescriptor()方法
可以取得给定属性的描述符
接收参数::属性所在的对象和要读取其描述符的属性名称
返回值:对象
可读取(对象的类型)
访问器属性: 对象的属性有 configurable、enumerable、get 和 set
数值:对象的属性有 configurable、enumerable、writable 和 value
在JavaScript中,可以针对任何对象——包括 DOM和BOM对象,使用 Object.getOwnProperty- Descriptor()方法
二、创建对象
1.工厂模式
ECMAScript无法创建类
createPerson()函数能够替代类用函数封装以特性接口创建对象的细节
可无数次调用该函数
返回值:包含三个属性一个方法的对象
缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)
2.构造函数模式
Person()函数
与createPerson()函数的不同之处
按照惯例,构造函数始终都应该以一个 大写字母开头,而非构造函数则应该以一个小写字母开头
创建对象新例要使用new操作符
经历的步骤:
任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数,否则则为普通函数
缺点:每个方法都要在每个实例重新创建
3.原型模式
每个函数都有prototype(原型)属性
该属性是一个指针,指向一个对象
用途:包含可以由特定类型的所有实例共享的属性和方法
不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中
1.理解原型
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象
所有实现中都无法访问到[[Prototype]],但可以通过 isPrototypeOf()方法来确定对象之 间是否存在这种关系
Object.getPrototypeOf()函数
返回[[Prototype]]的值
调用 person1.sayName()的时候,会先后执行两次搜索(先实例,再原型)
原型最初只包含 constructor 属性,而该属性也是共享的,因此可以通过对象实例访问
可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值
设置同名的属性,在实例中创建,而原型中的属性将屏蔽
使用 delete 操作符则可以完全删 除实例属性,从而让我们能够重新访问原型中的属性
hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中
Object.getOwnPropertyDescriptor()方法只能用于实例属 性,要取得原型属性的描述符,必须直接在原型对象上调用 Object.getOwnProperty- Descriptor()方法
2.原型与 in 操作符
单独使用:in 操作符会在通 过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中
在 for-in 循环中使用:在使用 for-in 循环时,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性
Object.keys()方法
目的:取得对象上所有可枚举的实例属性
接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
Object.getOwnPropertyNames() 方法
得到所有实例属性,无论它是否可枚举
强制改动constructor属性将导致[[Enumerable]]特性被设置为 true
Object.defineProperty():重设构造函数功能
3.原型的动态性
在原型中查找值的过程是一次搜索,对原型对象所做的任何修改都能够立即从实例上反映出来
实例与原型之间的松散连接关系
实例中的指针仅指向原型,而不指向构造函数,一旦设立构造函数,将切断与实例之间的关系
4.原生对象的原型
所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法
如果因某个实现中缺少某个方法,就在原生对象的原型中添加这个方法,那么当在另一个支 持该方法的实现中运行代码时,就可能会导致命名冲突,也会意外重写原生方法
5.原生对象的问题
原型中所有属性是被很多实例共享的,这种共享对于函数非常合适
4.组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
5.动态原型模式
使用动态原型模式时,不能使用对象字面量重写原型
6.寄生构造函数模式
基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象
返回的对象与构造函数或者与构造函数的原型属 性之间没有关系
7.稳妥构造函数模式
没有公共属性,而且其方法也不引用 this 的对象
与寄生构造函数类似的模式的不同点
新创建对象的 实例方法不引用 this
不使用 new 操作符调用构造函数
与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也 没有什么关系,因此 instanceof 操作符对这种对象也没有意义。
三、继承
1.原型链
利用原型让一个引用类型继承另一个引用类型的属性和方法
别忘记默认的原型
默认原型都会包含一个内部指针,指向 Object.prototype
因此所有自定义类型都会继承 toString()、 valueOf()等默认方法
确定原型和实例的关系
通过两种方式来确定原型和实例之间的关系:
1.使用 instanceof 操作符,只要用 这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true
2.使用 isPrototypeOf()方法。同样,只要是原型链中出现过的原型,都可以说是该 原型链所派生的实例的原型,因此 isPrototypeOf()方法也会返回 true
谨慎地定义方法
给原型添加方法的代码一定要放在替换原型的语句之后,否则将无效
通过原型链实现继承时,不能使用对象字面量创建原型方法,否则将重写原型链
原型链的问题
包含引用类型值的原型,会被所有实例共享
通过原型来实现继承时,原型实际上会变成另一个类型的实例,原先的实例属性也就顺理成章地变成了现在的原型属性了
2.借用构造函数
在子类型构造函数的内部调用超类型构造函数
通过使用 apply()和 call()方法也可以在(将来)新创建的对象上执行构造函数
传递参数:可以在子类型构造函数中向超类型构造函数传递参数
借用构造函数的问题
3.组合继承(combination inheritance)
也叫做伪经典继承
将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式
思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
4.原型式继承
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型
前提:必须有一个对象可以作为另一个对象的基础
Object.create()方法
接收两个参数:一 个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象(会覆盖掉原型对象上同名属性)
5.寄生式继承
创建一个仅用于封装继承过程的函数,该 函数在内部以某种方式来增强对象,后再像真地是它做了所有工作一样返回对象
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一 点与构造函数模式类似
6.寄生组合式继承
组合继承大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部
导致的问题:子类型终会包含超类型对象的全部实例属性,但我们不得不在调用子 类型构造函数时重写这些属性
寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
思路:使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型
YUI的 YAHOO.lang.extend()方法采用了寄生组合继承,从而让这种模式首次 出现在了一个应用非常广泛的 JavaScript库中