JavaScript中创建对象的几种方式

377 阅读3分钟

JavaScript中创建对象的几种方式

  前端学习笔记系列,内容参考自JavaScript高级程序设计(第四版) 首先先说本文会介绍到的几种创建对象的方法:

  1. 工厂模式
  2. 构造函数模式
  3. 原型模式
  4. 对象迭代

  在JavaScript中,使用Object构造函数或者对象字面量都可以方便的可以很方便地创建对象,但这些方式也有明显不足:创建具有同样接口的对各对象需要重复编写很多代码。

工厂模式

  工厂模式是一种常见的设计模式,用于抽象创建特定对象的过程。工厂模式创建对象的一个例子如下:

function createPerson(name, age) {
    let o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function () {
        console.log(this.name);
    }
    return o;
}

let person1 = createPerson("Tom", 21);
let person2 = createPerson("Jack", 22);

  createPerson()函数接收两个参数,返回拥有传入参数对应信息的对象。工厂模式的优点是我们多次调用该函数传入不同的参数,可以解决创建多个类似的对象问题,但是没有办法解决新创建的对象是什么类型的问题。

构造函数模式

  ECMAScript中的构造函数是用于创建特定类型对象的,Object就是一个原生的构造函数。上面的例子使用构造函数可以这样写:

//按照惯例,构造函数的名称首字母都是大写的
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        console.log(this.name);
    }
}

let person1 = new Person("Tom", 21);
let person2 = new Person("Jack", 22);

person1.sayName();  //Tom
person2.sayName();  //Jack

这种方法和前面的工厂模式在函数内部很像,只是有以下几个区别:

  • 没有显式地创建对象
  • 属性和方法直接赋值给了this
  • 没有return 构造函数的主要问题在于其定义的函数会在每一个实例上都创建一遍。在这个例子中不同实例上的函数虽然是同名但却是不相等的,如下:
console.log(person1.sayName == person2.sayName);  //false

可以把函数定义移到构造函数外部解决解决这个问题,但是这样的话自定义类型引用的代码不能很好的聚集在一起,也容易搞乱作用域,要解决这个问题,可以使用下面的原型模式。

原型模式

  每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定引用类型的实例贡献的属性和方法。原来在构造函数直接赋值给对象实例的值,可以直接赋值给他们的原型,如下所示:

function Person() {}

Person.prototype.name = "Tom";
Person.prototype.age = 21;
Person.prototype.sayName = function() {
    console.log(this.name);
}

let person1 = new Person();
let person2 = new Person();

person1.sayName();  //Tom
person2.sayName();  //Tom

console.log(person1.sayName == person2.sayName);  //ture

使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。但是它弱化了向构造函数传递初始化参数的能力,会导致所有默认实例都取得相同的属性值。更大的缺点也来自它的共享性:如果两个实例都有一个数组属性,实例1往该数组中添加了一个字符串时,因为该属性是直接存在prototype上而不是实例中,其他对象的这个属性也会改变。

对象迭代

  ECMAScript 2017新增了两个静态方法,用于将对象内容转换为序列化、可迭代的格式。这两个静态方法Object.values()和Object.entries()接收一个对象,返回他们内容的数组。如下:

const o = {
    foo: 'bar',
    baz: 1,
    qux: {}
};

console.log(Object.values(o));  //["bar", 1, {}]
console.log(Object.entries(o));  //[["foo", "bar"], ["baz", 1], ["qux", {}]]

注意,这种方法非字符串属性会被转换成字符串输出,且符号属性会被忽略。

书中关于对象迭代内容介绍的比较少,后续有新的内容会继续改进。