概述
- JS所有其他的对象都继承自
Object对象,即那些对象都是Object的实例。
Object()
Object()方法用于将任意值转为对象。
Object(1) // Number {1}
Object(true) // Boolean {true}
Object("abc") // String {'abc'}
Object(null) // {}
Object(undefined) // {}
var arr = [];
Object(arr) === arr; // true
var obj = {};
Object(obj) === obj; // true
如果参数是
原始值(number/boolean/string),则会将其转为对应的包装对象的实例。
如果参数是引用值(object/array),则总是会返回该对象,不用转换。
判断数据是否为对象
function isObj(value){
return value === Object(value);
}
isObj([]) // true 传入的是对象,直接返回该对象,本来就是一个对象,所以严格相等
isObj(1) // false 传入的是原始值,会被转为对象,所以不严格相等
构造函数
Object可以当作构造函数使用,使用new命令。可通过它来创建一个新对象。- 通过
字面量方式创建的对象等同于使用构造函数创建的对象,详见《数据类型—对象》一章。
var obj = new Object();
var obj = {};
// 这两种相同,只是后者更简便
var o = {a: 1};
var o2 = new Object(o);
o === o2 // true
var n = new Object(1);
n // Number {1}
如果参数是
原始值(number/boolean/string),则会将其转为对应的包装对象的实例。
如果参数是引用值(object/array),则总是会返回该对象,不用转换。
属性描述对象
- JS提供了一个内部数据结构用来
描述对象的属性,控制它的行为。比如该属性是否可写、可遍历等。每个属性都有自己对应的属性描述对象。 属性描述对象的各个属性称为元属性,因为它们可以看作是控制对象属性的属性。元属性如下:
value 属性值
- 该属性的属性值,默认为
undefined。
var obj = {};
obj.p = 123;
Object.getOwnPropertyDescriptor(obj, 'p').value // 123 读取 p 的 value
Object.defineProperty(obj, 'p', {value: 234}); // 重新设置 p 的 value
obj.p // 234
writable 可被改变
boolean值,表示该属性值是否可被改变,默认为true。
var obj = {}
Object.defineProperty(obj, 'a', {
value: 123,
writable: false
});
obj.a // 123
obj.a = 0
obj.a // 123 未改变
严格模式下给
writable为false的属性赋值会报错。
enumerable 可遍历
boolean值,表示该属性是否可遍历,默认为true。如果为false则会导致for in循环、Object.keys()、JSON.stringify跳过该属性。
var obj = Object.defineProperty({}, 'a', {
value: 123,
enumerable: false
});
obj.a // 123
for (const key in obj) {
console.log(key) // 空
}
Object.keys(obj) // []
JSON.stringify(obj) // '{}'
Object.getOwnPropertyNames(obj) // ['a'] 不管属性是否可遍历都可以获取
configurable 可配置性
boolean值,表示该属性的可配置性(是否可以修改元属性),默认为true。如果为false,则writable、enumerable和configurable都不能被修改。
var obj = Object.defineProperty({}, 'a', {
value: 123,
writable: false,
enumerable: false,
configurable: false
});
obj.a // 123
Object.defineProperty(obj, 'a', {writable: true}) // Uncaught TypeError: Cannot redefine property: a
// writable元属性只有从false改为true才会报错,从true改为false则允许
Object.defineProperty(obj, 'a', {enumerable: true}) // Uncaught TypeError: Cannot redefine property: a
Object.defineProperty(obj, 'a', {configurable: true}) // Uncaught TypeError: Cannot redefine property: a
修改
value属性的情况比较特殊。只要writable和configurable有一个为true,就允许改动value。
configurable决定了目标属性是否可以被删除(delete)。
set get 存取器
- 除了直接定义
value外,属性还可以用存取器定义getter/setter,一旦定义了存取器,那么存取的时候都将执行对应的函数。
// 写法一
var obj = Object.defineProperty({}, 'a', {
get: function(){
return 'getter'
},
set: function(value){
console.log('setter' + value);
}
});
obj.a // 'getter' 读取时执行的是get函数返回值
obj.a = 1 // 'setter1' 赋值时执行的是get函数
// 写法二(推荐)
var obj = {
get p() {
return 'getter';
},
set p(value) {
console.log('setter: ' + value);
}
};
写法一属性
a的configurable和enumerable都为false,从而导致属性a不可遍历。
写法二属性a的configurable和enumerable都为true,因此属性a可遍历。
静态方法
Object.keys(obj)
- 用来遍历对象的属性,返回一个数组。该数组的成员都是该对象
自身的(不包括继承的)所有可遍历的属性名。
var obj = {
a: 1,
b: 2,
}
Object.keys(obj); // ['a', 'b']
Object.getOwnPropertyNames(obj)
- 用来遍历对象的属性,返回一个数组。该数组的成员都是该对象
自身的(不包括继承的)所有属性名(包括不可遍历的)。
var obj = {
a: 1,
b: 2,
}
Object.getOwnPropertyNames(obj); // ['a', 'b']
Object.getOwnPropertyDescriptor(obj, prop)
-
- 获取对象某个属性的
属性描述对象,只能获取对象自身属性,不能获取继承的属性。
- 获取对象某个属性的
var obj = {a: 1};
Object.getOwnPropertyDescriptor(obj, 'a')
// {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptors(obj)
- 获取对象所有属性的
属性描述对象,只能获取对象自身属性,不能获取继承的属性。
var obj2 = {a: 1, b: 1}
Object.getOwnPropertyDescriptors(obj)
// {
// a: {value: 1, writable: true, enumerable: true, configurable: true}
// b: {value: 1, writable: true, enumerable: true, configurable: true}
// }
Object.defineProperty(obj, prop, propDescObj)
- 通过属性描述对象定义或修改一个属性,返回修改后的对象。
var obj = Object.defineProperty({}, 'p', {
value: 123,
writable: true,
enumerable: true,
configurable: true
});
obj.p // 123
Object.defineProperties(obj, {prop: {propDescObj}})
- 通过属性描述对象定义或修改多个属性,返回修改后的对象。
var obj = Object.defineProperties({}, {
a: {
value: 1,
writable: true,
},
b: {
value: 2,
enumerable: false
},
c: {
get: function(){
return 3
}
}
})
obj // {a: 1, b: 2}
obj.c // 3
上述两个方法注意项:
- 如果要定义或修改的属性已存在,则相当于更新该属性的属性描述对象。
- 定义了
get()或set()时,不能将writable属性设为true或者同时定义value属性,否则会报错。writable、configurable、enumerable这三个属性的默认值都为false。
Object.preventExtensions(obj)
冻结!使一个对象无法再添加新的属性。
var obj = {};
Object.preventExtensions(obj);
Object.defineProperty(obj, 'p', {
value: 'hello'
});
// TypeError: Cannot define property:p, object is not extensible.
obj.p = 1; // 这种赋值不会报错,但也赋值不上
obj.p // undefined
可修改冻结前的原有属性,除非原属性的
writable为false。
Object.isExtensible()
- 是否可以为一个对象添加属性,返回
boolean。使用了Object.preventExtensions()方法返回false,未使用返回true。
var obj = {}
Object.isExtensible(obj); // true 可以添加属性
Object.preventExtensions(obj);
Object.isExtensible(obj); // false 不可以添加属性
Object.seal()
冻结!使一个对象既无法添加新属性也无法删除旧属性,本质是把属性描述对象的configurable属性设为false。
var obj = {a: 1};
Object.seal(obj);
delete obj.a // false 无法删除
obj.a // 1
obj.b = 2
obj.b // undefined 无法添加新属性
Object.isSealed(obj)
- 检查一个对象是否使用了
Object.seal()。
var obj = {a: 1};
Object.isSealed(obj); // false
Object.seal(obj);
Object.isSealed(obj); // true
Object.isExtensible(obj); // false 不可以给对象添加新属性
可修改冻结前的原有属性,除非原属性的
writable为false。
Object.freeze(obj)
- 冻结!使一个对象无法添加新属性、无法删除旧属性、无法修改属性值,变成了一个
常量。
var obj = {a: 1};
Object.freeze(obj);
obj.a = 2;
obj.a // 1 无法修改
obj.b = 2;
obj.b // undefined 无法添加
delete obj.a; // false 无法删除
obj // {a: 1}
修改、添加、删除操作在
严格模式下会报错。
Object.isFrozen(obj)
- 检查一个对象是否使用了
Object.isFrozen()。
var obj = {
p: 'hello'
};
Object.freeze(obj);
Object.isFrozen(obj) // true 已被冻结
Object.isSealed(obj) // true 已被冻结
Object.isExtensible(obj) // false 不可以添加新属性
实例方法
- 除了静态方法还有许多方法定义在
Object.prototype对象上,它们称为实例方法,所有Object的实例对象都继承了这些方法。
Object.prototype.valueOf()
- 返回一个对象的
值,默认情况下返回对象本身。
var obj = new Object();
obj.valueOf() === obj; // true
valueOf()方法的主要用途是JS自动类型转换时会默认调用。
var obj = {};
1 + obj // 1[object Object]
// 重写valueOf
Object.prototype.valueOf = function(){
return 2;
}
var obj2 = {};
1 + obj2 // 3
Object.prototype.toString()
- 返回一个对象的
字符串形式,默认情况下返回类型字符串。
var obj = {};
obj.toString() // '[object Object]'
- 通过自定义
toString方法,可以让对象在自动类型转换时得到想要的字符串。
Object.prototype.toString = function(){
return 'hello world!'
}
var obj = {};
obj.toString() // 'hello world!'
数组、字符串、函数、Date都分别重写了Object.prototype.toString。
[1,2,3].toString() // '1,2,3'
'123'.toString() // '123'
(function test(){return 1}).toString() // '(function test(){return 1}).toString()'
// 首尾添加()是为了变成表达式
(new Date()).toString() // 'Sat Feb 17 2024 19:45:19 GMT+0800 (中国标准时间)'
toString()的应用
- 此方法会返回对象的
类型字符串,因此可以用来判断一个值的数据类型。但由于实例对象有可能会重写此方法,所以通过call方法直接调用。
Object.prototype.toString.call(2) // "[object Number]"
Object.prototype.toString.call('') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(Math) // "[object Math]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
- 封装一个判断值的数据类型的方法。
var getValueType = function (value){
var str = Object.prototype.toString.call(value);
return str.match(/\[object (.*?)\]/)[1].toLowerCase();
};
getValueType(1); // 'number'
getValueType({}); // 'object'
getValueType([]); // 'array'
getValueType(function(){}) // 'function'
Object.prototype.toLocaleString() (不完整)
- 返回一个值的
类型字符串,与toString()返回结果相同。
var obj = {};
obj.toString(obj) // "[object Object]"
obj.toLocaleString(obj) // "[object Object]"
- 主要作用是留出一个接口,让各种不同的对象实现自己版本的
toLocaleString(),用来返回针对某些地域的特定的值。 - 目前有三个对象重写了
toLocaleString方法:Array、Number、Date。
var date = new Date();
date.toString() // 'Sat Feb 17 2024 20:03:23 GMT+0800 (中国标准时间)'
date.toLocaleString() // '2024/2/17 20:03:23'
Object.prototype.hasOwnProperty(prop)
- 返回一个
boolean,判断该实例对象自身(不包括继承的)是否有该属性。
var obj = {a: 1};
obj.hasOwnProperty('a') // true
obj.hasOwnProperty('b') // false
obj.hasOwnProperty('toString') // false 继承自Object.prototype
Object.prototype.propertyIsEnumerable()
- 返回一个
boolean,判断某个属性是否可遍历。只能判断对象自身属性,继承的属性一律返回false。
var obj = Object.defineProperty({}, 'a', {
value: 1,
enumerable: false
})
obj.propertyIsEnumerable('a') // false
obj.propertyIsEnumerable('toString') // false 继承的属性
冻结方法的局限
- 局限一:可以通过改变原型对象来为对象增加属性,其中一种解决方案是将
原型也给冻结。
var obj = new Object();
Object.preventExtensions(obj);
var proto = Object.getPrototypeOf(obj);
proto.a = 1;
obj.a // 1 原型上新增了属性
// 冻结原型
Object.preventExtensions(proto);
proto.b = 1;
proto.b // undefined 无法新增
- 局限二:如果属性值是对象,那么只能冻结对象的内存地址,而不能冻结对象本身的内容。
var obj = {
arr: [1,2,3]
}
Object.freeze(obj);
obj.arr.push(4);
obj.arr // [1, 2, 3, 4] 添加成功,无法冻结对象本身内容