Object.keys
- 返回一个由一个给定对象的自身可枚举属性组成的数组,所有元素为字符串的数组
- 数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。
- 如果对象的键-值都不可枚举,那么将返回由键组成的数组。
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']
var myObj = Object.create({}, {
getFoo: {
value: function () { return this.foo; }
}
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']
在ES5里,如果此方法的参数不是对象(而是一个原始值),那么它会抛出 TypeError。在ES2015中,非对象的参数将被强制转换为一个对象。
Object.keys("foo");
// TypeError: "foo" is not an object (ES5 code)
Object.keys("foo");
// ["0", "1", "2"]
Object.defineProperty()
Object.defineProperty(obj, prop, descriptor):
-
直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
obj:要在其上定义属性的对象。 prop:要定义或修改的属性名称。 descriptor:将被定义或修改的属性描述符 -
返回值:被传递给函数的对象。
-
通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(
for...in 或 Object.keys 方法), 这些属性的值可以被改变,也可以被删除。 -
默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的。
举例:首先定义一个对象
const obj = {
firstName: 'A',
lastName: 'B'
}
给这个对象添加一个fullName属性,fullName的值为obj.firstName-obj.lastName
Object.defineProperty(obj, 'fullName', {
// 访问描述符
// 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this为obj
get () {
return this.firstName + '-' + this.lastName
},
// 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this为obj
set (value) {
const names = value.split('-')
this.firstName = names[0]
this.lastName = names[1]
}
})
console.log(obj.fullName) // A-B
属性描述符
- 对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。
- 数据描述符:是一个具有值的属性,该值可能是可写的,也可能不是可写的。
- 存取描述符:是由getter-setter函数对描述的属性。
- 描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符均具有以下可选键值
configurable:当且仅当该属性的configurable为true时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为false。enumerable:当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为false。
数据描述符同时具有以下可选键值:
value:该属性对应的值。可以是任何有效的JavaScript值(数值,对象,函数等)。默认为undefined。writable:当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为false。
存取描述符同时具有以下可选键值:
get:一个给属性提供getter的方法,如果没有getter则为undefined。 当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象 (由于继承关系,这里的this并不一定是定义该属性的对象)。默认为undefined。set:一个给属性提供setter的方法,如果没有setter则为undefined。 当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为undefined。
描述符可同时具有的键值
- 如果一个描述符不具有
value,writable,get和set任意一个关键字,那么它将被认为是一个数据描述符。 - 如果一个描述符同时有(
value或writable)和(get或set)关键字,将会产生一个异常。
创建属性
- 如果对象中不存在指定的属性,
Object.defineProperty()就创建这个属性。 - 当描述符中省略某些字段时,这些字段将使用它们的默认值。
- 拥有布尔值的字段的默认值都是
false。value,get和set字段的默认值为undefined。 - 一个没有
get/set/value/writable定义的属性被称为“通用的”,并被“键入”为一个数据描述符
var o = {}; // 创建一个新对象
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 对象o拥有了属性a,值为37
// 在对象中添加一个属性与存取描述符的示例
var bValue;
Object.defineProperty(o, "b", {
get : function(){
return bValue;
},
set : function(newValue){
bValue = newValue;
},
enumerable : true,
configurable : true
});
o.b = 38;
// 对象o拥有了属性b,值为38
// o.b的值现在总是与bValue相同,除非重新定义o.b
// 数据描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get: function() {
return 0xdeadbeef;
}
});
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors
修改属性
- 如果属性已经存在,
Object.defineProperty()将尝试根据描述符中的值以及对象当前的配置来修改这个属性。 - 如果旧描述符将其
configurable属性设置为false,则该属性被认为是“不可配置的”,并且没有属性可以被改变(除了单向改变 writable 为 false)。 - 当属性不可配置时,不能在数据和访问器属性类型之间切换。
- 当试图改变不可配置属性(除了
value和writable属性之外)的值时会抛出TypeError,除非当前值和新值相同。 Writable属性:当writable属性设置为false时,该属性被称为“不可写”。它不能被重新分配。
如示例所示,试图写入非可写属性不会改变它,也不会引发错误。
var o = {}; // Creates a new object
Object.defineProperty(o, 'a', {
value: 37,
writable: false
});
console.log(o.a); // 37
o.a = 25; // 不会报错,但是修改不会生效
console.log(o.a); // logs 37
// strict mode 严格模式会报错
(function() {
'use strict';
var o = {};
Object.defineProperty(o, 'b', {
value: 2,
writable: false
});
o.b = 3; // throws TypeError: "b" is read-only
return o.b; // returns 2 without the line above
}());
Enumerable 特性
enumerable定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。
var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true
for (var i in o) {
console.log(i);
}
// 打印 'a' 和 'd' (in undefined order)
Object.keys(o); // ["a", "d"]
o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
Configurable 特性:
configurable特性表示对象的属性是否可以被删除,以及除value和writable特性外的其他特性是否可以被修改。- 如果
o.a的configurable属性为true,则不会抛出任何错误,并且该属性将在最后被删除。
var o = {}
Object.defineProperty(o, 'a', {
get: function () {
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', {value: 12}) // throws a TypeError
Object.defineProperty(o, 'a', { // throws a TypeError (set was undefined previously)
set: function () {
}
})
Object.defineProperty(o, 'a', { // throws a TypeError (even though the new get does exactly the same thing)
get: function () {
return 1
}
})
console.log(o.a) // 1
delete o.a // false
console.log(o.a) // 1
添加多个属性和默认值:考虑特性被赋予的默认特性值非常重要,通常,使用点运算符和Object.defineProperty()为对象的属性赋值时,数据描述符中的属性默认值是不同的,如下例所示。
var 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
});
一般的 Setters 和 Getters:下面的例子展示了如何实现一个自存档对象。 当设置 temperature 属性时,archive 数组会获取日志条目。
function Archiver() {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!');
return temperature;
},
set: function(value) {
temperature = value;
archive.push({ val: temperature });
}
});
this.getArchive = function() { return archive; };
}
var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
或
var pattern = {
get: function () {
return 'I alway return this string,whatever you have assigned';
},
set: function () {
this.myname = 'this is my name string';
}
};
function TestDefineSetAndGet() {
Object.defineProperty(this, 'myproperty', pattern);
}
var instance = new TestDefineSetAndGet();
instance.myproperty = 'test';
console.log(instance.myproperty); // 'I alway return this string,whatever you have assigned'
console.log(instance.myname); // 'this is my name string'
继承属性:如果访问者的属性是被继承的,它的 get 和set 方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。
function myclass() {
}
var value;
Object.defineProperty(myclass.prototype, "x", {
get() {
return value;
},
set(x) {
value = x;
}
});
var a = new myclass();
var 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;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // undefined
不像访问者属性,值属性始终在对象自身上设置,而不是一个原型。然而,如果一个不可写的属性被继承,它仍然可以防止修改对象的属性。
function myclass() {
}
myclass.prototype.x = 1;
Object.defineProperty(myclass.prototype, "y", {
writable: false,
value: 1
});
var a = new myclass();
a.x = 2;
console.log(a.x); // 2
console.log(myclass.prototype.x); // 1
a.y = 2; // Ignored, throws in strict mode
console.log(a.y); // 1
console.log(myclass.prototype.y); // 1
Object.create
-
创建一个新对象,使用现有的对象来提供新创建的对象的
__proto__。 -
语法:
Object.create(proto, propertiesObject)proto:新创建对象的原型对象。 propertiesObject:可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性) 对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。 如果propertiesObject参数是 null 或非原始包装对象,则抛出一个 TypeError 异常。 -
返回值:一个新对象,带着指定的原型对象和属性。
-
1.方法内部定义一个新的空对象
obj -
2.将
obj._proto__的对象指向传入的参数proto -
3.返回一个新的对象
Object.create() 和 new Object() 的不同
new Object()方式创建
var newObj = {
name: 'fx',
why: {
day: 1
}
}
var b = new Object(newObj)
b.name = 'bfx'
b.why = {
bday: 'b'
}
console.log('b:', b)
console.log('newObj:', newObj)
输出如下
Object.create()方式创建
var newObj = {
name: 'fx',
why: {
day: 1
}
}
var b = Object.create(newObj)
console.log(b)
b.name = 'bfx'
b.why = {
bday: 'b'
}
console.log('b:', b)
console.log('newObj:', newObj)
输出如下
例:
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
o = Object.create({}, { p: { value: 42 } })
// 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
o.p = 24
console.log(o.p) // 42
o.q = 12
for (var prop in o) {
console.log(prop)
}
//"q"
delete o.p // false
delete o.q // true
//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
用 Object.create实现类式继承
// Shape - 父类(superclass)
function Shape() {
this.x = 0;
this.y = 0;
}
// 父类的方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - 子类(subclass)
function Rectangle() {
Shape.call(this); // call super constructor.
}
// 子类续承父类
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.'
如果你希望能继承到多个对象,则可以使用混入的方式。
function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// 继承一个类
MyClass.prototype = Object.create(SuperClass.prototype);
// 混合其它
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// 重新指定constructor
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
// do a thing
};
Object.assign 会把 OtherSuperClass原型上的函数拷贝到 MyClass原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法。
var o;
// 创建一个原型为null的空对象
o = Object.create(null);
o = {};
// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);
o = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: {
writable:true,
configurable:true,
value: "hello"
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) {
console.log("Setting `o.bar` to", value);
}
}
});
function Constructor(){}
o = new Constructor();
// 上面的一句就相当于:
o = Object.create(Constructor.prototype);
// 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码
propertyIsEnumerable (属性是否可枚举)
- 返回一个布尔值,表示指定的属性是否可枚举。
- 语法
obj.propertyIsEnumerable(prop) - 每个对象都有一个
propertyIsEnumerable方法。此方法可以确定对象中指定的属性是否可以被for...in循环枚举,但是通过原型链继承的属性除外。如果对象没有指定的属性,则此方法返回false。
var o = {};
var a = [];
o.prop = 'is enumerable';
a[0] = 'is enumerable';
o.propertyIsEnumerable('prop'); // 返回 true
a.propertyIsEnumerable(0); // 返回 true
===
var a = ['is enumerable'];
a.propertyIsEnumerable(0); // 返回 true
a.propertyIsEnumerable('length'); // 返回 false
Math.propertyIsEnumerable('random'); // 返回 false
this.propertyIsEnumerable('Math'); // 返回 false
自身属性和继承属性
var a = [];
a.propertyIsEnumerable('constructor'); // 返回 false
function firstConstructor() {
this.property = 'is not enumerable';
}
firstConstructor.prototype.firstMethod = function() {};
function secondConstructor() {
this.method = function method() { return 'is enumerable'; };
}
secondConstructor.prototype = new firstConstructor;
secondConstructor.prototype.constructor = secondConstructor;
var o = new secondConstructor();
o.arbitraryProperty = 'is enumerable';
o.propertyIsEnumerable('arbitraryProperty'); // 返回 true
o.propertyIsEnumerable('method'); // 返回 true
o.propertyIsEnumerable('property'); // 返回 false
o.property = 'is enumerable';
o.propertyIsEnumerable('property'); // 返回 true
// 之所以这些会返回 false,是因为,在原型链上 propertyIsEnumerable 不被考虑
// (尽管最后两个在 for-in 循环中可以被循环出来)。
o.propertyIsEnumerable('prototype'); // 返回 false (根据 JS 1.8.1/FF3.6)
o.propertyIsEnumerable('constructor'); // 返回 false
o.propertyIsEnumerable('firstMethod'); // 返回 false
hasOwnProperty
- 返回一个布尔值,表示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
obj.hasOwnProperty(prop)prop要检测的属性的String字符串形式表示的名称,或者Symbol。- 所有继承了
Object的对象都会继承到hasOwnProperty方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和in运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
即使属性的值是 null 或 undefined,只要属性存在,hasOwnProperty 依旧会返回 true。
o = new Object();
o.propOne = null;
o.hasOwnProperty('propOne'); // 返回 true
o.propTwo = undefined;
o.hasOwnProperty('propTwo'); // 返回 true
自身属性与继承属性
o = new Object();
o.prop = 'exists';
o.hasOwnProperty('prop'); // 返回 true
o.hasOwnProperty('toString'); // 返回 false
o.hasOwnProperty('hasOwnProperty'); // 返回 false
遍历一个对象的所有自身属性
var buz = {
fog: 'stack'
};
for (var name in buz) {
if (buz.hasOwnProperty(name)) {
console.log('this is fog (' +
name + ') for sure. Value: ' + buz[name]);
}
else {
console.log(name); // toString or something else
}
}
in
- 如果指定的属性在指定的对象或其原型链中,则
in运算符返回true。 - prop in object
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees // 返回true
3 in trees // 返回true
6 in trees // 返回false
"bay" in trees // 返回false (必须使用索引号,而不是数组元素的值)
"length" in trees // 返回true (length是一个数组属性)
"PI" in Math // 返回true
"toString" in {}; // 返回true
in右操作数必须是一个对象值。例如,你可以指定使用String构造函数创建的字符串,但不能指定字符串文字。
var color1 = new String("green");
"length" in color1 // 返回true
var color2 = "coral";
"length" in color2 // 报错(color2不是对象)
Object.prototype.toString()
- 语法:
obj.toString() - 返回值:一个表示该对象的字符串。
- 每个对象都有一个
toString()方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用 - 默认情况下,
toString()方法被每个Object对象继承。如果此方法在自定义对象中未被覆盖,toString()返回"[object type]",其中 type 是对象的类型。 toString()调用 null 返回[object Null],undefined返回[object Undefined]
var o = new Object();
o.toString(); // returns [object Object]
使用 toString() 检测对象类型
var toString = Object.prototype.toString;
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]