Skip to content

JavaScript之面向对象 #6

Open
@chenyong9528

Description

@chenyong9528

面向对象

《JavaScript高级程序设计》笔记

创建对象的方式

1. 工厂模式

function createPerson(name, age, job) {
  var o = new Object()
  o.name = name
  o.age = age
  o.job = job
  o.sayName = function() {
    alert(this.name)
  }
  return o
}

var person = createPerson("faker", 18, "LOL")

缺点:对象无法识别,方法不能共享

2. 构造函数模式

function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = function() {
    alert(this.name)
  }
}

var person = new Person("faker", 18, "LOL")

缺点:还是方法共享问题

2.1 构造函数模式优化

function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = sayName
}

function sayName() {
  alert(this.name)
}

var person = new Person("faker", 18, "LOL")

缺点:方法共享了,但谈不上封装,每增加一个方法就得在外面再定义一个函数

3. 原型模式

function Person() {}
Person.prototype = {
  constructor: Person, // 重写原型对象会切断原型与构造函数之间的联系,我们应该纠正它
  name: "faker",
  age: 18,
  job: "LOL",
  sayName() {
    alert(this.name)
  },
}

var person = new Person()

缺点:实例只有共享属性和方法,没有自身的属性,这还不是它最大的问题,最大的问题是它共享本质所导致的;例如,在原型中存在一个引用类型的数组,那么所有实例都会共享该数组,通过实例操作数组会在所有实例中反映出来。

4. 组合模式

function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
}

Person.prototype = {
  constructor: Person,
  sayName() {
    alert(this.name)
  },
}

var person = new Person("faker", 18, "LOL")

集构造函数和原型模式二者之长,这是JavaScript中用的最常用的一种模式

5. 动态原型模式

function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  if (typeof this.sayName != "function") {
    Person.prototype.sayName = function() {
      alert(this.name)
    }
  }
}

var person = new Person("faker", 18, "LOL")

具有更好的封装性

6. 寄生构造函数模式

function Person(name, age, job) {
  var o = new Object()
  o.name = name
  o.age = age
  o.job =job
  o.sayNmae = function() {
    alert(this.name)
  }
  return o
}

var person = new Person("faker", 18, "LOL")

不太了解有什么用

7. 稳妥构造函数模式

function Person(name) {
  var o = new Object()
  o.sayName = function() {
    alert(name)
  }

  return o
}

var person = Person("faker")

在某些安全环境中会用到这种模式,因为只有sayName方法可以访问到name,它有两个特点:

  1. 不使用this
  2. 不使用new创建对象

仔细看看,其实这是闭包的一种应用

继承的方式

1. 原型链

function SuperType() {
  this.name = "faker"
}

SuperType.prototype.getName = function() {
  return this.name
}

function SubType() {
  
}

// 继承了SuperType
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType

var instance = new SubType()

alert(instance.getName()) // faker

缺点:

  1. 包含引用类型的值会被所有实例共享(原型不就是用来共享的?)
  2. 子类原型成为了父类的一个实例,父类实例中的属性也变成了原型中的属性(我们想要的是一个纯净的原型)

2. 借用构造函数

function SuperType(name){
  this.name = name
}

function SubType(name){  
  SuperType.call(this, name)
}

var instance = new SubType("faker")
alert(instance.name)   // faker

缺点:仅使用借用构造函数无法避免构造函数存在的共享问题

3. 组合继承

function SuperType(name) {
  this.name = name
}

SuperType.prototype.getName= function() {
  return this.name
}

function SubType(name) {
  // 继承了属性
  SuperType.call(this, name)
}

// 继承了方法
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType

var instance = new SubType("faker")
alert(instance.getName()) // faker

缺点:虽然集原型链和构造函数二者之长,但有两个问题:

  1. 调用了两次父构造函数
  2. 实例的name会屏蔽掉SubType原型中的name,其实原型中的name的存在是没有意义的

4. 原型式继承

function object(o) {
  function F() {}
  F.ptototype = o
  return new F()
}
var o = {
  name: "faker"
}
var obj = object(o)
alert(obj.name) // faker

// 更好的方式
var obj1 = Object.create(o)
alert(obj1.name) // faker

这是道格拉斯提出的一种,以现有对象为原型来创建新对象的方式,ES5中用Object.create()规范了这种方式

5. 寄生式继承

function createAnother(o) {
  var clone = Object.create(o)
  clone.sayHi = function() { // 增强对象
    alert("Hi!")
  }
  return clone
}

顾名思义,这是一种寄生在原型式继承之上的一种模式

6. 寄生组合式继承

function inheritPrototype(subType, superType) {
  var prototype = Object.create(superType.prototype)
  prototype.constructor = subType
  subType.prototype = prototype
}

function SuperType(name) {
  this.name = name
}

SuperType.prototype.getName = function() {
  return this.name
}

function SubType(name, age) {
  SuperType.call(this, name)
  this.age = age
}

inheritPrototype(SubType, SuperType)

var instance = new SubType("faker", 18)
alert(instance.getName()) // faker

寄生组合式继承是寄生式继承和借用构造函数的组合,相比组合继承,它只调用了一次父构造函数,因此在子类的原型上也没有了多余的属性。这是目前最理想的继承范式。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions