说明: 以下知识来源网上素材,纯属个人笔记; 如需了解可以查找原文,请勿在此纠结
在js中,对象是键值对的集合,可以通过很多种方式创建; 六种创建方式:对象字面量、构造函数、Object.create()、工厂函数、class、单例模式。
1.对象字面量(Object Literal)
用法: 直接通过{}定义对象,是最简单的方式;
原理: 直接分配内存创建一个普通对象,继承object.prototype;
- 直接内存分配: 当使用{}定义对象时,js引擎会直接在堆内存中分配一个普通对象。
- 隐藏原型继承: 该对象的[[Prototype]](即 proto) 会自动指向 Object.prototype, 继承原始对象默认方法 (如:toString).
- 属性初始化: 字面量中的属性和方法会直接挂载到对象自身,而非原型链。
例子:
js
const person {
name: '小明',
age: 25,
greet() {
console.log(`hi , i'm ${this.name}`)
}
}
person.greet() //hi ,i'm 小明
js
const obj = {key:"value"};
//等价于:
const obj = new Object(); //隐式调用 Object 构造函数
obj.key = "value";
特点:
- 适合创建单例对象或一次性对象;
- 所有属性方法均为实例自身成员,无法共享
2.构造函数(constructor function)
用法:通过 new 关键字调用构造函数创建对象
原理: new 操作
- 1.创建空对象: 在内存中创建一个新对象 {}
- 2.绑定原型:将新对象的[[Prototype]] 即原型链, 指向构造函数的prototype属性
- 3.执行构造函数:将构造函数内的this指向新对象,执行构造函数代码(初始化属性)
- 4.返回对象: 若构造函数未显示返回其他对象,则返回新对象;
例子:
js
function person(name, age){
// this = {}; (隐式操作)
// this.__proto__ = person.prototype; (隐式操作)
this.name = name;
this.age = age;
this.greet = function(){
console.log(`hi, i'm ${this.name}`)
}
// return this; (隐式操作)
}
const xiaoming = new person('小明', 25);
xiaoming.greet(); //hi, i'm 小明
//原型链结构: xiaoming.__proto__ -> person.prototype -> Object.prototype -> null;
缺点:每个实例的方法都是独立的,浪费内存(可以优化共享原型方法);
- 若方法定义在构造函数内部,每个实例会有独立的方法副本(浪费内存);
- 若方法定义在person.prototype上,所有实例共享同一方法(推荐做法)
3. Object.create()
用法:基于现有对象作为原型创建新对象。
原理:创建一个新对象,并将其Prototype 指向传入的对象;
- 显式原型继承:直接创建一个新对象,并手动指定其[[Prototype]] 即:原型链 __ proto __
- 参数处理: 1.若传入null, 新对象的[[Prototype]]为null (无原型链); 2.若传入对象proto, 新对象继承proto的属性和方法;
例子:
js
const prototypeObj = {
greet(){
console.log(`hi, i'm ${this.name}`)
}
}
const person = Object.create(prototypeObj);
person.name = "小明";
person.age = 10;
person.greet(); //hi , i'm 小明
扩展:可创建无原型的对象(Object.create(null)),适合纯粹的数据存储;
4.工厂函数(Factory Function)
用法: 封装对象创建逻辑的函数,直接返回新对象;
原理:手动创建对象并返回,不依赖 new 或原型链;
- 封装对象创建: 通过普通函数返回新对象,不依赖new或者原型链。
- 独立实例: 每次调用工厂函数都会产生新的对象,所有属性和方法均为实例自身成员。
例子:
js
function createPeason(name,age){
const obj = {}; //显示创建对象
obj.name = name;
obj.age = age;
obj.greet = function(){} //方法不共享
return obj;
}
const xiaoming = createPeason('小明',10);
xiaoming.greet(); //hi, i'm 小明;
特点:避免 new 的使用,但方法无法共享(每个实例的方法独立)。
对比构造函数:
- 优势: 更直观,避免new 的依赖。
- 劣势: 方法无法共享,内存效率低。 (因为是闭包啊)
5.ES6 class 语法
用法:使用clase关键字定义类,通过new实例化;
原理:本质是构造函数的语法糖(构造函数另一种写法),基于原型链实现继承。
- 方法挂载: 类中定义的方法会自动添加到prototype对象上,实现共享;
- 继承机制: extends 关键字通过修改原型链实现继承(子类的prototype 指向父类实例)
例子:
js
class Person {
construtor(name,age){
this.name = name;
this.age = age;
}
greet(){
console.log(`hi, i'm ${this.name}`)
}
}
const xiaoming = new Person('小明',10);
xiaoming.greet(); // hi, i'm 小明;
//原型链结构: xiaoming.__proto__ -> Person.prototype -> Object.prototyle -> null;
优点: 语法更清晰,支持继承(extends)和静态方法(static)
6.单例模式(Singleton)
用法:确保一个类只有一个实例;
原理:通过闭包或模块化限制实例化次数
- 闭包封装: 通过立即执行函数(iife)和闭包保存唯一实例的引用。
- 惰性初始化: 在首次调用时创建实例,后续调用直接返回已存在的实例;
例子:
js
const singleton - (function(){
let instance;
function createInstance() {
return { id: Math.random() };
}
return {
getInstance(){
if(!instance) instance = createInstance();
return instance;
}
}
})();
const obj1 = singleton.getInstance();
const obj2 = singleton.getInstance();
console.log(obj1 === obj2); //true
汇总 - 总结对比
方式 | 特点 | 适用场景 |
---|---|---|
对象字面量 | 简单直接,适合一次性对象 | 简单数据存储、配置项 |
构造函数 | 可复用,但方法定义在原型上 节省内存 | 需要多个相似实例 |
Object.create() | 灵活控制原型链,可创建无原型的对象 | 原型继承、纯净对象 |
工厂函数 | 避免 new, 但方法无法共享 | 简单对象生活 |
class | 语法清晰,支持继承 | 面向对象开发 |
单例模式 | 全局唯一实例 | 全局状态管理 |
选择建议:
- 简单对象: 优先用对象字面量或者工厂函数。
- 复用和继承: 使用class或构造函数。
- 原型控制: 使用Object.create()。
- 全局唯一: 单例模式。
温馨说明: 以上素材来源网上,如有整理不对地方,欢迎指出