属性类型
数据属性
- [[Configurable]]:是否可以通过delete删除属性,能否修改属性特征
- [[Enumerable]]:能否用过for-in循环返回属性
- [[Writable]]:是否修改属性的值
- [[Value]]:包含属性的数据值
访问器属性
- [[Configurable]]:是否可以通过delete删除属性,能否修改属性特征
- [[Enumerable]]:能否用过for-in循环返回属性
- [[Get]]:读取属性时调用的函数
- [[Set]]:写入属性时调用的函数
定义读取属性
1 | Object.defineProperty(obj, "name", {}) |
创建对象
工厂模式
用函数封装特定接口创建对象的细节,但是无法识别对象
1 | function createPerson(name, age, job) { |
构造函数模式
调用构造函数的过程:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(所以this就指向了该对象)
- 执行构造函数
- 返回新对象
构造函数模式的问题:
- 不同实例上的同名函数是不相等的
1 | function Person(name, age) { |
原型模式
可以让所有对象实例共享所含的属性和方法
如图所示,实例的__proto__属性指向他的构造函数的prototype。
通过Object.getPrototypeOf(obj)可以返回这个对象的原型。
通过Object.hasOwnProperty(“name”)可以检测一个属性是否存在该实例中,而不是在原型中。
通过Object.keys()可以取得随向上所有可枚举的实例属性。
原型模式的问题:
- 省略了为构造函数传参的过程,所有实例默认情况下将取得相同的属性值
- 共享的本性(特别对包含引用类型值的属性)
1 | function City() { |
组合使用构造函数和原型模式
构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性
1 | function City(name) { |
动态原型模式
1 | function City(name) { |
寄生构造函数模式
创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后在返回新创建的对象。
问题:构造函数返回的对象与构造函数外部创建的对象没有什么不同,所以不能依赖instanceof来确定对象类型
1 | function SpecialArray(...arr) { |
稳妥构造函数模式
稳妥对象:没有公共属性,并且方法也不引用this的对象(安全性)
稳妥构造函数与寄生构造函数类似,但有两点不同:
- 新创建对象的实例方法不引用this
- 不适用new操作符调用构造函数
1 | // 稳妥构造函数 |
继承
原型链
利用原型让一个引用类型继承另一个引用类型的属性和方法。
问题:
- 主要问题是包含引用类型值的原型
- 在创建子类型的实例时,不能想超类型的构造函数中传递参数
1 | function SuperType() { |
借用构造函数
问题:
- 方法都在构造函数中定义,因此函数复用就无从谈起
- 在父类的原型中定义的方法,对于子类是不可见的。必须所有类都采用构造函数模式
1 | function SuperType(name) { |
组合继承
使用原型链实现对原型属性和方法的继承,解用构造函数来实现对实例属性的继承。
问题:会调用两次父类的构造函数
1 | function SuperType(name) { |
原型式继承
1 | function object(o) { |
包含引用类型值的属性始终都会共享响应的值(浅复制)
寄生式继承
1 | function createAnother(o){ |
寄生组合式继承
通过构造函数来继承属性,通过原型链的混成形式来继承。
不必为了指定子类型的原型而调用超类型的构造函数,使用寄生式继承超类型的原型。
1 | function SuperType(name) { |