第6章 面向对象的程序设计
对象:无序属性的集合,其属性值可以包含基本值,对象或者函数
6.1 理解对象
创建单个对象
- Object构造函数:
var obj = new Object();obj.a=... - 字面量:
var obj = {a:..}
6.1.1
-
属性类型
- 数据属性:
- 一个数据值的位置,用于读写值
- 四个描述符
- [[Configurable]]
- [[Enumerable]]
- [[Writable]]
- [[Value]]
- 访问器属性:
- 一堆getter/setter:用于读写值
- 四个描述符
- [[Configurable]]
- [[Enumerable]]
- [[Get]]
- [[Set]]
- 数据属性:
-
访问器属性不可直接定义,必须调用
Object.defineProperty()来调用
6.1.2 定义多个属性:
Object.defineProperties()
6.1.3 读取属性特性:
Object.getOwnPropertyDescriptor()
6.2 创建对象
Object构造函数或字面量不适合创建同一接口的多个对象
6.2.1:工厂模式
- 原理:定义一个用于创建对象的方法,每次创建对象时调用此方法
- 定义
function createPerson(name){ var o = new Object(); o.name = name; ... return o; } var a = createPerson('小明') - 缺点:无法标识创建对象为某个特定类型
6.2.2:构造函数模式
- 原理:函数的构造调用
- 创建一个新对象
- 将构造函数的this指向新对象
- 执行构造函数中的代码
- 返回新对象(如果代码中无返回时)
- 定义
function Person(name){ this.name = name; ... } var a = new Person('小明') - 优点:实例可以标识为某个特定类型
- 缺点:每个方法都要在每个实例上重新创建一遍(不同实例的同名函数并不是同一个)
6.2.3:原型模式
- 原理:原型链
- 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,此属性为一个对象,称为该函数的原型对象。默认情况下,原型对象会默认添加一个constructor属性,其值为该函数(所在的指针)
a.isPropertyOf(b):a是否需为b的原型对象Object.getPropertyOf():获取某个对象的原型对象obj.hasOwnProperty('b'):obj中是否有b属性(只在当前对象上,不在原型链上)'b' in obj:obj中是否有b属性(在实例上或者原型链上)for .. in:只会出现enumerable为true的属性Object.getOwnPropertyNames():获取某个对象的所有实例属性(和enumerable无关)Object.keys():获取实例所有可枚举属性
- 定义
function Person(name){} Person.prototype.name = ".."//完全区别于Person.name="..",如果这样写,a实例是访问不到name的,只有当前函数Person可以访问的到 ... var a = new Person() - 优点:
- 可以标识为某个特定类型
- 方法共享
- 缺点:
- 实例的所有属性都相同,无法通过传参进行定制化
- 当其属性值为引用类型时,共享的属性会被实例修改
6.2.4:组合使用构造函数模式和原型模式
- 原理:构造函数 + 原型
- 构造函数:通过传参自定义实例属性
- 原型:通过原型定义共享属性和方法
- 定义
function Person(name){ this.name = ".." } Person.prototype = { constructor:Person, ... } ... var a = new Person() - 优点:
- 可以标识为某个特定类型
- 方法共享
- 可以通过传参定制实例属性,共享公共属性(仍然可以修改)
6.2.5:动态原型模式
- 原理:将原型模式封装进构造方法中,并使原型的定义只发生在第一次
- 定义
function Person(name){ this.name = ".."; if(typeof this.sayName !== 'function'){ Person.prototype.sayName = function(){...} } } var a = new Person() - 优点:
- 可以标识为某个特定类型
- 方法共享
- 可以通过传参定制实例属性,共享公共属性(仍然可以修改)
6.2.6:寄生构造函数模式:
- 原理:工厂模式的构造函数形式
- 定义
function Person(name){ var o = new Object(); o.name = name; ... return o; } var a = new Person('小明') - 缺点:无法标识创建对象为某个特定类型
6.2.7:稳妥构造函数模式:
- 原理:稳妥对象
- 没有公共属性,且其他方法也不引用this对象
- 不可直接访问对象的属性,而是通过方法调用
- 定义
function Person(name){ var o = new Object(); //只有私有属性 ... o.sayName = functiuon(){...} return o; } var a = new Person('小明')
6.3 继承
继承分为接口继承和实现继承,js中只能实现实现继承
6.3.1 原型链继承
- 原理:重写子函数(类)的prototype属性为父函数(类)的实例
- 定义
function Super(){}; function Sub(){}; Sub.prototype = new Super(); var instance = new Sub() - 缺点:
- 父类包含引用类型的值时,会被重写
- 创建子类实例时,无法向父类构造函数传参
6.3.2 借用构造函数
- 原理:在子类构造函数中调用父类构造函数
- 定义
function Super(){}; function Sub(){ Super.call(this) }; var instance = new Sub() - 优点:可传参
- 缺点:父中函数无法复用,在每个子类中都需重新定义
6.3.3 组合集成
- 原理:原型链 + 构造函数
- 使用原型链实现对原型属性和方法的继承
- 通过构造函数实现对实例属性的继承
- 定义
function Super(name){ this.name = name; }; Super.prototype.sayName = function(){...} function Sub(name,age){ Super.call(this.name); this.age = age; }; Sub.prototype = new Super(); Sub.prototype.constructor = Sub; Sub.prototype.sayAge = function(){...} var instance = new Sub() - 优点:可传参,可复用
- 缺点:Super构造函数调用两次
6.3.4 原型式继承
Obect.create(o)
createObject(o){
function F(){}
F.prototype = o;
return F;
}
6.3.5 寄生继承
- 原理:创建一个仅用于封装继承过程的函数,在函数内部增强对象再返回
- 定义
function createAnother(original){ var clone = Obect.create(original);//或者createObject(original) clone.sayHi = function(){} return clone; }; var instance = createAnother();
6.3.6 寄生组合式继承
- 原理:
- 父级:
- 构造函数:通过传参定义实例定制属性,定义父级共享属性
- 原型上:定义父级共享属性和方法
- 子级:
- 构造函数:
- 调用父级构造函数并传参
- 定义子级共享属性
- 原型:
- 指向父级原型
- 重定向constructor
- 定义子级共享属性和方法,重写父级方法
- 构造函数:
- 父级:
- 定义
function Super(...){...}; Super.prototype... = ...; function Sub(...){ Super.call(...); ... }; Sub.prototype = Object.create(Super) Sub.prototype.constructor = Sub; ... var instance = new Sub()