javascript高级程序设计-第6章 -面向对象的程序设计
! 构造函数、原型和实例的关系:
每个构造函数都有一个原型对象,
原型对象都包含一个指向构造函数的指针,
而实例都包含一个指向原型对象的内部指针
理解对象
属性类型
数据属性 - 包含一个数据值的位置。在这个位置可读取写入
4种描述其行为的特性:
[[Configurable]] - 表示是否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
[[Enumerable]] - 表示能否通过for - in循环返回属性。
[[Writable]] - 表示能否修改属性的值
[[Value]] - 包含这个属性的数据值
object.defineProperty(属性所在的对象,属性的名字,一个描述符对象[描述对象的属性必须是:configurable、enumerable、writable、value])
访问器属性
getter函数 - 负责返回有效的值
setter函数 - 负责决定如何处理数据
4个特征:
[[Configurable]] - 表示是否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
[[Enumerable]] - 表示能否通过for - in循环返回属性。
[[Get]] - 在读取属性时调用的函数
[[Set]] - 在写入属性时调用的函数
定义多个属性
object.defineProperties(要添加或修改的对象,第二个对象与第一个对象种要添加或修改的属性一一对应)
读取属性的特性
object.getOwnPropertyDescriptor( )
创建对象
工厂模式
function xxx( ){
return ;}
var x = xxx( );
构造函数模式
以大写字母打头,当要创建新实例时,必须使用new操作符。
4个步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(this指向这个新对象)
- 执行构造函数中的代码(为这个新对象添加新属性)
4.返回新对象
1. 将构造函数当做函数
通过new操作符来调用函数,该函数就可以作为构造函数。
2. 构造函数的问题
每个方法都要在每个实例上重新创建一遍
原型模式
prototype - 包含可以由特定类型的所有实例共享的属性和方法。
通过构造函数而创建的那个对象实例的原型对象
1. 理解原型对象
新建函数时,会根据特定的规则为该函数创建一个prototype属性,该P属性指向函数的原型对象。
hasOwnProperty( ) 属性为实例化 new 时返回true,属性为原型时返回false
2. 原型与in操作符
单独使用:通过对象能够访问给定属性时返回true (name in object)
for-in循环时,返回所有能够通过对象访问的、可枚举的属性。
object.keys( ) //返回一个包含所有可枚举属性的字符串数组
3. 更简单的原型语法
用一个包含所有属性和方法的对象字面量来重写整个原型对象
//重设构造函数,只适用es5的浏览器
object.defineProperty(Person.prototype,"constructor",{
enumerable:false,
value:Person
}
4. 原型的动态性
原型对象修改(通过构造函数方式修改)的变动都能从实例上反映出来,
但重写原型对象,会切断构造函数和最初原型的联系,实例的指针仅指向原型,不指向构造函数。
5. 原生对象的原型
所有原生引用类型都在其构造函数的原型上定义了方法。
不推荐在产品化的程序中修改原生对象的原型。可能导致命名冲突
6. 原型对象的问题
1.省略了构造函数传递初始化参数这一环节,结果所有实例在默认情况下都取得相同的属性值。
2.实例化的对象无法修改原型的固有属性。
组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
动态原型模式
使用if语句检查的可以是初始化之后应该存在的任何属性或方法
寄生构造函数模式
parasitic 创建一个函数,封装创建对象的代码,再返回新创建的对象,
在构造函数里实例化一个对象或数组,再添加值和方法,再返回该对象或数组。
稳妥构造函数模式
稳妥对象(durable objects) 没有公共属性,不引用this对象。
首先创建要返回的对象。
可以定义私有变量和函数
添加方法,
返回对象
继承
原型链
利用原型让一个引用类型继承另一个引用类型的属性和方法。
- 搜索实例,2. 搜索原型的prototype,3. 搜索上层原型的prototype
1.别忘记默认的原型
- 所有函数的默认原型都是object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype
2.确定原型和实例的关系
1. 使用instanceof操作符,使用该操作符来测试实例与原型链中出现过的构造函数,结果就会返回true
2.使用isPrototypeOf( )方法,只要原型链中出现过的原型,都可以说该原型链所派生的实例的原型,也会返回true
3.谨慎地定义方法
子类型有时候需要覆盖超类型中的某个方法或者需要添加超类型中不存在的某个方法,给原型添加方法的代码一定要放在替换原型的语句之后。
且不能在原型链实现继承后,使用字面量添加新方法,会导致继承失效
4.原型链的问题
引用类型的原型属性会被所有实例共享
在创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数 ( constructor stealing )
在子类型构造函数的内部调用超类型构造函数。而函数是在特定环境中执行代码的对象,因此apply( )和call( ) 也可以在新创建的对象上执行构造函数。
1.传递参数
2.借用构造函数的问题 - 方法都在构造函数中定义,无法复用,且超类型的原型中定义的方法,对子类型不可见。
useful:组合继承
使用原型链实现对原型属性和方法的继承,通过构造函数来实现对实例属性的继承。
原型式继承
es5通过新增object.create( )方法规范了原型式继承,接收两个参数:一个用作新对象原型的对象和(可选)一个作为新对象定义额外属性的对象。
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象
寄生组合式继承
创建超类型原型的一个副本
为创建的副本添加constructor属性
将新创建的对象(即副本)复制给子类型的原型。