持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
前面的文章已经简单介绍过对象,这篇文章重点介绍一下对象的一些操作,方法和属性。
1. 创建方式
object的创建方式有两种:构造函数和对象字面量的方式创建。
// 构造函数
let obj = new Object();
// 对象字面量
let obj = {};
Object构造函数为给定的参数创建一个包装类对象,具体如下:
- 如果给定值是null或undefined,那么就会创建并返回一个空对象
- 如果给定值是基本数据类型,那么就会创建并返回这个基本类型的包装类型的对象
- 如果给定值是引用类型,那么就会创建并返回这个引用类型的复制变量,也就是说源对象的引用地址是同一个。
2. 关于继承中对象的相关描述
在JavaScript中,几乎所有的对象都是object类型的实例,它们都是从Object.prototype这里继承属性和方法
3. 删除对象的属性
如果是普通对象的话,一般使用delete关键字删除对象,如果是map对象,可以使用map.prototype.delete()方法来删除属性。
// map对象
const map1 = new Map();
map1.set('bar', 'foo');
console.log(map1.delete('bar')); // true
console.log(map1.has('bar')); // false
// 普通对象
const obj = {
firstname: 'LILI',
lastname: 'chen'
}
console.log(obj.lastname); // chen
delete obj.lastname;
console.log(obj.lastname); // undefined
4. 静态方法
静态方法指的是直接使用类名去调用的方法,静态方法是无法被子类继承或者实例对象拥有(也就是说不能通过new实例化出来的对象来调用)的。
4.1 Object.assign()
通过复制一个对多个对象来创建一个新的对象。
4.1.1 语法
Object.assign(target, ...sources);
- target: 目标对象,接收源对象属性的对象,也是修改后的返回值
- sources: 源对象,包含将被合并的属性
4.1.2 应用
复制对象
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
4.1.2 深拷贝问题
如果源对象是一个对象的引用,它仅仅会复制其引用值, 否则的话复制的就是原始值。
let obj1 = { a: 0, b: { c: 0 } };
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0 } }
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0 } }
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0 } }
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0 } }
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0 } }
obj1.b.c = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 1 } }
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 1 } }
所以在深拷贝中建议使用下面这种方式
obj1 = { a: 0, b: { c: 0 } };
let obj3 = JSON.parse(JSON.strigify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { 'a': 0, 'b': { 'c': 0 } }
4.1.3 合并对象问题
合并对象时,会修改源对象的值
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }
合并具有相同属性的对象
如果目标对象和源对象中存在相同属性key,则目标对象中的属性将被源对象中的属性覆盖,后面源对象的属性将类似地覆盖前面的源对象的属性
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };
const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3}
拷贝Symbol类型属性
const o1 = { a: 1 };
const o2 = { [Symnol('foo')]: 2 };
const obj = Object.assign({}, o1, o2 );
console.log(obj); // { a: 1, [Symnol('foo')]: 2 }
Object.getOwnPropertySymbols(obj); // [Symnol('foo')]
原型链上的属性和不可枚举属性不能被复制
const obj = Object.create({ foo: 1 }, {
bar: {
value: 2
},
baz: {
value: 3,
enumerable: true
}
})
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
基本类型会被包装为对象
const v1 = 'abc';
const v2 = true;
const v3 = 10;
const v4 = Symbol('foo');
const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
console.log(obj); // {'0': 'a', '1': 'b', '2': 'c'}
异常会打断后续拷贝任务
const target = Object.defineProperty({}, 'foo', {
value: 1,
writable: false
});
Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3}, { baz: 4 });
console.log(target.bar); // 2
console.log(target.foo2); // 3
console.log(target.foo); // 3
console.log(target.foo3); // undefined
console.log(target.baz); // undefined
拷贝访问器
const obj = {
foo: 1,
get bar() {
return 2;
}
};
let copy = Object.assign({}, obj);
console.log(copy); // { foo: 1, bar: 2 }
总结:
Object主要还是用于对象的合并上面(不管源对象是否变化)、浅拷贝、对对象数组的处理
4.2 Object.create()
使用指定的原型对象和属性创建一个新对象。
4.2.1 用法:
let newObj = Object.create(obj);
4.2.2 参数
obj指的就是新创建对象的原型对象,必须为null或者除基本类型包装对象以外的对象。
let o1 = Object.create(null);
let o2 = Object.create(null, {
alice: { value: 18, enumerable: true },
bob: { value: 27, enumerable: true },
});
console.log(o1); // {}
console.log(o2); // {alice: 18, bob: 27}
4.2.3 应用:
Object.create()一般用在继承中去创建对象,关于继承我会在后面的文章中介绍,暂时先知道Object.create()在继承中会用于创建对象即可。
let obj = {};
let obj1 = {};
obj.prototype = Object.create(obj1.prototype);
4.3 Object.defineProperty()
给对象添加一个属性并指定该属性的配置。这种方式一般是允许精确地添加或修改对象的属性。
4.3.1 语法
Object.defineProperty(obj, prop, descriptor);
4.3.2 参数
- obj: 要定义属性的对象
- prop: 要定义的属性名或Symbol
- descriptor: 要定义或修改的属性描述符
对象的属性描述符目前有两种类型:
数据描述符:
- configurable: 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为 false。
- enumerable: 当且仅当该属性的enumerable键值为true时,该属性才会出现在对象的枚举属性中。默认为false。
- value: 该属性对应的值。可以是任何有效的JavaScript值(数值,对象,函数等)。默认为undefined。
- writable: 当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符 (en-US)改变。 默认为 false。
存取描述符:
- get: 属性的getter函数,如果没有getter,则为undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 默认为 undefined。
- set: 属性的setter函数,如果没有setter,则为undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的this对象,默认值为undefined。
4.3.3 创建属性
如果对象中不存在指定的属性,Object.defineProperty()会创建这个属性。当描述符中华魂省略某些字段时,这些字段将使用它们的默认值。
const o = {};
Object.defineProperty(o, 'a', {
value: 37,
writable: true,
enumerable: true,
configurable: true
})
4.3.4 修改属性
如果属性已经存在,Object.defineProperty()将尝试根据描述符中的值以及对象当前的配置来修改这个属性。如果旧描述符将其configurable 属性设置为false,则该属性被认为是“不可配置的”,并且没有属性可以被改变(除了单向改变 writable 为 false)。当属性不可配置时,不能在数据和访问器属性类型之间切换。
writable属性
当writable属性设置为false时,该属性被称为“不可写的”,不能被重新赋值。
let o = {};
Object.defineProperty(o, 'a', {
value: 37,
writable: false
});
console.log(o.a); // 37
o.a = 25; // 报错
Enumerable属性
enumerable定义了对象的属性是否可以在for...in循环和Object.keys()中被枚举。
let o = {};
Object.defineProperty(o, "a", { value : 1, enumerable: true });
Object.defineProperty(o, "b", { value : 2, enumerable: false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable 默认为 false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则 enumerable 为 true
Object.defineProperty(o, Symbol.for('e'), {
value: 5,
enumerable: true
});
Object.defineProperty(o, Symbol.for('f'), {
value: 6,
enumerable: false
});
for (let i in o) {
console.log(i);
}
// logs 'a' and 'd' (in undefined order)
Object.keys(o); // ['a', 'd']
Configurable属性
configurable特性表示对象的属性是否可以被删除,以及除value和writable特性以外的其他特性是否可以被修改。
let o = {};
Object.defineProperty(o, 'a', {
get() { return 1; },
configurable: false
});
Object.defineProperty(o, 'a', {
configurable: true
}); // throws a TypeError
Object.defineProperty(o, 'a', {
enumerable: true
}); // throws a TypeError
Object.defineProperty(o, 'a', {
set() {}
}); // throws a TypeError (set was undefined previously)
Object.defineProperty(o, 'a', {
get() { return 1; }
}); // throws a TypeError
// (even though the new get does exactly the same thing)
Object.defineProperty(o, 'a', {
value: 12
}); // throws a TypeError // ('value' can be changed when 'configurable' is false but not in this case due to 'get' accessor)
console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1
如果o.a的configurable属性为true,则不会抛出任何错误,而且属性可以正常被删除。
4.3.5 添加多个属性和默认值
添加属性可以使用点运算符或者Object.defineProperty()多次设置,只是使用点运算符和Object.defineProperty()为对象的属性赋值时,数据描述符中的属性的默认值是不一样的。
let o = {};
o.a = 1;
// 等同于:
Object.defineProperty(o, "a", {
value: 1,
writable: true,
configurable: true,
enumerable: true
});
// 另一方面,
Object.defineProperty(o, "a", { value : 1 });
// 等同于:
Object.defineProperty(o, "a", {
value: 1,
writable: false,
configurable: false,
enumerable: false
});
4.3.6 自定义setter和getter
let temperature = null;
let archive = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!');
return temperature;
},
set: function(value) {
temperature = value;
archive.push({ val: temperature });
}
});
4.3.7 继承属性
如果访问者的属性是被继承的,它的get和set方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。
function myclass() {
}
let value;
Object.defineProperty(myclass.prototype, "x", {
get() {
return value;
},
set(x) {
value = x;
}
});
let a = new myclass();
let b = new myclass();
a.x = 1;
console.log(b.x); // 1
这种情况可以通过将值存储在另一个属性中解决。在get和set方法中,this指向某个被访问和修改属性的对象。
function myclass() {
}
Object.defineProperty(myclass.prototype, "x", {
get() {
return this.stored_x;
},
set(x) {
this.stored_x = x;
}
});
let a = new myclass();
let b = new myclass();
a.x = 1;
console.log(b.x); // undefined
4.4 Object.defineProperties()
给对象添加多个属性并分别指定它们的配置。
4.4.1 语法
Object.defineProperties(obj, props)
4.4.2 参数
- obj: 在其上定义或修改属性的对象。
- props: 要定义其可枚举属性或修改的属性描述符的对象。对象中存在的属性描述符主要有两种:数据描述符合访问器描述符(关于描述符的介绍参考上面)。
let obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
});
4.5 Object.enteries()
返回给定对象自身可枚举属性的[key, value]数组。
4.5.1 语法
Object.enteries(obj)
4.5.2 参数
- obj: 可以返回其可枚举属性的键值对的对象
返回给定对象自身可枚举属性的键值对数组,属性的顺序与通过手动循环对象的属性值给出的顺序相同。
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
将Object转成Map
new Map接受一个可迭代的entries。
let obj = { foo: "bar", baz: 42 };
let map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
4.6 Object.freeze()
冻结对象,其他代码不能删除或更改任何属性。
4.6.1 语法:
Object.freeze(obj)
4.6.2 参数
- obj: 要被冻结的对象
4.6.3 冻结对象
let obj = {
prop: function() {},
foo: 'bar'
};
// 新的属性会被添加,已存在的属性可能
// 会被修改或移除
obj.foo = 'baz';
obj.lumpy = 'woof';
delete obj.prop;
// 作为参数传递的对象与返回的对象都被冻结
// 所以不必保存返回的对象(因为两个对象全等)
let o = Object.freeze(obj);
o === obj; // true
Object.isFrozen(obj); // === true
// 现在任何改变都会失效
obj.foo = 'quux'; // 静默地不做任何事
// 静默地不添加此属性
obj.quaxxor = 'the friendly duck';
4.6.4 冻结数组
let a = [0];
Object.freeze(a); // 现在数组不能被修改了。
a[0]=1; // fails silently
a.push(2); // fails silently
4.6.5 冻结对象也分浅冻结和深冻结, Object.freeze()默认是浅冻结
// 深冻结函数。
function deepFreeze(obj) {
// 取回定义在 obj 上的属性名
let propNames = Object.getOwnPropertyNames(obj);
// 在冻结自身之前冻结属性
propNames.forEach(function(name) {
let prop = obj[name];
// 如果 prop 是个对象,冻结它
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
// 冻结自身 (no-op if already frozen)
return Object.freeze(obj);
}
obj2 = {
internal: {}
};
deepFreeze(obj2);
obj2.internal.a = 'anotherValue';
obj2.internal.a; // undefined
4.7 Object.getOwnPropertyDescriptor()
返回对象指定的属性配置
4.7.1 语法
Object.getOwnPropertyDescriptor(obj, prop)
4.7.2 参数
- obj: 需要查找的目标对象
- prop: 目标对象内属性名称
一个属性描述符是一个记录,由下面属性当中的某些组成的:
- value:该属性的值 (仅针对数据属性描述符有效)
- writable:当且仅当属性的值可以被改变时为 true。(仅针对数据属性描述有效)
- get:获取该属性的访问器函数(getter)。如果没有访问器,该值为 undefined。(仅针对包含访问器或设置器的属性描述有效)
- set:获取该属性的设置器函数(setter)。如果没有设置器,该值为 undefined。(仅针对包含访问器或设置器的属性描述有效)
- configurable:当且仅当指定对象的属性描述可以被改变或者属性可被删除时,为 true。
- enumerable:当且仅当指定对象的属性可以被枚举出时,为 true。
let o, d;
o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
// d {
// configurable: true,
// enumerable: true,
// get: /*the getter function*/,
// set: undefined
// }
4.8 Object.getOwnPropertyNames()
返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名
4.8.1 语法:
Object.getOwnPropertyNames(obj)
4.8.2 参数:
- obj: 一个对象,其自身的可枚举属性和不可枚举属性的名称被返回
用这种方法获取到的可枚举属性和通过for...in循环的顺序是一致的,数组中不可枚举属性的顺序未定义。
let arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]
// 类数组对象
let obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]
4.9 Object.getOwnPropertySymbols()
返回一个数组,它包含了指定对象自身所有的符号属性
4.9.1 语法:
Object.getOwnPropertySymbols(obj)
参数
- obj: 要返回Symbol属性的对象
与Object.getOwnPropertyNames()类似,您可以将给定对象的所有符号属性作为 Symbol 数组获取。请注意,Object.getOwnPropertyNames()本身不包含对象的 Symbol 属性,只包含字符串属性。所有的对象在初始化的时候不会包含任何的 Symbol,除非你在对象上赋值了 Symbol 否则Object.getOwnPropertySymbols()只会返回一个空的数组。
let obj = {};
let a = Symbol("a");
let b = Symbol.for("b");
obj[a] = "localSymbol";
obj[b] = "globalSymbol";
let objectSymbols = Object.getOwnPropertySymbols(obj);
console.log(objectSymbols.length); // 2
console.log(objectSymbols) // [Symbol(a), Symbol(b)]
console.log(objectSymbols[0]) // Symbol(a)
4.10 Object.getPrototypeOf()
返回指定对象的原型对象
4.10.1 语法:
Object.getPrototypeOf()
4.10.2 参数
- obj: 要返回其原型的对象
let proto = {};
let obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true
let reg = /a/;
Object.getPrototypeOf(reg) === RegExp.prototype; // true
4.11 Object.is()
比较两个值是否相同。所有NaN值都相等(与 == 和 === 不同)
4.11.1 语法
Object.is(value1, value2)
4.11.2 参数:
- value1: 被比较的第一个值
- value2: 被比较的第二个值
如果满足以下条件,那么返回值就是true。
- 都是undefined
- 都是null
- 都是true或者false
- 都是相同长度、相同字符、按相同顺序排列的字符串
- 都是相同对象(同一个对象的值引用)
- 都是数字且
- 都是+0
- 都是-0
- 都是NaN
- 都是同一个值,非零且都不是NaN
// Case 1: Evaluation result is the same as using ===
Object.is(25, 25); // true
Object.is('foo', 'foo'); // true
Object.is('foo', 'bar'); // false
Object.is(null, null); // true
Object.is(undefined, undefined); // true
Object.is(window, window); // true
Object.is([], []); // false
let foo = { a: 1 };
let bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false
// Case 2: Signed zero
Object.is(0, -0); // false
Object.is(+0, -0); // false
Object.is(-0, -0); // true
Object.is(0n, -0n); // true
// Case 3: NaN
Object.is(NaN, 0/0); // true
Object.is(NaN, Number.NaN) // true
Object.is() 与 == 不同。== 运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false 判断为 true),而 Object.is 不会强制转换两边的值。
Object.is() 与 === 也不相同。差别是它们对待有符号的零和 NaN 不同,例如,=== 运算符(也包括 == 运算符)将数字 -0 和 +0 视为相等,而将 Number.NaN 与 NaN 视为不相等。
4.12 Object.isExtensible()
判断对象是否可扩展
4.12.1 语法
Object.isExtensible(obj)
4.12.2 参数:
- obj: 需要检测的对象
默认情况下,对象是可扩展的(即可以为他们添加新的属性)。以及它们的__proto__属性可以被更改。Object.preventExtensions、Object.seal或Object.freeze可以修改让对象是不可扩展的。
// 新对象默认是可扩展的。
let empty = {};
Object.isExtensible(empty); // === true
// ...可以变的不可扩展。
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false
// 密封对象是不可扩展的。
let sealed = Object.seal({});
Object.isExtensible(sealed); // === false
// 冻结对象也是不可扩展。
let frozen = Object.freeze({});
Object.isExtensible(frozen); // === false
4.13 Object.isFrozen()
判断对象是否已经冻结
4.13.1 语法
Object.isFrozen(obj)
4.13.2 参数
- obj: 被检测的对象
一个对象是冻结指的是它不可扩展,所有的属性都是不可配置的,且所有数据属性(即没有getter或setter组件的访问器的属性)都是不可写的。
// 一个对象默认是可扩展的,所以它也是非冻结的。
Object.isFrozen({}); // === false
// 一个不可扩展的空对象同时也是一个冻结对象。
let vacuouslyFrozen = Object.preventExtensions({});
Object.isFrozen(vacuouslyFrozen) //=== true;
// 一个非空对象默认也是非冻结的。
let oneProp = { p: 42 };
Object.isFrozen(oneProp) //=== false
// 让这个对象变的不可扩展,并不意味着这个对象变成了冻结对象,
// 因为 p 属性仍然是可以配置的 (而且可写的).
Object.preventExtensions(oneProp);
Object.isFrozen(oneProp) //=== false
// 此时,如果删除了这个属性,则它会成为一个冻结对象。
delete oneProp.p;
Object.isFrozen(oneProp) //=== true
// 把这个属性改为不可配置,会让这个对象成为冻结对象。
Object.defineProperty(nonWritable, "e", { configurable: false }); // 变得不可配置
Object.isFrozen(nonWritable) //=== true
4.14 Object.isSealed()
判断对象是否已经密封
4.14.1 语法
Object.isSealed(obj)
4.14.2 参数
- obj:要被检查的对象
如果这个对象是密封的,则返回true, 否则返回false。密封对象是指那些不可扩展的,且所有自身属性都不可配置且因此不可删除(但不一定是不可写)的对象
// 新建的对象默认不是密封的。
let empty = {};
Object.isSealed(empty); // === false
// 如果你把一个空对象变的不可扩展,则它同时也会变成个密封对象。
Object.preventExtensions(empty);
Object.isSealed(empty); // === true
// 但如果这个对象不是空对象,则它不会变成密封对象,因为密封对象的所有自身属性必须是不可配置的。
let hasProp = { fee: "fie foe fum" };
Object.preventExtensions(hasProp);
Object.isSealed(hasProp); // === false
// 如果把这个属性变的不可配置,则这个属性也就成了密封对象。
Object.defineProperty(hasProp, 'fee', {
configurable: false
});
Object.isSealed(hasProp); // === true
4.15 Object.keys()
返回一个包含所有给定对象自身可枚举属性名称的数组。
4.15.1 语法
Object.keys(obj)
4.15.2 参数
- obj: 要返回其枚举自身属性的对象
Object.keys() 返回一个所有元素为字符串的数组,其元素来自给定的 object 上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。
// 简单数组
const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']
// 类数组对象
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
// 具有随机键顺序的类数组对象
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']
4.16 Object.preventExtensions()
防止任何对象的任何扩展
4.16.1 语法
Object.preventExtensions(obj)
4.16.2 参数
- obj: 将要变得不可扩展的对象
Object.preventExtensions()将对象标记为不再可扩展,这样它将永远不会具有它被标记为不可扩展时持有的属性之外的属性。注意,一般来说,不可扩展对象的属性可能仍然可被删除。 这个属性仅阻止添加自身的属性,其原型上的属性仍然是可以添加的 一旦将对象变为不可扩展的对象,就再也不能使其可扩展。
// Object.preventExtensions 将原对象变的不可扩展,并且返回原对象。
let obj = {};
let obj2 = Object.preventExtensions(obj);
obj === obj2; // true
// 字面量方式定义的对象默认是可扩展的。
let empty = {};
Object.isExtensible(empty) //=== true
// ...但可以改变。
Object.preventExtensions(empty);
Object.isExtensible(empty) //=== false
4.17 Object.seal()
封闭一个对象,阻止添加新属性并防止其他代码删除对象的属性
4.17.1 语法
Object.seal(obj)
4.17.2 参数
- obj: 将要被密封的对象
密封一个对象会让这个对象变的不能添加新属性,且所有已有属性会变的不可配置。属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。但属性的值仍然可以修改。 不会影响从原型链上继承的属性。但 proto ( 已弃用 ) 属性的值也会不能修改。
let obj = {
prop: function() {},
foo: 'bar'
};
// 可以添加新的属性
// 可以更改或删除现有的属性
obj.foo = 'baz';
obj.lumpy = 'woof';
delete obj.prop;
let o = Object.seal(obj);
o === obj; // true
Object.isSealed(obj); // === true
// 仍然可以修改密封对象的属性值
obj.foo = 'quux';
// 但是你不能将属性重新定义成为访问器属性
// 反之亦然
Object.defineProperty(obj, 'foo', {
get: function() { return 'g'; }
}); // throws a TypeError
// 除了属性值以外的任何变化,都会失败。
obj.quaxxor = 'the friendly duck';
// 添加属性将会失败
delete obj.foo;
// 删除属性将会失败
4.18 Object.setPrototypeOf()
设置对象的原型(即内部[[ Prototype ]]属性)
4.18.1 语法:
Object.setPrototypeOf(obj, prototype)
4.18.2 参数
- obj: 要设置其原型的对象
- prototype:该对象的新原型(一个对象或null)
建议使用Object.setPrototypeOf(obj, prototype)来设置对象的原型,这是es6新增的。原来的旧的Object.prototype.__pro__访问器已经被弃用。
class Human {}
class SuperHero {}
Object.setPrototypeOf(SuperHero, Human);
4.19 Object.values()
返回给定对象自身可枚举值的数组
4.19.1 语法
Object.values(obj)
4.19.2 参数
- obj: 被返回可枚举属性值的对象
Object.values()返回一个数组,其元素是在对象上找到的可枚举属性值。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
let obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]
// array like object
let obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']
// array like object with random key ordering
// when we use numeric keys, the value returned in a numerical order according to the keys
let an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']
5 实例属性
5.1 Object.prototype.constructor
-
一个引用值,指向Object构造函数
-
所有对象(使用 Object.create(null) 创建的对象除外)都将具有 constructor 属性。 在没有显式使用构造函数的情况下,创建的对象(例如对象和数组文本)将具有 constructor 属性,这个属性指向该对象的基本对象构造函数类型。
const o = {} o.constructor === Object // true const o = new Object o.constructor === Object // true const a = [] a.constructor === Array // true const a = new Array a.constructor === Array // true const n = new Number(3) n.constructor === Number // true -
改变 constructor 的属性不会影响 instanceof 运算符:
let a = []; a.constructor = String a.constructor === String // true a instanceof String // false a instanceof Array // true a = new Foo(); a.constructor = 'bar' a.constructor === 'bar' // true
5.2 Object.prototype.proto
指向一个对象,当一个object实例化时,使用该对象作为实例化对象的原型
当前属性已经被弃用,建议使用Object.getPrototypeOf()
禁止扩展,封闭对象,冰冻对象的区别
禁止扩展可以理解成不能往外扩大,也就是说
- 不能新增属性
- 可以修改属性
- 可以删除属性
封闭对象可以理解成把对象密封在一个独立的空间,外面的进不来,里面的出不去
- 不能新增属性
- 可以修改属性
- 不能删除属性
冰冻对象可以理解成把对象完全冻住,让它啥也干不了
- 不能新增属性
- 不能修改属性
- 不能删除属性
6. 实例方法
6.1 Object.prototype.defineGetter()
将一个函数和一个属性相关联,当该属性被访问时,执行该函数,返回函数的返回值
这个方法已经弃用,建议用Object.defineProperty中的get描述符定义
6.2 Object.prototype.defineSetter()
将一个属性与一个函数相关联,当该属性被设置时,执行该函数,执行该函数去修改某个属性。
这个方法已经弃用,建议用Object.defineProperty中的set描述符定义
6.3 Object.prototype.lookupGetter()
返回一个函数,该函数通过给定属性的 Object.prototype.defineGetter() 得出。 这个方法已经弃用。
6.4 Object.prototype.lookupSetter()
返回一个函数,该函数通过给定属性的 Object.prototype.defineSetter() 得出。 这个方法已经弃用。
6.5 Object.prototype.hasOwnProperty()
返回一个布尔值,用于表示一个对象自身是否包含指定的属性,该方法并不会查找原型链上继承来的属性。
6.5.1 语法
obj.hasOwnProperty(prop)
6.5.2 参数
- prop:要检测的属性的String字符串形式表示的名称,或者Symbol。
所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
let buz = {
fog: 'stack'
};
for (let name in buz) {
if (buz.hasOwnProperty(name)) {
console.log('this is fog (' +
name + ') for sure. Value: ' + buz[name]); // this is fog (fog) for sure. Value: stack
}
else {
console.log(name); // toString or something else
}
}
6.5.3 使用hasOwnProperty作为属性名
当某个对象可能自有一个占用该属性名的属性时,就需要使用外部的 hasOwnProperty 获得正确的结果:
let foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // 始终返回 false
// 如果担心这种情况,
// 可以直接使用原型链上真正的 hasOwnProperty 方法
({}).hasOwnProperty.call(foo, 'bar'); // true
// 也可以使用 Object 原型上的 hasOwnProperty 属性
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
6.6 Object.prototype.isPrototypeOf()
返回一个布尔值,用于表示该方法所调用的对象是否在指定对象的原型链中。
6.6.1 语法:
Object.prototype.isPrototypeOf(obj)
6.6.2 参数
- object:在该对象的原型链上搜寻
function Foo() {}
function Bar() {}
function Baz() {}
Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);
let baz = new Baz();
console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true
6.7 Object.prototype.propertyIsEnumerable()
返回一个布尔值,用于表示内部属性 ECMAScript [[Enumerable]] attribute 是否被设置。
6.7.1 语法
obj.propertyIsEnumerable(prop)
6.7.2 参数
- prop:需要测试的属性名
每个对象都有一个 propertyIsEnumerable 方法。此方法可以确定对象中指定的属性是否可以被 for...in 循环枚举,但是通过原型链继承的属性除外。如果对象没有指定的属性,则此方法返回 false。
let o = {};
let a = [];
o.prop = 'is enumerable';
a[0] = 'is enumerable';
o.propertyIsEnumerable('prop'); // 返回 true
a.propertyIsEnumerable(0); // 返回 true
let a = ['is enumerable'];
a.propertyIsEnumerable(0); // 返回 true
a.propertyIsEnumerable('length'); // 返回 false
Math.propertyIsEnumerable('random'); // 返回 false
this.propertyIsEnumerable('Math'); // 返回 false
6.8 Object.prototype.toLocaleString()
调用toString()。返回一个对象的字符串表示
6.8.1 语法
obj.toLocaleString();
6.9 Object.prototype.toString()
返回一个代表该对象的字符串。
6.9.1 语法
obj.toLocaleString();
let o = new Object();
o.toString(); // 返回 [object Object]
6.10 Object.prototype.valueOf()
返回指定对象的原始值
6.10.1 语法
- valueOf()
function MyNumberType(n) {
this.number = n;
}
MyNumberType.prototype.valueOf = function () {
return this.number;
};
const myObj = new MyNumberType(4);
myObj + 3; // 7