您现在的位置是:网站首页 > 对象创建方式文章详情

对象创建方式

在JavaScript中,对象是核心概念之一,创建对象的方式多种多样,每种方式都有其适用场景和优缺点。从简单的字面量到复杂的构造函数和类,开发者可以根据需求选择最合适的方法。

对象字面量

对象字面量是最简单直接的创建对象的方式,适合快速定义简单对象。语法简洁,直接通过{}定义属性和方法。

const person = {
  name: 'Alice',
  age: 25,
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

person.greet(); // 输出: Hello, my name is Alice

这种方式适合不需要复用对象结构的场景,但如果需要创建多个相似对象,重复代码会增多。

工厂模式

工厂模式通过函数封装对象创建过程,解决字面量方式重复代码的问题。函数内部构造对象并返回。

function createPerson(name, age) {
  return {
    name,
    age,
    greet() {
      console.log(`Hello, my name is ${this.name}`);
    }
  };
}

const person1 = createPerson('Alice', 25);
const person2 = createPerson('Bob', 30);

缺点是无法识别对象类型(所有实例的构造函数都是Object),且方法会在每个实例中重复创建。

构造函数模式

构造函数通过new操作符调用,自动创建新对象并绑定this。约定构造函数名称首字母大写。

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function() {
    console.log(`Hello, my name is ${this.name}`);
  };
}

const person = new Person('Alice', 25);
console.log(person instanceof Person); // true

虽然解决了对象类型识别问题,但方法仍然在每个实例中重复定义,浪费内存。

原型模式

利用原型链共享属性和方法,所有实例共享同一组方法,减少内存消耗。

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

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const person1 = new Person('Alice', 25);
const person2 = new Person('Bob', 30);
console.log(person1.greet === person2.greet); // true

缺点是所有实例共享引用类型属性时可能产生意外修改。

组合模式(构造函数+原型)

结合构造函数和原型的优点,实例属性通过构造函数定义,共享方法通过原型定义。

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

Person.prototype = {
  constructor: Person,
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const person = new Person('Alice', 25);

这是ES5时代最常用的对象创建模式。

动态原型模式

将原型方法的定义封装在构造函数中,保持代码集中性,同时避免重复初始化原型。

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

  if (typeof this.greet !== 'function') {
    Person.prototype.greet = function() {
      console.log(`Hello, my name is ${this.name}`);
    };
  }
}

Object.create()

直接基于现有对象创建新对象,无需构造函数。

const personProto = {
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const person = Object.create(personProto);
person.name = 'Alice';
person.age = 25;

适合需要精确控制原型链的场景。

ES6类语法

class语法糖本质仍是基于原型的继承,但提供了更清晰的语法结构。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

const person = new Person('Alice', 25);

支持静态方法、继承等特性:

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }

  study() {
    console.log(`${this.name} is studying`);
  }
}

单例模式

确保一个类只有一个实例,并提供全局访问点。

const singleton = (function() {
  let instance;

  function init() {
    return {
      name: 'Singleton',
      log() {
        console.log('I am the only one');
      }
    };
  }

  return {
    getInstance() {
      if (!instance) {
        instance = init();
      }
      return instance;
    }
  };
})();

const s1 = singleton.getInstance();
const s2 = singleton.getInstance();
console.log(s1 === s2); // true

对象解构与属性简写

ES6新增的语法特性可以简化对象创建过程。

const name = 'Alice';
const age = 25;

const person = {
  name,
  age,
  ['id_' + Math.random().toString(36).substr(2)]: true
};

const { name: personName } = person;
console.log(personName); // Alice

使用Symbol作为属性键

Symbol可以创建唯一的对象属性键,避免属性名冲突。

const id = Symbol('id');
const person = {
  [id]: '123',
  name: 'Alice'
};

console.log(person[id]); // 123
console.log(Object.keys(person)); // ['name']

不可变对象

通过Object.freeze()或第三方库创建不可变对象。

const person = Object.freeze({
  name: 'Alice',
  address: Object.freeze({
    city: 'Beijing'
  })
});

person.name = 'Bob'; // 静默失败(严格模式报错)

代理对象(Proxy)

使用Proxy可以创建具有自定义行为的对象。

const handler = {
  get(target, prop) {
    return prop in target ? target[prop] : 'Not found';
  }
};

const person = new Proxy({ name: 'Alice' }, handler);
console.log(person.name); // Alice
console.log(person.age); // Not found

对象池模式

对于创建成本高的对象,可以通过对象池复用实例。

class ObjectPool {
  constructor(createFn) {
    this.createFn = createFn;
    this.pool = [];
  }

  acquire() {
    return this.pool.length ? this.pool.pop() : this.createFn();
  }

  release(obj) {
    this.pool.push(obj);
  }
}

const pool = new ObjectPool(() => ({ id: Date.now() }));
const obj1 = pool.acquire();
pool.release(obj1);

上一篇: 函数柯里化

下一篇: 属性描述符

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步