1、定义
Object 是 JavaScript 的一种 数据类型 ,用花括号 {}
表示。
它用于存储各种键值集合和更复杂的实体。
Objects 可以通过 构造函数 或者使用 对象字面量 的方式创建。
2、描述
在 JavaScript 中,几乎所有的对象都是 Object 类型的实例,它们都会从 Object.prototype 继承属性和方法,虽然大部分属性都会被覆盖(shadowed)或者说被重写了(overridden)。
除此之外,Object 还可以被故意的创建,但是这个对象并不是一个“真正的对象”(例如:通过 Object.create(null)
),或者通过一些手段改变对象,使其不再是一个“真正的对象”(比如说:Object.setPrototypeOf
)。
通过原型链,所有的 Object 都能观察到 Object 原型对象(Object prototype object)的改变,除非这些受到改变影响的属性和方法沿着原型链被进一步的重写。
尽管有潜在的危险,但这为覆盖或扩展对象的行为提供了一个非常强大的机制。
Object 构造函数为给定的参数创建一个包装类对象(object wrapper),具体有以下情况:
- 如果给定值是 null 或 undefined,将会创建并返回一个空对象
- 如果传进去的是一个基本类型的值,则会构造其包装类型的对象
- 如果传进去的是引用类型的值,仍然会返回这个值,经他们复制的变量保有和源对象相同的引用地址(传址)
当以非构造函数形式(字面量)被调用时,Object 的行为等同于 new Object()
。
2.1、对象初始化
2.1.1、定义
对象初始化是一个描述对象初始化过程的表达式。
对象初始化是由一组描述对象的属性组成,属性的值可以是原始类型,也可以是其他对象。
2.1.2、创建对象
可以通过下面三种方法初始化对象:
new Object()
Object.create()
(不常用)- 字面量
// 创建一个空对象
var obj = new Object();
var obj = Object.create();
var obj = {};
// 创建一个带初始值的对象
var obj = new Object({ a: 1 });
var obj = Object.create({ a: 1 });
var obj = { a: 1 };
// 如果键和值相同,则可以使用简写方式
var a = 1;
var obj = {
// 基础形式
// a: a
// 简写形式
a
}
2.1.3、属性访问
可以通过下面两种方式访问对象的属性:
- 小数点
- 方括号里面带字符串
var obj = {
a: 1
}
console.log(obj.a); // 1
console.log(obj['a']); // 1
那么这两种方式有什么区别呢?
需要知道,对象的键都是字符串,为了精简代码一般都不会加引号,怎么证明呢?可以通过下述代码证明:
var obj = {
a: 1
}
// Object.keys():返回对象的所有键的数组
var arr = Object.keys(obj);
console.log(arr); // ["a"]
然而在一些特殊的情况下,对象的键并不是一个简单的字符串,可能会带一些字符,此时,键就必须用引号包裹:
var obj = {
"a-b": 1
}
console.log(obj.a-b); // 报错
// 此时就需要用到方括号
console.log(obj["a-b"]); // 1
2.1.4、属性定义
直接 对象实例.属性名
就可以完成属性的定义。(如果对象没有该属性则添加,如果对象有该属性则修改)
var obj = {}
obj.a = 1;
console.log(a); // 1
2.1.5、重复属性名
属性使用了同样的名称时,后面的属性会覆盖前面的属性。、
var obj = {
x: 1,
x: 2
}
console.log(obj); // { x: 2 }
2.1.6、对象字面量表示法和JSON
区别:
- JSON 只允许
key: value
的形式,且属性名必须用双引号括起来,属性定义不允许使用简写形式 - JSON 中,不允许将值设置为函数
2.2、删除属性
Object 自身没有提供方法删除其自身属性,为了删除对象上的属性,必须使用 delete 操作符。
var obj = {
a: 1
}
delete obj.a;
console.log(obj); // {}
3、构造函数
Object 构造函数将输入转换为一个对象。其行为取决于输入的类型。
- 如果给定的值是 null 或 undefined,它会创建并返回一个空对象
- 否则,它将返回一个和给定的值相对应的类型的对象
- 如果给定值是一个已经存在的对象,则会返回这个值
备注: Object() 可以使用 new 关键字调用,也可以不使用。两者都会创建一个新的对象。
3.1、语法
// new object():创建一个空对象
var obj = new Object();
// new Object({key: value}):创建一个带初始值的对象
var obj = new Object({a: 1});
通过对象构造函数创建一个空对象的三种方式:
var obj = new Object();
var obj = new Object(null);
var obj = new Object(undefined);
4、方法
4.1、Object.assign(target, …sources)
Object.assign(target, ...sources)
方法将所有可枚举的自有属性从一个或多个源对象复制到目标对象。(返回修改后的对象)
- target:目标对象,接收源对象属性的对象,也是修改后的返回值
- sources:源对象,包含将被合并的属性
var target = { a: 1, b: 2 };
var source = { b: 4, c: 5 };
var returnedTarget = Object.assign(target, source);
console.log(target); // { a: 1, b: 4, c: 5 }
console.log(returnedTarget); // { a: 1, b: 4, c: 5 }
console.log(returnedTarget === target); // true
如果赋值期间出错,例如如果属性不可写,则会抛出 TypeError;如果在抛出异常之前添加了任何属性,则会修改 target 对象。
备注: Object.assign() 不会在 source 对象值为 null 或 undefined 时抛出错误。
需要注意的是,Object.assign(target, ...sources)
只赋值属性值(浅拷贝)。
var obj1 = {
a: 1,
b: {
c: 2
}
}
var obj2 = Object.assign({}, obj1);
// 修改基本数据类型
obj1.a = 2;
console.log(obj1.a); // 2
console.log(obj2.a); // 1
// 修改复杂数据类型
obj1.b.c = 3;
console.log(obj1.b.c); // 3
console.log(obj2.b.c); // 3
那么对象如何实现深拷贝呢?点击这里查看
4.2、Object.create(proto)
Object.create(proto)
方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型。(返回一个新对象)
- proto:新创建对象的原型对象
var obj = Object.create({ a: 1 });
console.log(obj); // { a: 1 }
注意:proto 参数需为 null 或 除基本类型包装对象以外的对象,否则抛出一个 TypeError 异常。
4.2.1、创建以 null 为原型的对象
以 null 为原型的对象存在不可预期的行为,因为它未从 Object.prototype 继承任何对象方法。
// 创建一个null对象
var nullObj = Object.create(null);
nullObj.toString(); // nullObj.toString is not a function
但是可以为以 null 为原型的对象添加方法,如下所示:
var nullObj = Object.create(null);
// 将 Object 原型链上的 toString 方法添加到 nullObj 上
nullObj.toString = Object.prototype.toString;
console.log(nullObj.toString()); // [object Object]
创建以 null 为原型的对象也可以防止原型污染攻击。如果恶意脚本向 Object.prototype 添加了一个属性,这个属性将能够被程序中的每一个对象所访问,而以 null 为原型的对象则不受影响。
4.2.2、实现类式继承
// 创建父元素
function Shape() {
this.x = 0;
this.y = 0;
}
// 为父元素的原型添加 move 方法
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);
4.3、Object.defineProperty(obj, prop, descriptor)
Object.defineProperty(obj, prop, descriptor)
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性。(返回此对象)
- obj:要定义属性的对象
- prop:要定义或修改的属性的名称或 Symbol
- descriptor:要定义或修改的属性描述符
备注: 应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。
Object.defineProperty(obj, 'prop', {
value: 42,
writable: false
});
备注: 在 ES6 中,由于 Symbol 类型的特殊性,用 Symbol 类型的值来做对象的 key 与常规的定义或修改不同,而 Object.defineProperty 是定义 key 为 Symbol 的属性的方法之一。
该方法允许精确地添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,在枚举对象属性时会被枚举到(for…in 或 Object.keys方法),可以改变这些属性的值,也可以删除这些属性。
默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。
- 数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的
- 存取描述符是由 getter 函数和 setter 函数所描述的属性
- 一个描述符只能是这两者其中之一;不能同时是两者
这两种描述符都是对象。
它们共享以下可选键值(默认值是指在使用 Object.defineProperty() 定义属性时的默认值):
- configurable:设置该属性的描述符是否能够被改变,以及该属性能否被删除(默认为 false,即不能被改变)
- enumerable:设置该属性是否会出现在对象的枚举属性中(默认为 false,即不会出现在枚举中)
数据描述符还具有以下可选键值:
- value:该属性的默认值(默认为 undefined)
- writable:设置该属性能否被赋值运算符改变(默认为 false,即不能)
存取描述符还具有以下可选键值:
- get:属性的 getter 函数(默认为 undefined)
- 当访问该属性时,会调用此函数
- 执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的 this 并不一定是定义该属性的对象)
- 该函数的返回值会被用作属性的值
- set:属性的 setter 函数(默认为 undefined)
- 当属性值被修改时,会调用此函数
- 该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象
- 如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符
- 如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(obj, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 对象 o 拥有了属性 a,值为 37
// 在对象中添加一个设置了存取描述符属性的示例
Object.defineProperty(obj, "b", {
// get : function() { return bValue; },
// set : function(newValue) { bValue = newValue; },
// 可以缩写为:
get() {
return console.log('b属性被获取了');
},
set(newValue) {
console.log('b属性被改变了');
},
enumerable : true,
configurable : true
});
当试图改变不可配置属性(除了 value 和 writable 属性之外)的值时,会抛出TypeError,除非当前值和新值相同,如下所示:
var obj = {};
Object.defineProperty(obj, 'a', {
value: 1
});
console.log(obj.a); // 1
Object.defineProperties(obj, 'a', {
get() {
console.log(obj.a);
}
}); // 报错
试图写入非可写属性不会改变它,也不会引发错误,如下所示:
var o = {};
Object.defineProperty(o, 'a', {
value: 1,
writable: false
});
console.log(o.a); // 1
o.a = 2;
console.log(o.a); // 1
4.4、Object.defineProperties(obj, props)
Object.defineProperties(obj, props)
方法直接在一个对象上定义新的属性或修改现有属性。(返回该对象)
- obj:在其上定义或修改属性的对象
- props:要定义其可枚举属性或修改的属性描述符的对象,对象中存在的属性描述符主要有两种:数据描述符和访问器描述符(和上面的
Object.defineProperty
一样)- configurable
- enumerable
- value
- writable
- get
- set
var obj = {};
Object.defineProperties(obj, {
'prop1': {
value: true,
writable: true
},
'prop2': {
value: 'Hello',
writable: false
}
});
4.5、Object.getOwnPropertyDescriptor(obj, prop)
Object.getOwnPropertyDescriptor(obj, prop)
方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
- obj:需要查找的目标对象
- prop:目标对象内属性名称
const obj = {
prop: 1
};
// 对象上存在该属性
const descriptor1 = Object.getOwnPropertyDescriptor(obj, 'prop');
console.log(descriptor1);
/*
Object {
configurable: true
enumerable: true
value: 1
writable: true
}
*/
// 对象上不存在该属性
const descriptor2 = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(descriptor2); // undefined
- 在 ES5 中,如果该方法的第一个参数不是对象(而是原始类型),那么就会产生出现 TypeError。
- 而在 ES2015,第一个的参数不是对象的话就会被强制转换为对象。
Object.getOwnPropertyDescriptor('obj', 'prop');
// 类型错误:"foo" 不是一个对象 // ES5 code
Object.getOwnPropertyDescriptor('obj', 'prop');
// Object returned by ES2015 code: {
// configurable: false,
// enumerable: true,
// value: "o",
// writable: false
// }
4.6、Object.getOwnPropertyDescriptors(obj)
Object.getOwnPropertyDescriptors(obj) 方法用来获取一个对象的所有自身属性的描述符。(所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象)
- obj:任意对象
const obj = {
prop: 1,
prop2: 2
};
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
/*
Object {
prop: {
configurable: true
enumerable: true
value: 1
writable: true
},
prop2: {
configurable: true
enumerable: true
value: 2
writable: true
}
}
*/
4.7、Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames(obj)
方法返回一个由指定对象的所有自身属性的属性名组成的数组。(包括不可枚举属性但不包括 Symbol 值作为名称的属性)
- obj:一个对象,其自身的可枚举和不可枚举属性的名称被返回
// 数组
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr)); // ["0", "1", "2", "length"]
// 对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj)); // ["0", "1", "2"]
注意:
Object.getOwnPropertyNames(obj)
方法不会获取原型链上继承的属性
function Father(){
this.id = 1;
}
function Son(){
this.age = 2;
}
Son.prototype = new Father();
console.log(Object.getOwnPropertyNames(new Son())); // ["age"]
4.7.1、只获取不可枚举的属性
function getNoenum(obj) {
// enum_and_nonenum:包含可枚举属性和不可枚举属性
var enum_and_nonenum = Object.getOwnPropertyNames(obj);
// enum_only:包含可枚举属性
var enum_only = Object.keys(obj);
// nonenum_only:包含不可枚举属性
var nonenum_only = enum_and_nonenum.filter(function(key) {
var indexInEnum = enum_only.indexOf(key);
if (indexInEnum == -1) {
// 没有发现在 enum_only 健集中意味着这个健是不可枚举的,
// 因此返回 true 以便让它保持在过滤结果中
return true;
} else {
return false;
}
});
}
- 在 ES5 中,如果参数不是一个原始对象类型,将抛出一个 TypeError 异常
- 在 ES2015 中,非对象参数被强制转换为对象
4.8、Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols()
方法返回一个给定对象自身的所有 Symbol 属性的数组。
- obj:要返回 Symbol 属性的对象
注意:因为所有的对象在初始化的时候不会包含任何的 Symbol,除非你在对象上赋值了 Symbol 否则
Object.getOwnPropertySymbols()
只会返回一个空的数组。
var obj = {};
console.log(Object.getOwnPropertySymbols(obj)); // Array []
var a = Symbol('a');
obj[a] = 1;
console.log(Object.getOwnPropertySymbols(obj)); // Array [Symbol(a)]
4.9、Object.getPrototypeOf(obj)
Object.getPrototypeOf()
方法返回指定对象的原型(内部[[Prototype]]属性的值)。
- obj:要返回其原型的对象
const obj = {
prop: 1
};
// 对象上存在该属性
const prot = Object.getPrototypeOf(obj);
console.log(prot); // Object:对象的原型属性
注意:如果对象没有继承属性,则返回 null。
const obj = Object.create(null);
console.log(Object.getPrototypeOf(obj)); // null
- 在 ES5 中,如果参数不是一个对象类型,将抛出一个TypeError异常
- 在 ES2015 中,参数会被强制转换为一个 Object
4.10、Object.prototype.hasOwnProperty(prop)
obj.hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
- prop:要检测的属性的 String 字符串形式表示的名称,或者 Symbol
var obj = {
a: 1
};
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('b')); // false
注意:即使属性的值设置为 null 或 undefined 也会返回 true。
var obj = {
a: null,
b: undefined
};
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('b')); // true
建议使用
Object.hasOwn()
替代Object.prototype.hasOwnProperty()
,因为前者更适用于用Object.create(null)
创建的对象。
4.11、Object.hasOwn(instance, prop)
Object.hasOwn(instance, prop)
方法判断对象是否存在自身的属性,返回布尔值。(若存在则返回 true,如果属性是继承的或者不存在则返回 false)
- instance:要测试的 JavaScript 实例对象
- prop:要测试属性的 String 类型的名称或者 Symbol
var obj = {
a: 1
};
console.log(Object.hasOwn(obj, 'a')); // true
console.log(Object.hasOwn(obj, 'b')); // false
4.11.1、和 in 的区别
const obj = {
a: 1
};
// Object.hasOwn
console.log(Object.hasOwn(obj, 'a')); // true
console.log(Object.hasOwn(obj, 'toString')); // false
// in
console.log('prop' in obj); // true
console.log('toString' in obj); // true
4.12、Object.setPrototypeOf(obj, prototype)
Object.setPrototypeOf()
方法设置一个指定的对象的原型到另一个对象或 null。(返回指定的对象)
建议在对象创建的时候就直接使用
Object.create()
方法来创建一个想要的对象原型,而不是之后通过Object.setPrototypeOf()
方法来更改对象的原型,原因是因为这个操作在各个浏览器和 JavaScript 引擎上都是一个效率很慢的操作。
- obj:要设置其原型的对象
- prototype:该对象的新原型(一个对象或 null)
在下面两种情况下会抛出 TypeError 异常:
- obj 参数是不可扩展的,或者它是一个不可修改原型的特异对象(exotic object),例如 Object.prototype 或 window
- prototype 参数不是对象或 null
如果 obj 参数不是一个对象(例如,数字、字符串,等),该方法将什么也不做。
function Human(name, level) {
this.name = name;
this.level = level;
}
function SuperHero(name, level) {
Human.call(this, name, level);
}
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
console.log(new SuperHero('a', 1).name); // 'a'
出于对性能的考虑,不建议使用
Object.setPrototype()
方法来替代类的继承extends
。
4.13、Object.keys(obj)
Object.keys(obj)
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。
- obj:要返回其枚举自身属性的对象
const obj= { 100: 'a', 2: 'b', 7: 'c' };
// 由于对象没有顺序,键的数组会进行排序
console.log(Object.keys(obj)); // console: ['2', '7', '100']
在 ES5 里,如果此方法的参数不是对象(而是一个原始值),那么它会抛出 TypeError。
在 ES2015 中,非对象的参数将被强制转换为一个对象。
// In ES5
Object.keys('foo'); // TypeError: "foo" is not an object
// In ES2015+
Object.keys('foo'); // ["0", "1", "2"]
4.14、Object.values(obj)
Object.values()
方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用 for…in 循环的顺序相同(区别在于 for-in 循环枚举原型链中的属性)。
- obj:被返回可枚举属性值的对象
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
// 返回值的数组与返回键的数组对应
console.log(Object.values(an_obj)); // ['b', 'c', 'a']
4.15、Object.entries(obj)
Object.entries()
方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for…in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
- obj:可以返回其可枚举属性的键值对的对象
const obj = {
a: 1,
b: 2
}
for(const item of Object.entries(obj)) {
console.log(item);
}
/*
['a', 1]
['b', 2]
*/
4.15.1、将 Object 转化为 Map
var obj = {
a: 1,
b: 2
}
var map = new Map(Object.entries(obj));
console.log(map); // Map { a: 1, b: 2 }
4.16、Object.fromEntries(iterable)
Object.fromEntries(iterable)
方法把键值对列表转换为一个对象。(返回一个新对象)
- iterable:类似 Array 、 Map 或者其他实现了可迭代协议的可迭代对象
4.16.1、Map 转 Object
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj); // { foo: 'bar', baz: 42 }
4.16.2、Array 转 Object
const arr = [['0', 'a'], ['1', 'b'], ['2', 'c']];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
4.17、Object.freeze(obj)
Object.freeze(obj)
方法可以冻结一个对象。(返回传入的参数)
一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。
此外,冻结一个对象后该对象的原型也不能被修改。
- obj:要冻结的对象
const obj = {
prop: 1
};
Object.freeze(obj);
obj.prop = 2;
console.log(obj.prop); // 1
被冻结对象自身的所有属性都不可能以任何方式被修改。任何修改尝试都会失败,无论是静默地还是通过抛出TypeError异常(最常见但不仅限于strict mode)。
被冻结的对象并不是完全冻结,而是浅层次的冻结(浅冻结),如下所示:
var obj = {
a: {}
};
Object.freeze(obj);
obj.a.b = 1;
console.log(obj.a.b); // 1
4.17.1、冻结函数与普通函数的区别
- 自身的所有属性都不可能以任何方式被修改
- 在严格模式下,任何修改都会抛出 TypeError
4.17.2、实现深冻结
// 深冻结函数。
function deepFreeze(obj) {
// 取回定义在 obj 上的属性名
var propNames = Object.getOwnPropertyNames(obj);
// 在冻结自身之前冻结属性
propNames.forEach(function(name) {
var prop = obj[name];
// 如果 prop 是个对象,冻结它
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
// 冻结自身 (no-op if already frozen)
return Object.freeze(obj);
}
obj = {
a: {}
};
deepFreeze(obj);
obj.a.b = 1;
console.log(obj.a.b); // undefined
注意:在 ES5 中,如果这个方法的参数不是一个对象(一个原始值),那么它会导致 TypeError。
4.17.3、和 Object.seal() 的区别
用 Object.seal()
密封的对象可以改变它们现有的属性。使用 Object.freeze()
冻结的对象中现有属性是不可变的。
4.18、Object.isFrozen(obj)
Object.isFrozen(obj)
方法判断一个对象是否被冻结。(返回布尔值)
- obj:被检测的对象
一个对象是冻结的是指它不可扩展,所有属性都是不可配置的,且所有数据属性(即没有 getter 或 setter 组件的访问器的属性)都是不可写的。
// 一个对象默认是可扩展的,所以它也是非冻结的。
Object.isFrozen({}); // === false
// 一个不可扩展的空对象同时也是一个冻结对象。
var vacuouslyFrozen = Object.preventExtensions({});
Object.isFrozen(vacuouslyFrozen) //=== true;
// 一个非空对象默认也是非冻结的。
var oneProp = { p: 42 };
Object.isFrozen(oneProp) //=== false
// 让这个对象变的不可扩展,并不意味着这个对象变成了冻结对象,
// 因为 p 属性仍然是可以配置的 (而且可写的).
Object.preventExtensions(oneProp);
Object.isFrozen(oneProp) //=== false
// 此时,如果删除了这个属性,则它会成为一个冻结对象。
delete oneProp.p;
Object.isFrozen(oneProp) //=== true
// 一个不可扩展的对象,拥有一个不可写但可配置的属性,则它仍然是非冻结的。
var nonWritable = { e: "plep" };
Object.preventExtensions(nonWritable);
Object.defineProperty(nonWritable, "e", { writable: false }); // 变得不可写
Object.isFrozen(nonWritable) //=== false
// 把这个属性改为不可配置,会让这个对象成为冻结对象。
Object.defineProperty(nonWritable, "e", { configurable: false }); // 变得不可配置
Object.isFrozen(nonWritable) //=== true
// 一个不可扩展的对象,拥有一个不可配置但可写的属性,则它仍然是非冻结的。
var nonConfigurable = { release: "the kraken!" };
Object.preventExtensions(nonConfigurable);
Object.defineProperty(nonConfigurable, "release", { configurable: false });
Object.isFrozen(nonConfigurable) //=== false
// 把这个属性改为不可写,会让这个对象成为冻结对象。
Object.defineProperty(nonConfigurable, "release", { writable: false });
Object.isFrozen(nonConfigurable) //=== true
// 一个不可扩展的对象,值拥有一个访问器属性,则它仍然是非冻结的。
var accessor = { get food() { return "yum"; } };
Object.preventExtensions(accessor);
Object.isFrozen(accessor) //=== false
// ...但把这个属性改为不可配置,会让这个对象成为冻结对象。
Object.defineProperty(accessor, "food", { configurable: false });
Object.isFrozen(accessor) //=== true
// 使用 Object.freeze 是冻结一个对象最方便的方法。
var frozen = { 1: 81 };
Object.isFrozen(frozen) //=== false
Object.freeze(frozen);
Object.isFrozen(frozen) //=== true
// 一个冻结对象也是一个密封对象。
Object.isSealed(frozen) //=== true
// 当然,更是一个不可扩展的对象。
Object.isExtensible(frozen) //=== false
- 在 ES5 中,如果参数不是一个对象类型,将抛出一个TypeError异常
- 在 ES2015 中,非对象参数将被视为一个冻结的普通对象,因此会返回true
4.19、Object.seal(obj)
Object.seal()
方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。(返回传入的参数)
- obj:将要被密封的对象
const obj = {
a: 1
};
Object.seal(obj );
obj.a= 2;
console.log(obj.a); // 1
delete obj.a;
console.log(obj.a); // 1
注意:在 ES5 中,如果这个方法的参数不是一个(原始)对象,那么它将导致 TypeError。
4.19.1、封闭函数与普通函数的区别
- 不能添加新属性
- 所有已有属性会变得不可配置(属性不可以删除,只有值可以被修改)
- 在严格模式下,删除属性会抛出 TypeError
4.20、Object.isSealed(obj)
Object.isSealed()
方法判断一个对象是否被密封。(返回布尔值)
- obj:要被检查的对象
密封对象是指那些不可扩展的,且所有自身属性都不可配置且因此不可删除(但不一定是不可写)的对象。
// 新建的对象默认不是密封的。
var empty = {};
Object.isSealed(empty); // === false
// 如果你把一个空对象变的不可扩展,则它同时也会变成个密封对象。
Object.preventExtensions(empty);
Object.isSealed(empty); // === true
// 但如果这个对象不是空对象,则它不会变成密封对象,因为密封对象的所有自身属性必须是不可配置的。
var hasProp = { fee: "fie foe fum" };
Object.preventExtensions(hasProp);
Object.isSealed(hasProp); // === false
// 如果把这个属性变的不可配置,则这个属性也就成了密封对象。
Object.defineProperty(hasProp, 'fee', {
configurable: false
});
Object.isSealed(hasProp); // === true
// 最简单的方法来生成一个密封对象,当然是使用 Object.seal.
var sealed = {};
Object.seal(sealed);
Object.isSealed(sealed); // === true
// 一个密封对象同时也是不可扩展的。
Object.isExtensible(sealed); // === false
// 一个密封对象也可以是一个冻结对象,但不是必须的。
Object.isFrozen(sealed); // === true,所有的属性都是不可写的
var s2 = Object.seal({ p: 3 });
Object.isFrozen(s2); // === false,属性"p"可写
var s3 = Object.seal({ get p() { return 0; } });
Object.isFrozen(s3); // === true,访问器属性不考虑可写不可写,只考虑是否可配置
- 在 ES5 中,如果这个方法的参数不是一个对象(一个原始类型),那么它会导致TypeError
- 在 ES2015 中,非对象参数将被视为是一个密封的普通对象,只返回true
4.21、Object.preventExtensions(obj)
Object.preventExtensions(obj)
方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。(返回已经不可扩展的对象)
- obj:将要变得不可扩展的对象
不可扩展的对象:不能向对象中添加新属性。不可扩展对象的属性可能仍然可被删除。尝试将新属性添加到不可扩展对象将静默失败或抛出 TypeError(最常见的情况是在严格模式 (en-US)中,但不排除其他情况)。
注意:一旦将对象变为不可扩展的对象,就再也不能使其可扩展。
// Object.preventExtensions 将原对象变的不可扩展,并且返回原对象。
var obj = {};
var obj2 = Object.preventExtensions(obj);
obj === obj2; // true
// 字面量方式定义的对象默认是可扩展的。
var empty = {};
Object.isExtensible(empty) //=== true
// ...但可以改变。
Object.preventExtensions(empty);
Object.isExtensible(empty) //=== false
// 使用 Object.defineProperty 方法为一个不可扩展的对象添加新属性会抛出异常。
var nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", { value: 8675309 }); // 抛出 TypeError 异常
// 在严格模式中,为一个不可扩展对象的新属性赋值会抛出 TypeError 异常。
function fail()
{
"use strict";
nonExtensible.newProperty = "FAIL"; // throws a TypeError
}
fail();
- 在 ES5 中,如果参数不是一个对象类型(而是原始类型),将抛出一个TypeError异常
- 在 ES2015 中,非对象参数将被视为一个不可扩展的普通对象,因此会被直接返回
4.22、Object.isExtensible(obj)
Object.isExtensible()
方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。(返回布尔值)
- obj:需要检测的对象
默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的 Object.prototype.__proto__
已弃用 属性可以被更改。
Object.preventExtensions
,Object.seal
或 Object.freeze
方法都可以标记一个对象为不可扩展(non-extensible)。
// 新对象默认是可扩展的。
var empty = {};
Object.isExtensible(empty); // === true
// ...可以变的不可扩展。
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false
// 密封对象是不可扩展的。
var sealed = Object.seal({});
Object.isExtensible(sealed); // === false
// 冻结对象也是不可扩展。
var frozen = Object.freeze({});
Object.isExtensible(frozen); // === false
- 在 ES5 中,如果参数不是一个对象类型,将抛出一个 TypeError 异常
- 在 ES6 中,non-object 参数将被视为一个不可扩展的普通对象,因此会返回 false
4.23、Object.is(val1, val2)
Object.is()
方法判断两个值是否为同一个值。(返回布尔值)
- val1:被比较的第一个值
- val2:被比较的第二个值
注意:
- +0 和 -0 被视为不同的值
- NaN 被视为相同的值
4.24、Object.prototype.isPrototypeOf(obj)
isPrototypeOf(obj)
方法用于测试一个对象是否存在于另一个对象的原型链上。(返回布尔值)
- obj:在该对象的原型链上搜寻
function Father() { };
function Son() { };
Son.prototype = Father;
var son = new Son();
console.log(Father.isPrototypeOf(son)); // true
4.24.1、与 instanceof 的区别
isPrototypeOf()
与 instanceof
运算符不同。
在表达式 object instanceof AFunction
中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。
4.25、Object.prototype.propertyIsEnumerable()
obj.propertyIsEnumerable(prop)
方法判断指定的属性是否可枚举。(返回布尔值)
- prop:需要测试的属性名
// 数组
var arr = [1];
console.log(arr.propertyIsEnumerable(0)); // true
// 对象
var obj = {a: 1};
console.log(obj.propertyIdsEnumerable('a')); // true
4.26、Object.prototype.toLocaleString()
obj.toLocaleString()
方法返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的(locale-specific purposes)而重载使用。
4.27、Object.prototype.toString()
obj.toString()
方法返回一个表示该对象的字符串。该方法旨在重写(自定义)派生类对象的类型转换的逻辑。
默认情况下,toString() 不接受任何参数。然而,继承自 Object 的对象可能用它们自己的实现重写它,这些实现可以接受参数。例如,Number.prototype.toString()
和 BigInt.prototype.toString()
方法接受一个可选的 radix 参数(设置转化后的进制,默认十进制)。
返回的字符串类型为 [object Type]
,Type 为原始数据的类型,下面进行详细的分类:
- Object:
[object Object]
- Array:
[object Array]
- Function:
[object function]
- Error:
[object Error]
- Boolean:
[object Boolean]
- Number:
[object Number]
- String:
[object String]
- Date:
[object Date]
- RegExp:
[object RegExp]
- Arguments:
[object Arguments]
- null:
[object Null]
- undefined:
[object Undefined]
4.28、Object.prototype.valueOf()
obj.valueOf()
方法返回指定对象的 this 值。
var obj = {
a: 1
};
console.log(obj.valueOf()); // Object { a: 1 }