【JS系列】一块儿理解对象的7种建立方式(全)

前言

起初接触JS时常见的简单地建立单个对象的方式有下面三种:设计模式

  • 字面量,即var obj = {}
  • new Object()
  • Object.create()

以上三种方式在须要建立不少对象时会产生大量重复代码。显然须要更高效的方式,下面将介绍7种建立对象的方式安全

1. 工厂模式

1.1介绍

工厂模式正是源自设计模式中的工厂模式,其基本思想:函数

  • 既然建立对象数量多时产生了大量重复代码
  • 那就建一座工厂,经过工厂来建立加工
  • 再返回建立好的对象
function createPerson(name, age) {
  var obj = new Object()
  obj.name = name
  obj.age = age
  obj.getName = function() {
    console.log(this.name)
  }
  return obj
}

var person1 = createPerson("AAA", 23)
person1.getName()   //AAA

var person2 = createPerson("BBB", 30)
person2.getName()   //BBB
复制代码

1.2 优劣分析

  • 解决了复用性的问题
  • 但没法判断对象的类型(只知道是Objcet类型)

2. 构造函数模式

2.1 介绍

构造函数可建立特定类型的对象,相似Object和Array(实际上Object和Array自己就是构造函数)post

function Person(name, age) {
  this.name = name
  this.age = age
  this.getName = function() {
    console.log(this.name)
  }
}

var person1 = new Person("AAA", 23)
person1.getName()  //AAA

var person2 = new Person("BBB", 30)
person2.getName()  //BBB
复制代码

经过上面代码咱们能够看到构造函数模式:优化

  • 没有经过new Object()显示建立对象
  • 属性和方法赋值给了this
  • 无需return

2.2 构造函数也是函数

构造函数其实也是个函数,只是能够经过new调用来建立对象,固然也可做为普通函数使用ui

//做为普通函数使用
Person("CCC", 24)
Person.getName() //CCC

//在一个对象内调用
var obj = {}
Person.call(obj, "DDD", 25)
obj.getName()    //DDD
复制代码

2.3 优劣分析

  • 构造函数模式能够建立具体类型的对象
console.log(person1 instanceof Object)  //true
console.log(person1 instanceof Person)  //true
复制代码
  • 然而,构造函数中的方法是通用的,但每一个实例都建立了一份属于本身的副本,形成内存浪费
console.log(person1.getName === person2.getName)  //false
复制代码

3. 原型模式

3.1 介绍

原型模式即经过原型对象来实现属性和方法的共享,实例对象不会建立各自的副本this

要理解原型模式,建议先理解透彻JS原型的概念——推荐阅读一张图完全KO原型链(prototype,__proto__)spa

function Person() {
}

Person.prototype.name = "AAA"
Person.prototype.age = 23
Person.prototype.getName = function() {
  console.log(this.name)
}

var person1 = new Person()
person1.getName()    //AAA

var person2 = new Person()
person2.name = "BBB"
person2.getName()    //BBB

console.log(person1.getName === person2.getName)  //true
复制代码

然而原型对象也存在缺陷——对于引用类型的属性,各实例对象间指向同一个地址,某个对象修改了属性,全部对象都会受到影响prototype

function Person() {
}

Person.prototype.arr = [1,2,3]
Person.prototype.getArr = function() {
  console.log(this.arr)
}

var person1 = new Person()
person1.arr.push(4)
person1.getArr()    //1,2,3,4

var person2 = new Person()
person2.getArr()    //1,2,3,4

person1.arr.push(5)
person1.getArr()    //1,2,3,4,5
person2.getArr()    //1,2,3,4,5
复制代码

3.2 优劣分析

  • 原型模式解决了属性和方法的共享问题
  • 但对于引用类型的属性,各实例对象会相互影响

4. 组合模式

4.1介绍

组合模式即组合构造函数模式和原型模式,取两者之长,构造函数模式用于定义实例的属性,原型模式用于定义方法和共享的属性设计

function Person(name, age) {
  this.name = name
  this.age = age
  this.arr = [1,2,3]
}

Person.prototype = {
  getName: function() {
    console.log(this.name)
  }
}

var person1 = new Person("AAA", 23)
person1.arr.push(4)
console.log(person1.arr)  //1,2,3,4
person1.getName()         //AAA

var person2 = new Person("BBB", 30)
console.log(person2.arr)  //1,2,3
person2.getName()         //BBB
复制代码

4.2 优劣分析

  • 组合模式是使用最普遍的一种模式,尤为在须要定义引用类型时

5. 动态原型模式

5.1 介绍

刚说组合模式是承认度最高的一种模式,然而也有些美中不足——每建立一个实例对象,原型方法都被重复定义一次

动态原型模式正是解决这个问题,使用if语句,使得原型方法只初始化一次

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

  //这里只须要使用任何一个方式或属性,不必定是getName,getAge也能够
  //只要保证if里面的代码只执行一次就行
  if(typeof this.getName !== 'function') {
    Person.prototype.getName = function() {
      console.log(this.name)
    }

    Person.prototype.getAge = function() {
      console.log(this.age)
    }
  }
}

var person = new Person("AAA", 23)
person.getName()    //AAA
复制代码

5.2 优劣分析

  • 动态原型模式在组合模式的基础上作了优化,可谓更加完美

6. 寄生构造函数模式

6.1 介绍

长得和工厂模式同样,但调用方式和构造函数模式同样的模式,经过new调用构造函数,本该返回一个实例对象,但return语句重写了构造函数的返回值

function Person(name, age) {
  var obj = new Object()
  obj.name = name
  obj.age = age
  obj.getName = function() {
    console.log(this.name)
  }
  return obj
}

var person = new Person("AAA", 23)
person.getName()   //AAA
复制代码

6.2 优劣分析

  • 这种方式和工厂模式没什么不一样,要说不一样的就是经过new来调用,仍然存在工厂模式的没法判别对象类型的问题

  • 我的认为这是一种多余的方式,复杂而很差理解,也没有什么应用场景

7. 稳妥构造函数模式

7.1 介绍

先介绍一个概念——稳妥对象:

  • 没有公共属性
  • 不使用this
  • 不使用new调用

稳妥构造函数模式和工厂模式相似,但遵循稳妥对象的原则

function Person(name, age) {
  var obj = new Object()

  //getName是惟一读取name的方式
  obj.getName = function() {
    console.log(name)
  }
  return obj
}

var person = Person("AAA", 23)
person.name = "BBB" //无效
person.getName()    //AAA
复制代码

7.2 优劣分析

  • 稳妥构造函数模式的特色就是安全性,适用于某些须要安全执行的环境
  • 相似工厂模式,没法判别对象类型

8. 总结

  1. 工厂模式:解决了大量重复代码的问题,但没法判别对象类型

  2. 构造函数模式:实例对象又具体的类型,但每一个实例对象都有方法的副本,形成内存浪费

  3. 原型模式:可共享方法和属性,但存在引用类型相互影响的问题

  4. 组合模式:取构造函数模式和原型模式之长 (最经常使用)

  5. 动态原型模式:优化了组合模式

  6. 寄生构造函数模式:相似工厂模式,只是经过new调用

  7. 稳妥构造函数模式:相似工厂模式,只是提高了安全性

后记

但愿本文对你有帮助,更多文章将持续更新……

感谢你的点赞和鼓励

与本文相关的文章有:

【JS系列】一张图完全KO原型链(prototype,__proto__)

【JS系列】对象详解

【JS系列】继承的这6种方式!(上)

【JS系列】继承的这6种方式!(下)

相关文章
相关标签/搜索