实例创建
工厂模式
优点:可以批量创造相似对象 缺点:无法识别对象,(即,无法知道一个对象的类型) 解决方法:构造函数模式
function createPerson(name, age, job){
var o = new Object()
o.name = name
o.job = job
o.age = age
o.sayHi = function(){
console.log(o.name)
}
return o
}
const instance = createPerson("xiaoming", 18, programer)
instance instanceof createPerson//false
构造函数模式
优点:可以识别对象了 缺点:实例的同名方法是不一样的(内存地址) 解决方法:原型模式
function createPerson(name, age, job){
this.name = name
this.job = job
this.age = age
this.sayHi = function(){
console.log(this.name)
}
}
const instance = new createPerson("xiaoming", 18, programer)
instance instanceof createPerson//true
原型模式
缺点:方法/属性共享之后,一个实例修改原型对象上的属性,另一个实例就会受影响 解决方法:组合使用构造函数模式与原型模式
function Person() {
}
Person.prototype.name = "name"
Person.prototype.sayName = function () {
console.log(this.name)
}
const p1 = new Person()
const p2 = new Person()
组合使用构造函数模式和原型模式
function Person(name, age, job){
this.name = name
this.age = age
this.job = job
this.friends = ["Shelby", "Court"]
}
Person.prototype={
constructor: Person,
sayHi:function(){
console.log(this.name)
}
}
const p1 = new Person("xiaoming", 30, "teacher")
const p2 = new Person("xiaogang", 28, "policeman")
p1.friends === p2.friends //false
p1.sayHi === p2.sayHi //true
这里使用了new关键字,根据MDN的解释构造函数new的时候发生了以下行为:
- 一个继承自 Foo.prototype 的新对象被创建。
- 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
- 由构造函数返回的对象就是 new表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
这里我们实现一下new
function(fn){
const args = [].slice.apply(arguments).slice(1)
const obj = {}
obj.prototype = fn.prototype
const result = fn.apply(obj,args)
//一般情况都是返回obj,因为构造函数没有返回值
return (res instanceof Object) ? res : obj
}
类的继承
原型模式
缺点:
1.subType的实例们共享了superType实例的所有属性,并且如果subType的实例中有一个对原型链上属性的修改,会影响其他实例
2.创建子类的是不能向超类的构造函数传递参数
解决方法:借用构造函数
function superType(){
this.colors =['red', 'black']
}
function subType(){
}
subType.prototype = new superType()
const sub = new subType()
console.log(sub instanceof subType) //true
console.log(sub instanceof superType) //true
借用构造函数
缺点:同构造函数,对于方法的继承的低效,方法不能共享 解决方法:组合继承
function superType(name){
this.name = name
}
function subType(){
superType.call(this, "xiaoming")
}
const instance = new subType()
组合继承
思路:使用原型链对原型属性和方法的继承,使用借用构造函数模式实现对实例属性的继承
缺点:使用了两次构造函数,第二次是原型链上的操作,共享了实例的所有属性/方法
function superType(name){
this.name = name
}
super.prototype.sayHi = function(){
console.log(this.name)
}
function subType(){
//继承属性
superType.call(this, "xiaoming")
this.age = age
}
//继承方法
subType.prototype = new superType()
subType.prototype.constructor = subType
const instance = new subType()
原型式继承
ES5通过Object.create()规范了原型式继承,可以传入两个参数
- 用作新对象原型的对象,下方中的o,只传入第一个参数时效果和下方函数运行结果相同
- 为新对象额外定义一个熟悉,使用方法类似Object.defineProperties()方法中的第二个参数
function object(o){
function F(){}
F.prototype= o
return new F()
}
//其本质是一种浅复制
var person = {
name:'Nicholas',
friends:['a', 'b', 'c']
}
var anotherPerson = object(person)
anotherPerson.name = "Gerg"
anotherPerson.friends.push('d')
var yetAnotherPerson = object(person)
yetAnotherPerson.name = "Linda"
yetAnotherPerson.friends.push("e")
//复制的对象改变了friends属性
console.log(person.friends)//a, b, c, d, e
寄生式继承
缺点:不能做到函数复用(内存地址)而降低效率,与构造函数类似
function createAnother(original){
var clone = object(original)
clone.sayHi = function(){
console.log("hi")
}
return clone
}
寄生组合式继承
1. 创建一个空的构造函数,该构造函数的原型链为父类的原型链
2. new 空的构造函数得到的对象,纠正该对象的constructor从父类转到子类,并作为子类的原型
3. 将新原型链接到子类的原型上
//这里并没有使用超类构造函数,也没有涉及超类的实例属性/方法
function inheritPrototype(subType, superType){
var prototype = object(superType.propotype)
//如果没有这一步,那么prototype的constructor是superType
prototype.constructor = subType
subType.propertype = prototype
}
function supertype(name){
this.name = name
this.colors = ['red', 'blue', 'green']
}
super.prototype.sayName = function(){
console.log(this.name)
}
function subtype(name, age){
//这里只调用了一次超类的构造函数
supertype.call(this, name)
this.age = age
}
//这里不涉及任何构造函数的操作
inheritPrototype(subtype, supertype)
subtype.propertype.sayAge =function(){
console.log(this.age)
}