ECMA-262 将对象定义为一组属性的无序集合。严格来说,这意味着对象就是一组没有特定顺序的值。对象的每个属性或方法都由一个名称来标识,这个名称映射到一个值。
在 JavaScript 中,有多种方式可以创建对象,每种方式都有其优缺点。以下是几种常用的方法,以及它们的优缺点和示例:
1. 工厂函数
可以复用对象模板 ,没法使用instanceof
判断对象类型。
function createPerson(name, age) {
let o = new Object();
o.name = name;
o.age = age;
o.say = function () {
console.log(this.name);
};
return o;
}
let person1 = createPerson('simahe', 18);
let person2 = createPerson('mingqina', 20);
console.log(person1); //{name: 'simahe', age: 18, say: ƒ}
console.log(person2); //{name: 'mingqina', age: 20, say: ƒ}
console.log(person1 instanceof createPerson); //false
console.log(person1 instanceof Object); //true
2. 字面量方式
语法简洁明了易于理解,但对象模板不能复用。
const person1 = {
name: 'simahe',
age: '18',
say: function () {
console.log(`name: ${this.name}, age: ${this.age}`);
},
};
person1.say(); // name: simahe, age: 18
console.log(person1); //{name: 'simahe', age: '18', say: ƒ}
console.log(person1.__proto__ == Object.prototype); //true
3. 构造函数
可以复用对象模板 ,每个实例都会重复创建相同的方法,占用内存。
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(this.name);
};
}
let person1 = new Person('simahe', 18);
let person2 = new Person('mingqian', 20);
console.log(person1); //{name: 'simahe', age: 18, say: ƒ}
console.log(person2); //{name: 'mingqina', age: 20, say: ƒ}
console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true
console.log(person2 instanceof Person); //true
console.log(Person.prototype); //{}空对象,没有定义方法
console.log(person1.__proto__ === Person.prototype); //true
console.log(person1.__proto__.constructor === Person); //true
4.原型模式
函数的属性和方法都定义在 prototype
原型对象上,属性共享问题。
-
Person
的Prototype
属性,指向它的原型对象; -
Person.prototype.constructor === Person
原型对象的constructor
指向自己; -
person1
,person2
,有__proto__
属性,指向它的原型对象,也就是构造函数的原型对象; -
person1.__proto__ === Person.prototype
,Person.prototype.__proto__ === Object.prototype
,Object.prototype.__proto__ ===null
原型链注意:对象是
__proto__
,函数是prototype
function Person() {}
Person.prototype.name = 'simahe';
Person.prototype.age = 18;
Person.prototype.skill = ['java', 'c#', 'php'];
Person.prototype.say = function () {
console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
person1.skill.push('node');
person1.skill.push('go');
console.log(person1.skill); // ['java', 'c#', 'php', 'node', 'go']
console.log(person2.skill); // 也被修改 ['java', 'c#', 'php', 'node', 'go']
console.log(person1 instanceof Person); //true
console.log(Person.prototype); //{name: 'simahe', age: 18, skill: Array(5), say: ƒ}
console.log(person1.__proto__); //{name: 'simahe', age: 18, skill: Array(5), say: ƒ}
console.log(person2.__proto__); //{name: 'simahe', age: 18, skill: Array(5), say: ƒ}
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Person.prototype.constructor === Person);
5. Object.create()
使用现有的对象来提供新创建的对象的 __proto__
。性能不如字面量方式。
const personPrototype = {
name: 'simahe',
age: '18',
say: function () {
console.log(`name: ${this.name}, age: ${this.age}`);
},
};
const person1 = Object.create(personPrototype);
const person2 = Object.create(personPrototype);
console.log(person1.__proto__); //{name: 'simahe', age: 18, say: ƒ}
console.log(person2.__proto__); //{name: 'mingqina', age: 20, say: ƒ}
6. Class
ES6 新增语法,更适合面向对象语言。
class Parent {
constructor(name) {
this.name = name;
}
say() {
console.log(`name: ${this.name}, age: ${this.age}`);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类构造函数
this.age = age;
}
}
const child = new Child('simahe', 18);
child.say(); // name: simahe, age: 18
console.log(child.age); // 18
总结
选择对象的创建方式时,考虑代码的可读性、可维护性以及扩展性是非常重要的。
- 简单对象:字面量方式、工厂函数
- 多个实例,构造函数、
Class
- 需要继承:
Object.create()
、Class