前言
JavaScript中的所有对象都来自 Object
;所有对象从Object.prototype
继承方法和属性,当然它们可能被覆盖。本文主要介绍创建对象的几种方式。
1. 对象字面量{…}
对象字面量的方式是最常用的方式之一,它用内含属性的花括号{...}
快速创建对象。
var obj1 = {};
obj1.name = "Tom";
var obj2 = { name: "Tom", age: 12 };
var name = "Tom", age = 12;
var obj3 = { name: name, age: age };
// ES2015中,属性名和变量名相同时可简写为:
var obj3 = { name, age };
// 扩展属性,ES2018新特性,可用于克隆或合并对象,浅拷贝,不包括原型
var obj4 = { ...obj3 };
以字面量方式创建的对象属性默认是可写,可枚举和可配置的
对象的原型默认为Object.prototype
。通过定义属性__proto__
(只能使用冒号标记的属性定义)的值来变更原型。只有给出的值是对象或null
,对象的原型才会被设置为给出的值,否则原型不会改变。
var obj1 = {};
Object.getPrototypeOf(obj1) === Object.prototype; // true
var obj2 = { __proto__: null };
Object.getPrototypeOf(obj2) === null; // true
var __proto__= {};
var obj3 = { "__proto__": __proto__ };
Object.getPrototypeOf(obj3) === __proto__; // true
// 不使用冒号标记的属性定义,不会变更对象的原型,只是名字为__proto__的普通属性
var obj4 = { __proto__ };
Object.getPrototypeOf(obj4) === __proto__; // false
obj4.hasOwnProperty("__proto__"); // true
Object.getPrototypeOf(obj4) === Object.prototype; // true
var obj5 = { __proto__: "not an object or null" };
obj5.hasOwnProperty("__proto__"); // false
Object.getPrototypeOf(obj5) === Object.prototype; // true
2. new Object()
new Object([value])
参数value
是任意类型的可选参数。
- 如果
value
值是null
或undefined
或不传时,则会创建并返回一个空对象; - 如果
value
值是一个基本类型,则会构造其包装类的对象; - 如果
value
值是引用类型,则仍然返回这个值。
var obj1 = new Object(); // {}
var obj2 = new Object(undefined); // {}
var obj3 = new Object(null); // {}
// 基本类型,构造其包装类对象
var obj4 = new Object("Tom"); // String {"Tom"}, 相当于 new String("Tom");
Object.getPrototypeOf(obj) === String.prototype; // true
var arr = [1, 2];
var obj5 = new Object(arr);
obj5 === arr; // true
var obj6 = { name: "Tom", age: 12 };
var obj7 = new Object(obj6);
obj7 === obj6; // true
3. Object.create()
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
。
/**
* 创建一个具有指定原型的对象,并且包含指定的属性。
* @param o 新创建对象的原型对象。可能为空
* @param properties 包含一个或多个属性描述符的 JavaScript 对象。
*/
create(o: object | null, properties?: PropertyDescriptorMap): any;
interface PropertyDescriptorMap {
[s: string]: PropertyDescriptor;
}
interface PropertyDescriptor {
configurable?: boolean;
enumerable?: boolean;
value?: any;
writable?: boolean;
get?(): any;
set?(v: any): void;
}
var obj1 = Object.create(null);
Object.getPrototypeOf(obj1) === null; // true
var proto= {};
var obj2 = Object.create(proto);
Object.getPrototypeOf(obj2) === proto; // true
var obj3 = Object.create({}, { p: { value: 42 } });
// 属性描述对象中省略了的属性默认为false,所以p是不可写,不可枚举,不可配置的
Object.getOwnPropertyDescriptors(obj3); // p: {value: 42, writable: false, enumerable: false, configurable: false}
//创建一个可写的,可枚举的,可配置的属性p
var obj4 = Object.create({}, {
p: { value: 42, writable: true, enumerable: true, configurable: true }
});
//不能同时指定访问器(get和set)和 值或可写属性
var obj4 = Object.create({}, {
p: {
// value: 42, // 不能和get set同时存在
// writable: true, // 不能和get set同时存在
enumerable: true,
configurable: true,
get: function() { return 10 },
set: function(value) { console.log("Setting `p` to", value); }
}
});
点击此链接查看如何使用Object.create实现类式继承
4. Object.assign()
Object.assign()
方法并不是直接用来创建对象的,但它可以达到创建对象的效果,所以这里把它也作为一种创建对象的方式。
Object.assign()
方法用于将所有自身的可枚举属性的值从一个或多个源对象复制到目标对象。返回目标对象。
Object.assign(target, …sources)
- 如果目标对象或源对象中具有相同的属性,后面的对象的属性将覆盖前面的对象的属性。
- 只会拷贝源对象自身的可枚举属性到目标对象。对源对象原型上的对象不做处理。
- 该方法使用源对象的
Get
和目标对象的Set
来获取和设置值。
var o1 = { name: "Tom" };
var o2 = { name: "Jerry" };
var o3 = Object.create(o2, { // o2是o3的原型,name: "Jerry"是原型上的属性
a: { value: 42 }, // 不可枚举
b: { value: 42, writable: false, enumerable: true, configurable: false },
c: { enumerable: true, get: function() { return 10; } }
});
var obj1 = Object.assign(o1, o2);
obj1 === o1; // true
obj1; // {name: "Tom", b: 42, c: 10}
Object.getOwnPropertyDescriptors(obj1); // 不会拷贝属性的
/* b: {value: 42, writable: true, enumerable: true, configurable: true}
c: {value: 10, writable: true, enumerable: true, configurable: true}
name: {value: "Tom", writable: true, enumerable: true, configurable: true} */
var o4 = { a: "a", b: { name: "Tom", age: 18 } };
var obj2 = Object.assign({}, o4);
obj2.b === o4.b; // true, 浅拷贝,如果源值是一个对象的引用,它仅仅会复制其引用值。
// 合并对象,后面属性覆盖前面属性
var o1 = { a: 1, b: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { a: 3 };
var obj3 = Object.assign({}, o1, o2, o3);
obj3; // {a: 3, b: 2, c: 2}
// 基本类型会被转为包装对象,只有字符串的包装对象有自身可枚举属性。
var obj4 = Object.assign({}, "abc", null, true, undefined, 10, Symbol("foo"));
obj4; // {0: "a", 1: "b", 2: "c"}
// 拷贝过程中发生异常,会终止后续拷贝任务,已拷贝的数据保留
var t = Object.create( {}, { b: { value: 42, writable: false } }); // b是只读属性
Object.assign(t, {a: 1}, {a: 2, b: 2, c: 3}, {c: 4}); // Cannot assign to read only property 'b' of object '#<Object>'
t; // {a: 2, b: 42}