JS高程知识点总结-6(面向对象)

一、对象的概念
1、js中对象:无序属性的集合,键值对,其内部属性(无法直接访问)有:数据属性和访问器属性;
数据属性的4个特性(描述符):
[[Configurable]]:决定能否修改删除属性,默认true;
[[Enumerable]]:决定是否可枚举(能否用for-in返回),默认true;
[[Writable]]:属性值能否修改,默认true;
[[Value]]:保存属性值,默认undefined;
修改属性必须用Object.defineProperty()方法,接收三个参数:对象名,属性名,特性名称;
Configurable只能修改一次;删除属性要使用delete(propertyName)方法;
访问器属性(不包含数据值,包含一对getter/setter函数),其4个特性(描述符):
[[Configurable]]:决定能否修改删除属性,默认true;
[[Enumerable]]:决定是否可枚举(能否用for-in返回),默认true;
[[Get]]:读函数,默认undefined;
[[Set]]:写函数,默认undefined;
访问器属性必须用Object.defineProperty()方法定义(P141);
2、ES5定义多个属性的方法:Object.defineProperties(),接收两个对象,第一个是要修改和添加属性的对象,第二个是属性集合;
注:在调用Object.defineProperty()和Object.defineProperties()方法时,
如果不指定,那Configurable、Enumerable、Writable特性的值均默认为false;
3、获取属性描述符:Object.getOwnPropertyDescriptor()方法,接收两个参数,对象名和属性名,
根据属性的类型(数据属性和访问器属性),获取其对应的描述符;

二、对象的定义
1、工厂模式:封装属性和方法接口,创建多个相似对象,缺点是,无法对知道生成的对象类型;
2、自定义构造函数模式:
自定义构造函数(构造函数首字母必须大写,普通函数小写),用new操作符调用;缺点是每个方法在每个实例上都要创建一遍,即函数不能复用;
3、原型模式(P148):
3.1、概念
Person:构造函数;Person.prototype:原型对象;Person.prototype.constructor:指向Person;
通过构造函数的prototype属性,在其原型对象上定义实例的信息;构造函数的prototype属性指向原型对象;
原型对象自动获得constructor(构造函数)属性,指向构造函数,
使用构造函数生成的实例具有[Prototype]属性,指向构造函数的prototype属性(即原型对象);
使用Person.prototype.isPrototypeOf(实例)方法来检测某个对象是否是实例的原型对象;
ES5 使用Object.getPrototypeOf(实例)来获取实例的原型对象;
使用hasOwnProperty()方法来检测属性是否存在于实例本身;
3.2、in操作符:
单独使用:不论是实例属性还是原型属性,均返回true;
for-in:返回所有可枚举属性(包括原型和实例属性),包括屏蔽了原型中不可枚举属性的实例属性(IE8及之前版本不适用);
ES5新增替换for-in方法:
Object.keys():返回所有可枚举的实例属性;
Object.getOwnPropertyNames():返回所有实例属性;
默认情况下,原生constructor属性不可枚举;
4、现在默认使用自定义构造函数模式和原型模式组合的方式定义对象;构造函数用于定义实例属性,原型模式定义方法和共享的属性。
5、寄生构造函数模式:
形式与工厂模式基本一致,不过其使用new操作符,并且包装函数使用的是构造函数;
无返回值时默认返回新对象实例,在构造函数末尾加上return语句,可以重写返回值;
6、稳妥构造函数模式:
不使用new操作符调用构造函数,构造函数内部不使用this;

三、继承
1、OO语言一般有两种继承:接口继承(继承方法签名)和实现继承(继承实际的方法),JS中函数没有签名,只支持实现继承,由原型链实现;
2、原型链:
让一个构造函数的原型等于另一个对象的实例,则该原型也有proto属性,指向对应对象的原型,如此重复就形成了原型链,
3、原型链实现继承带来的两个问题:
1、引用类型值带来的问题,超类型的实例属性由于原型链的存在变成了子类型的原型属性,从而被子类型的所有实例共享;
2、创建子类型实例时,无法向超类型构造函数传递参数;
所以,一般不单独使用原型链;
4、借用构造函数:在子类型构造函数的内部调用超类型构造函数(call()、apply()方法),可传参;解决上述两个问题;
新引入的问题:导致方法都在构造函数中定义,无法实现函数复用;所以不常用这种方法;
5、组合继承
用原型链实现对原型属性和方法的继承,用构造函数实现对实例属性的继承;使每个实例有自己的属性,并且实现函数复用;
6、原型式继续:基于一个对象创建新对象,传入的对象作为构造函数的原型,相当于对该对象做浅复制;
ES5新增Object.create()方法实现原型式继承,接受两个参数:作为对象原型的对象(可选)和为新对象定义额外属性的对象;
第二个参数与Object.defineProperties()的第二个参数的格式一致,每个属性都由其描述符定义的;
7、寄生式继承:
创建封装继承过程的函数,将其复制给新对象(调用了实现原型式继承的函数),对新对象做增强(增加其属性和方法),然后返回新对象;
这种方式存在的问题和构造函数模式一致,无法实现函数复用,效率低。
8、寄生组合式继承:
组合继承是最常用的继承模式,但是这两种模式下,会两次调用超类型构造函数,第一次是创建子类型原型时;第二次是在子类型构造函数内部;
因此衍生寄生组合式继承,借用构造函数来继承属性,通过原型链的混成形式来继承方法;

其实质就是在定义子类型原型时,采用寄生式继承方式,而不使用超类型的构造函数,