1 理解对象
1.1 属性类型
数据属性(ecma-262第五版)
用于实现JavaScript引擎,JavaScript不能直接访问
[[Configurable]]: true(默认)或false, 限制以下行为
1 | ☑︎ 通过delete删除属性从而重新定义属性; |
[[Enumerable]]: true(默认)或false,限制以下行为
1 | ☑︎ 通过for-in循环返回属性; |
[[Writable]]: true(默认)或false, 限制以下行为
1 | ☑︎ 修改属性的值; |
[[Value]]: undefined(默认)或属性的数据值
修改数据属性 Object.defineProperty()
调用该方法时,如果不指定,configurable、enumerable 和 writable 特性的默认值都是 false。
1 | 兼容性 |
案例一: 设置属性只读[[Writable]]
1 | var persion = {}; |
访问器属性
读取访问器属性时调用 getter
函数,写入访问器属性时调用 setter
函数
[[Configurable]]: true(对象上的属性默认)或false
1 | ☑︎ 通过delete删除属性从而让定义属性; |
[[Enumerable]]: true(对象上的属性默认)或false
1 | 通过for-in循环返回属性。 |
[[Get]]: 读取属性是调用的函数,默认undefined
[[Set]]: 在写入属性时调用的函数,默认undefined
修改
方式一:Object.defineProperty()(ECMAScript 5)
1 | IE8(部分实现) IE9+ Firefox4+ Safari 5+ Opera12+ Chrome |
1 | var book = { |
方式二:两个非标准的遗留的方法(最初由firefox引入)
1 | _defineGetter_() |
1 | Firefox Safari 3 Chrome1 Opera9.5 |
1 | var book ={ |
1.2 定义多个属性(ECMAScript5)
1 | IE9+ Firefox 4+ Safari 5+ Opera 12+ Chrome |
1 | var book = {}; |
1.3 读取实例对象属性的特性 Object.getOwnPropertyDescriptor()
1 | IE9+ Firefox 4+ Safari 5+ Opera 12+ Chrome |
1 | var book ={}; |
2 创建对象
2.1 工厂模式
1 | function createPersion(name, age, job){ |
2.2 构造函数模式
1 | 不显示创建对象 |
过程
- 创建一个对象
- 将构造函数的作用于赋给新对象(this就指向这个新对象)
- 执行构造函数中的代码
- 返回新对象
1 | function Persion(name, age, job){ |
将构造函数当作函数
1 | function Persion(name, age, job){ |
缺点:每个方法在每个实例上都要重复创建一遍!
2.3 原型模式
每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。所有对象实例共享它所包含的属性和方法。
1 | //构造函数 |
理解原型对象
prototype、constractor、proto
1 | //实例对象访问其构造函数的prototype中的属性(非标准) |
isPrototypeOf()
1 | //判断persion1这个实例的原型是否是Persion.prototype |
hasOwnProperty()
继承自Object,用于检测一个属性存在于实例中还是原型中。
原型与in操作符
1 | ☑︎ 通过对象能够访问给定属性(实例或原型中的)时返回true |
in和hasOwnProperty()联合使用
1 | /* |
IE BUG
存在于早期版本中,即屏蔽不可枚举属性(比如toString)的实例属性不会出现在for-in循环中。
1 | var o = { |
替代for-in
1 | ie9+ Firefox 4+ Safari 5+ Opera12+ Chrome |
Object.keys()ECMAScript 5
取得对象上所可枚举的实例属性名。
1 | param{Object} 要枚举的对象 |
Object.getOwnPropertyNames()
取得对象上所(无论是否可枚举)实例属性名。
1 | param{Object} 要枚举的对象 |
1 | var keys = Object.getOwnPropertyNames(Persion.prototype); |
更简单的原型语法
避免每次为原型添加属性都重复一遍 Person.prototype.
1 | ☑︎ 本质上完全重写了默认的 prototype 对象,导致 constructor 属性指向 Object 构造函数而不是 Persion 函数(可修复); |
忽略副作用
1 | Persion.prototype = { |
修复副作用(原型的constructor属性不再指向Person的问题)
1 | Persion.prototype = { |
修复副作用(constructor编程可枚举的问题):
1 | function Person(){} |
原型的动态性
1 | ☑︎ 对于重写原型之前创建的实例,引用的仍然是之前的原型。 |
1 | function Person(){} |
原生对象的原型
所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。
取得默认方法
1 | alert(typeof Array.prototype.sort); //'function' |
定义新方法
1 | String.prototype.startWith = function(text){ |
原型对象的问题
实例一般都是要有属于自己的属性,不同实例的属性值如果不一样就不能放到原型中。
2.4 组合使用构造函数模式和原型模式(推荐)
1 | 构造函数模式用于定义实例属性; |
1 | function Persion(name, age, job){ |
2.5 动态原型模式
将所有信息都封装在构造函数中;通过检查某个方法是否有效来决定是否需要初始化原型。
注意: 该模式下不能使用对象字面量重写原型,否则某些实例已经创建的情况下重写原型会切断现有实例与新原型之间的联系。
1 | function Person(name, age, job){ |
2.6 寄生构造函数模式
1 | 缺陷 |
1 | function SpecialArray(){ |
2.7 稳妥构造函数模式
1 | ☑︎ 没有公共属性; |
1 | function Persion(name, age, job){ |
3 继承
继承分两类
- 接口继承(JS没有函数签名,因此无法实现)
- 实现继承
3.1 原型链
实现原型链的一种基本模式:
1 | //父对象 |
别忘记默认的原型
所有引用类型的默认原型都是 Object 的实例。
确定原型和实例的关系
- instanceof
如果实例的原型链中出现过指定的构造函数,结果就会返回true。
1 | alert(instance instanceof Object); //true |
- isPrototyptof()
效果同instanceof。
1 | alert(Object.prototype.isPrototypeOf(instance)); //true |
谨慎地定义方法1
2
3注意:
☑︎ 如果要替换原型,实例替换默认的 prototype 后才能在 prototype 中定义新的方法;
☑︎ 通过原型链实现继承时,不能通过字面量创建原型方法;
1 | //父对象 |
原型链的问题
1 | ☑︎ 包含引用类型的原型属性会被所有实例共享; |
3.2 借用构造函数
通过call()方法(或apply()方法),在(未来将要)新创建的SubType实例的环境下调用SuperType构造函数。
1 | //创建构造函数 |
传递参数
1 | function SuperType(name){ |
1 | ☑︎ 借用构造函数的问题 |
3.3 组合继承(推荐)
1 | ☑︎ 避免了原型链和借用构造函数的缺点,融合了它们的优点; |
1 | function SuperType(name){ |
3.4 原型式继承
不使用构造函数,借助原型基于已有的对象创建新对象,同时不必因此创建自定义类型。
1 | function object(o){ |
Object.create()
ECMAScripe 5
原型继承被规范化为此方法
1 | param {Object} 新对象原型 |
1 | //作为Objetc.create()第一个参数的对象 |
1 | IE9+ Firefox4+ Safari 5+ Opera12+ Chrome |
3.5 寄生式继承
寄生式(parasitic)继承是与原型式继承紧密相关的一种思路,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象。
1 | //该方法用于返回一个对象(只要返回一个对象就符合模式要求) |
3.6 寄生组合式继承(最理想)
1 | ☑︎ 只调用一次超类构造函数; |
1 | function object(o){ |
1 | /* |