1. 对象属性操作
1.1 定义属性
Object.defineProperty( obj, property, detail )
一次只能定义一个属性
传递三个参数
- 参数1: 需要进行定义属性的对象
- 参数2: 进行定义的属性名
- 参数3: 属性描述符,定义属性相关的配置信息
- 参数 3 的配置信息
- value: 定义属性的值
- configurable: 设置该属性是否可以被删除,默认是 false,即不可以被删除
- writable: 设置该属性是否可以被修改,默认是 false,即不可以被修改
- enumerable: 设置该属性是否可以被枚举,默认是 false,即不可以被枚举
注意,使用这种方式定义的属性,默认值都是 false。但是,使用字面量声明的对象,默认值都是 true,即可删,可改,可枚举
var obj = {a: 1};
Object.defineProperty(obj, 'b', {
value: 3,
configurable: false
})
delete obj.b
console.log(obj); // { a: 1, b: 3 } 无法删除
var obj = {a: 1};
Object.defineProperty(obj, 'b', {
value: 3,
writable: false
})
obj.b = 4
console.log(obj); // { a: 1, b: 3 } 无法修改
var obj = {a: 1};
Object.defineProperty(obj, 'b', {
value: 3,
enumerable: false
})
for(var property in obj) {
console.log(property); // a 无法枚举
}
Object.defineProperties( obj, detail )
可以一次性定义多个属性
- 参数1: 需要进行定义属性的对象
- 参数2: 需要进行定义的属性及其详细信息,传递的值是一个对象
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
configurable: true,
writable: true,
enumerable: true
},
b: {
value: 4,
configurable: false,
writable: false,
enumerable: false
}
})
1.2 属性存取器
get 和 set,可以代替 value 和 writable
var obj = {};
Object.defineProperties(obj, {
a: {
set: function (v) {
console.log('设置 a 属性值')
this._a = v;
},
get: function () {
console.log('获取 a 属性值')
return this._a;
},
// 访问器不能和 value 或者 writable 混在一起用,否则就会报错
// 因为访问器就代替了这两个功能
enumerable: true,
configurable: true
}
})
console.log(obj.a); // 先输出 '获取 a 属性值',再输出 undefined
obj.a = 3; // 输出 '设置 a 属性值'
console.log(obj.a); // 先输出 '获取 a 属性值',再输出 3
属性描述符只能在Object.defineProperty或Object.defineProperties中使用。
1.3 枚举属性
循环遍历对象中的可枚举属性,依次输出 for( property in obj ) { property… }
不可枚举属性,为继承于原型中的属性和直接定义为不可枚举的属性
基本使用1:
var obj = {a: 1, b: 2}
for(x in obj) {
console.log(x); // 依次输出 a, b, 类型为 字符串
}
for(x in obj) {
console.log(obj.x); // 依次输出 undefined, undefined,无法通过 . 来获取对应的属性值
}
for(x in obj) {
console.log(obj[x]); // 依次输出 1, 2,可以通过 [] 的方式来读取对应的属性
}
基本使用2:
var o = {
a: 1, b: 2
}
// 以 o 为原型创建 obj,其中 o 的属性均可枚举
var obj = Object.create(o);
// 设置 obj 自身可枚举的属性
obj.c = 3;
for(var prop in obj) {
console.log(prop);
}
// c, a, b 先将自身的可枚举属性进行遍历,再对原型中的可枚举属性进行遍历
2. 对象常用方法
2.1 复制与合并
Object.assign():用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.assign(target, …sources)
传递参数数量不定,可以传递 2个 到 多个,参数之间使用 , 隔开
- 改变了目标对象,并返回目标对象
- 参数1:target,目标对象
- 剩余参数:需要进行复制的多个源对象
2.1.1 复制
var target = {};
var objSource = { a: 1 };
var copy = Object.assign(target, objSource);
console.log(copy); // { a: 1 }
console.log(target); // { a: 1 }
console.log(target === copy); // true
将想要的属性合并进空对象中,相当于复制
2.1.2 合并
var o1 = { a: 1 };
var o2 = { a: 2, b: 2 };
var o3 = { a: 3, c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 3, b: 2, c: 3 }
console.log(o1); // { a: 3, b: 2, c: 3 }, 注意目标对象自身也会改变。
console.log(o1 === obj); // true
如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。
2.1.3 复制或合并异常(重要)
继承属性和不可枚举属性是不能拷贝的
var obj = Object.create({foo: 1}, { // foo 是个继承属性。
bar: {
value: 2 // bar 是个不可枚举属性。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});
var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
原始类型会被包装为 object,之后在进行相应的操作
var v1 = "abc";
var v2 = true;
var v3 = 10;
var v4 = Symbol("foo")
var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串才能包装成为具有自身可枚举属性的对象。
// 其他的原始类型虽然也能被包装成为对象,但是不具备可枚举属性,所以也不会被复制或合并
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
异常会打断接下来的拷贝任务
<script>
var target = Object.defineProperty({}, "foo", {
value: 1,
writable: false
}); // target 的 foo 属性是个只读属性。
Object.assign(target, { bar: 2 }, { fun: 3, foo: 4, baz: 5}, {a: 6});
// TypeError: "foo" is read-only
// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。
</script>
<script>
// 将输出语句放在第二个 script 标签中,会跳过方法错误,能够正常输出对应的值
console.log(target.bar); // 2,说明第一个源对象拷贝成功了。
console.log(target.fun); // 3,说明第二个源对象的第一个属性也拷贝成功了。
console.log(target.foo); // 1,error,只读属性不能被覆盖,返回的值仍是复制前的值。所以第二个源对象的第二个属性拷贝失败了。
console.log(target.baz); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。
console.log(target.a); // undefined,第三个源对象更是不会被拷贝到的。
</script>
2.2 使用指定的原型对象及其属性去创建一个新的对象
Object.create():会使用指定的原型对象及其属性去创建一个新的对象。
Object.create(proto, [ propertiesObject ])
- 参数1:被指定为新创建的对象的原型对象
- 参数2:可选,传递值为一个对象。该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与Object.defineProperties()的第二个参数一样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。 相当于类似原型对象。
- 抛出异常:如果 propertiesObject 参数不是 null 也不是对象,则抛出一个 TypeError 异常。
基本使用1:
var obj = {
a: 1, b: 2
}
var baz = Object.create(obj);
console.log(baz); // {} 空对象
console.log(baz.a); // 1,可以访问到原对象中的属性,则说明,原对象就是新对象的原型
基本使用2:
var o;
o = Object.create(Object.prototype, {
// p会成为新创建对象的数据属性,省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
p: { value: 42 },
q: { value: 12, enumerable: true, writable: true, configurable: true },
// foo会成为新创建对象的数据属性
foo: { value: "hello", writable: true, configurable: true },
// bar会成为新创建对象的访问器属性
bar: {
configurable: false,
get: function () { return 10 },
set: function (value) { console.log("Setting `o.bar` to", value) }
}
})
console.dir(o); // 访问器属性不能通过 log 查看到,需使用 dir,但是能正常使用
console.log(o.foo); // hello
console.log(o.bar); // 10
o.p = 24
console.log(o.p) //42,不可修改
for (var prop in o) {
console.log(prop);
// q, 由于剩余其他属性均未设置 enumerable 属性,默认为 false,不可枚举
}
console.log(delete o.p) // false, 删除失败
基本使用3:构造函数
function Shape() {
this.x = 0;
this.y = 0;
}
// 父类的原型方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// 子类
function Rectangle() {
Shape.call(this); // 调用构造函数
}
// 子类继承父类
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?', rect instanceof Rectangle);// true
console.log('Is rect an instance of Shape?', rect instanceof Shape);// true
rect.move(1, 1); // Outputs, 'Shape moved.'
2.3 遍历自身可枚举属性
Object.keys():会返回一个由一个给定对象的自身可枚举属性组成的数组。数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。
- 参数:传递一个对象
- 返回值:一个表示给定对象的所有可枚举属性的字符串数组。
基本使用1:
var o = {
a: 1, b: 2
}
// 以 o 为原型创建 obj,其中 o 的属性均可枚举
// 再添加一些不可枚举属性
var obj = Object.create(o, {
c: { value:3 }
});
// 设置 obj 自身可枚举的属性
obj.d = 4;
var arr = Object.keys(obj);
console.log(arr);
// ['d'] 只会将自身的可枚举属性进行遍历,将遍历到的属性名放置在数组中
基本使用2:遍历包装成为具有自身可枚举属性的对象,一般为类数组
/* 类数组对象 */
// new Object('abc')
var str_obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(str_obj));
// console: ['0', '1', '2']
/* 具有随机键排序的数组类对象 */
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(an_obj));
// console: ['2', '7', '100'],遍历的属性名会按照正常顺序排列
2.4 其他一些不是很常用的对象操作属性。如下:
- Object.freeze() 方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。如果某个属性的值是一个对象时,该对象是可以改变的,即次冻结方法只能进行浅冻结。
- Object.isFrozen() 方法判断一个对象是否被冻结(frozen)。
- Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
- Object.isSealed() 方法判断一个对象是否是密封的(sealed)。
- Object.seal() 方法可以让一个对象密封,并返回被密封后的对象。密封对象是指那些不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性,但可能可以修改已有属性的值的对象。