大家好,我是RevioLee。javascript高级程序设计第六章 面向对象的程序指南 是很多公司的基础面试题,又是记忆难点,总是记了忘,忘了记,所以这边自己总结了一套口诀,希望能帮助小伙伴记忆创建对象的方法和继承的方法。
创建方法
创建方式 | 口诀 | 意思 |
---|---|---|
工厂模式 | new里retrun | 函数里new对象return出去 |
构造函数 | this return new | this指针赋值,new构造函数 |
原型 | propertype | 构造函数.prototype赋值 |
组合 | this return propertype | 属性使用构造函数,方法使用原型 |
动态原型 | typyof fun | 判断方法存在,不存在则this.propertype.fun |
寄生函数 | 工厂模式new | 跟工厂模式很像,使用new创建 |
稳妥构造 | 工厂模式里方法不this | 跟工厂模式很像,但不使用this而是使用参数 |
继承
继承方式 | 口诀 |
---|---|
原型链 | fun1.prototype=new fun2() |
借构造函数 | fun1里fun2 call |
组合 | 构造属性原型方法 |
原型式 | Object.create() |
寄生式 | 普通函数使用原型式继承 |
寄生组合式 | 构造属性原型混合方法 |
这些是我平时记忆这些方法的口诀,小伙伴们也可以自己挑各种方式的关键词总结一套适合自己的记忆方式,谢谢大家
JavaScript面向对象的程序设计
理解对象
属性类型
- 数据属性
- configurable
- 默认true,设置false后只能设置writable
- enumerable
- 默认true
- writable
- 默认true
- value
- 默认undefined
- configurable
- 访问器属性
- configurable
- 默认true
- enumerable
- 默认true
- get
- 默认undefined
- set
- 默认undefined
- configurable
- 定义单个对象属性:Object.defineProperty(对象名,属性名(String),对象(Object))
定义多个属性
- Object.defineProperties(对象名,对象(Object))
读取属性特性
- Object.getOwnPropertyDescriptor(对象名,属性名(String)) 最后得到Object,包含着特性
创建对象
工厂模式
- 优点:解决了创建多个相似对象的问题
缺点:没有解决对象识别的问题function createPerson(name, age, job){ var o = new Object(); o.name=name; o.age = age; o.sayName = function(){ alert(this.name) }; return o; } var person1 = createPerson('Lee', 29, 'teacher'); var person2 = createPerson('Lin', 26, 'doctor');
构造函数模式
- 优点:解决工厂模式的缺点
缺点:实例中的方法不是同一个Function的实例
1.虽然可以用全局作用域内定义相同的Function,但是全局作用域声明的Function只被某个对象调用,让全局作用域名不副实,
2.如果对象需要定义很多方法,就需要在全局作用域定义多个方法,则没有任何封装性可言- 区别:1.没有显示地创建对象;
2.直接将属性和方法赋给了this对象;
3.没有return语句; - 注意:构造函数命名以大写字母开头
- 步骤:1.创建一个新对象,并继承原构造函数的原型;
2.将构造函数的作用域赋值给新对象(因此this指向了这个新对象)并为这个新对象添加属性;
3.返回新对象
- 区别:1.没有显示地创建对象;
- 将构造函数当作函数
- 构造函数与非构造函数的区别在于调用的方式不同
//当作构造函数调用 var Person = new Person('Lee', 29, 'Teacher') Person.sayName();'Lee' //当作函数调用 Person('Lee', 29, 'Teacher'); window.sayName();'Lee' //在其他对象的作用域中使用 var o = new Object() Person.call(o, 'Lee', 29, 'Teacher') o.sayName(); 'Lee'
- 构造函数与非构造函数的区别在于调用的方式不同
- 手写new方法
function myNew(fn,...args) { //指向fn的prototype,两者有相同的prototype let obj = Object.create(fn.prototype) //现在obj就代表Dog了,但是参数和this指向没有修改 const rel =fn.apply(obj,args) //正常规定,如何fn返回的是null或undefined(也就是不返回内容),我们返回的是obj,否则返回rel return rel instanceof Object ? rel : obj }
原型模式
- 缺点:1.省略了传递初始化参数;
2.共享特性导致引用类型的属性相等- 理解原型对象
- 每个函数都含有属性prototype,prototype指向该函数的原型对象,原型对象中则有constructor属性,指向该函数
- 原型对象.isPrototypeOf(实例)=判断该原型对象是否为该实例的proto
- Object.getPrototypeOf(实例)=获取原型对象
- 实例.hasOwnProperty(‘属性名’)=判断属性存在实例还是原型中
- 理解原型对象
- 原型与in操作符
- 单独使用
- ‘name’ in person1
- for in
- Object.keys(‘对象名“)
- Object.getOwnpropertyNames(‘对象名”)//获取对象上包括不可枚举属性在内的属性
- 单独使用
- 更简单的原型语法
- 使用对象字面量的方式重写prototype
- 注意唯一区别:对象字面量的constructor指向Object构造函数
修复:可在对象字面量中重新定义constructor,并通过Object.defineProperty()设置位enumerable:false
- 原型的动态性
- 无论何时创建实例,只要原型做了任何修改都能立即反应:原型中查找值的过程是一次搜索
- 注意:实例中的指针(proto)仅指向原型,而不指向构造函数
- 重写原型对象切断现有原型与任何之前已经存在的对象实例之前的联系
- 原生对象的原型
- 原生的引用类型采用原型模式创建的
- 如Array中的所有方法都是有Array.prototype.Fun创建
- 原生的引用类型采用原型模式创建的
组合使用构造函数模式和原型模式
- 集齐原型模式和构造函数模式的两者之长
- 独立的构造函数和独立的原型,封装性差些
动态原型模式
- 注意,不要重写原型,会切断之前实例与新原型的关系
function Person(name, age) { this.name = name this.age = age if (typeof (this.sayName) != 'function') { Person.prototype.sayName = () => { console.log(this.name) } } }
let person1 = new Person(‘Lee’,18)
person1.sayName()
寄生构造函数模式
- 优点:实现在不改变原有构造函数的前提下为构造函数添加特殊的方法
缺点:不能识别对象类型
function SpecialArray() { let values = new Array() values.push.apply(values, arguments) values.toPipedString = function () { return this.join("|") } return values } var colors = new SpecialArray("red", "blue", "green") colors.toPipedString()
稳妥构造函数模式
- 特点:1.没有公共属性,2.不引用this的对象,3不使用new操作符
场景:安全环境(禁用this和new)
缺点:不能识别对象类型 - 缺点:不能识别对象类型
- function Person(name, age) {
var o = new Object();
o.sayName = function () {
console.log(name)
}
return o
}
var friend = Person(“Lee”)
friend.sayName()
继承
原型链
- 基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法
/* 构造函数1 */ function SuperType() { this.property = true } /* 原型模式创建构造函数共享方法 */ SuperType.prototype.getSuperValue = function () { return this.property; } /* 构造函数2 */ function SubType() { this.subproperty = true } /* 构造函数2的原型等于构造函数1的实例 */ SubType.prototype = new SuperType() /* 修改构造函数2的原型 */ SubType.prototype.getSubValue = function () { return this.subproperty } var instance = new SubType() alert(instance.getSuperValue)
注意:instance.constructor指向SuperType。因为SubType的原型被重写
- 基本概念:构造函数与其原型对象和构造函数的实例与其原型对象的关系,如果使func1的原型对象等于func2的实例,那么func1包含着proto指向func2的原型对象,假如使func2的原型对象等于func3的实例,上述关系仍然成立,如此层层递进就构成了实例与原型的链条
- 1.别忘了默认的原型
- 2.确定原型和实例的关系
- instanceof()
- isPrototypeOf()
- 3.谨慎地定义方法
- 子类型添加或覆盖超类型方法一定写在重写超类型原型方法之后
- 否则子类型添加或覆盖超类型方法会被屏蔽
- 本质:原型对象指针改变
- 作用:通过实现原型链扩展了原型搜索机制
- 实例搜索属性,在通过原型链实现继承的情况下,搜索过程得以沿着原型链继承向上
- 缺点:1.引用类型值共享特性.2.不能在不影响所有对象实例的情况下传递参数
借用构造函数
- 基本思想:在子类型构造函数的内部中调用超类型的构造函数
function SuperType() { this.colors = ['blue', 'red'] } function SubType() { SuperType.call(this) } let instance1 = new SubType(); instance1.colors.push('green') console.log(instance1.colors) let instance2 = new SubType(); console.log(instance2.colors)
- 通过改变子类型中的this指针,指向超类型
- 缺点:函数无法复用
组合继承
- 思路:通过在原型上定义方法实现函数复用,又能保证每个实例拥有独立属性
/* 构造函数创建非共享属性 */ function SuperType(name) { this.name = name } /* 原型模式创建共享方法 */ SuperType.prototype.sayName = function () { console.log(this.name) } /* 借用构造函数继承SuperType */ function SubType(name, age) { SuperType.call(this, name) this.age = age } /* 原型链继承方法 */ SubType.prototype = new SuperType() SubType.prototype.construtor = SubType; SubType.prototype.sayAge = function () { console.log(this.age) } let instance1 = new SubType('Lee', 18) instance1.sayName() instance1.sayAge() let instance2 = new SubType('Sun', 22) instance2.sayName() instance2.sayAge()
- 缺点:调用了两次超类型构造函数
原型式继承
- 作用:只想让一个对象与另一个对象保持一致的情况下
let person = { name: 'Lee', colors: ['blue'] } let a1 = Object.create(person) a1.name = 'G' a1.colors.push('red') let a2 = Object.create(person) a2.name = 'D' a2.colors.push('green') console.log(person.colors)
- 缺点:共享特性
寄生式继承
- 主要考虑对象,而不是自定义类型和构造函数时
function createAnother(original) { var clone = Object.create(original) clone.sayHi = function () { console.log('hi') } return clone } let person = { name: 'Lee', friends: ['Sun'] } createAnother(person).sayHi()
- 缺点:无法函数复用
寄生组合式继承
- 1.借用构造函数继承属性
2.原型链混成形式继承方法
function SuperType(name) { this.name = name this.colors = ['blue', 'red', 'green'] } SuperType.prototype.sayName = function () { console.log(name) } //借用构造函数继承属性 function SubType(name, age) { SuperType.call(this, name) this.age = age } - //通过原型链混成形式继承方法 function inheritProtoType(SubType, SuperType) { let obj = Object.create(SuperType.prototype) //原型式继承 obj.constructor = SubType //增强对象 SubType.prototype = obj //指定对象 } inheritProtoType(SubType, SuperType) SubType.prototype.sayAge = function () { console.log(this.age) }