javascript基础系列之类的总结

153 阅读4分钟

实例创建

工厂模式

优点:可以批量创造相似对象 缺点:无法识别对象,(即,无法知道一个对象的类型) 解决方法:构造函数模式

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的时候发生了以下行为:

  1. 一个继承自 Foo.prototype 的新对象被创建。
  2. 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
  3. 由构造函数返回的对象就是 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()规范了原型式继承,可以传入两个参数

  1. 用作新对象原型的对象,下方中的o,只传入第一个参数时效果和下方函数运行结果相同
  2. 为新对象额外定义一个熟悉,使用方法类似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)
}