想必很多朋友面试的时候碰到过这问题
// 声明形式
var obj = {
key: 'value';
}
// 绝大多数内置对象也用这个形式来创建对象
// 构造形式
var obj = new Object();
obj.key = 'value';
// 很少使用这种形式来创建对象
// 只能逐个添加key/value
上述两种形式生成的对象是一样的
// 构造函数形式
function Fun(){
this.key = 'value';
};
var obj = new Fun();
// 连接原型链形式 -- 后面的原型链文章会详细讲,这里先了解
var obj = Object.create(null); // 创建一个对象,这个对象的[[prototype]]关联到null,返回这个对象给obj
obj.key = 'value';
对象有两种取值方式
- 属性访问:通过
.
操作符来取值。- 键访问:通过
[]
来取值
对象中,属性名永远是字符串
,其它值作为属性名,会被转换为字符串
。
两种取值方式的区别:
.
的方式,属性名需要满足标识符命名规范
(obj.ke-y就不符合规范)[]
可以接受任意UTF-8/Unicode字符串
作为属性名
使用表达式来设置属性名,这种形式称为
可计算属性名
let lastName = 'Wang'
var obj = {
['erGou' + lastName]: '男',
['xiaoHua' + lastName]: '女'
}
let gender = obj['erGou' + lastName];
tips:如果添加的属性名”看起来“像一个数字,他会变成下标
属性有
writable
、configurable
、enumerable
三个描述符,可以通过Object.defineProperty()
方法来修改描述符的值
可以通过Object.getOwnPropertyDescriptor(obj, "key");
来获取属性的描述符信息
决定是否可以修改属性值。
非严格模式
下修改属性值不报错,但修改失败。
严格模式
下修改属性值报错,抛出TypeError。
var obj = {
num1: 1
};
Object.defineProperty(obj, 'num1', {
writable: false
});
obj.num1 = 2;
console.log(obj.num1); // 1
决定是否可以使用
Object.defineProperty()
方法来修改属性描述符。
configurable
的值为false
,修改描述符,会产生一个TypeError
错误configurable
的值为false
的属性无法删除
- 把
configurable
修改为false
是单向操作,无法再改回true
例外:(嗯,就很无语....)
即便configurable
的值为false
,还可以把writable
从true
改为false
,但不能从false
改为true
。
var obj = {
num1: 1
};
Object.defineProperty(obj, 'num1', {
configurable: false
});
// 情况1 修改configurable值
Object.defineProperty(obj, 'num1', {
configurable: true
}); // Uncaught TypeError: Cannot redefine property: num1
// 情况2 删除configurable值为false的属性
delete obj.num1; // false 不会报错,但无法删除
// 情况3 例外的情况,可以修改writable为false但无法修改回来
Object.defineProperty(obj, 'num1', {
writable: false
}); // 可以修改成功
Object.defineProperty(obj, 'num1', {
writable: true
}); // Uncaught TypeError: Cannot redefine property: num1
结合writable: false
和configurable: false
可以创建一个真正的常量属性(不可配置、删除、修改)。
控制属性是否会出现在枚举中,值为
false
不会出现在枚举中,但仍然可以正常访问
var obj = {
num1: 1,
num2: 2,
num3: 3
};
Object.defineProperty(obj, 'num1', {
enumerable: false
});
for(key in obj) {
console.log(key);
} // num2、num3
如何判断一个属性是否可枚举呢?
使用Object.propertyIsEnumerable(key)
,返回true/false。只检查对象,不检查原型链
禁止对象
添加新属性
可以删除已有属性
非严格模式
下添加新属性,静默失败。
严格模式
下添加新属性,抛出TypeError
错误。
禁止对象
添加新属性
不能配置描述符(即把所有属性标记为configurable: false
)
效果:
- 不能添加新属性
- 不能配置描述符
- 不能删除现有属性
- 能修改属性值
禁止对象
添加新属性
不能配置描述符(即把所有属性标记为configurable: false
)
不能修改属性值(即把所有属性标记为writable: false
)
效果:
- 不能添加新属性
- 不能配置描述符
- 不能删除现有属性
- 不能修改属性值
注:value为引用类型的值,不受上述影响
getter
/setter
是一个隐藏函数(有默认值),在我们取/存
属性值时自动调用。(我们也可以改写getter
/setter
)
设置getter
/setter
的方法:
- 可以直接在对象中写
get
/set
方法,设置getter
和setter
- 也可以使用
Object.defineProperty()
来修改默认的getter
和setter
,但只能应用在单个属性上。
// 设置getter/setter的 方法1
var obj = {
get num1() { // 设置getter
return this._num1_;
},
set num1(num) { // 设置setter
this._num1_ = num;
}
}
obj.num1 = 2;
console.log(obj.num1); // 2
// 设置getter/setter的 方法2
var obj = {
num1: 1
}
Object.defineProperty(obj, 'num2', {
get: function() {
return this._num2_;
},
set: function(num) {
this._num2_ = num;
},
enumerable: true, // 在这种情况下创建新属性,enumerable默认为false
configurable: true // 在这种情况下创建新属性,configurable默认为false
// 在这种情况下创建新属性,没有writable属性,但是可写
});
obj.num2 = 2;
console.log(obj.num2); // 2
若只设置了getter
并返回一个固定值,那么set
操作会被忽略,即使设置了setter
。 因为getter
返回固定值,setter
没有意义。 所以一般来讲,getter
和setter
是成对出现的。
这个问题是不是又很面熟...
判断一个属性是否在,有两种方法in
和hasOwnProperty
,即使属性的enumerable
为fasle
,也能被找到
会检查属性是否在
对象
及[[Property]]
中
var obj = {
num1: 1
};
obj.__proto__.num2 = 2; // 把num2挂载到原型链
// 一般使用Object.creat()来挂载
console.log('num1' in obj); // true
console.log('num2' in obj); // true
只检查属性是否在
对象
,不检查[[Property]]
var obj = {
num1: 1
};
obj.__proto__.num2 = 2; // 把num2挂载到原型链
// 一般使用Object.creat()来挂载
console.log(obj.hasOwnProperty('num1')); // true
console.log(obj.hasOwnProperty('num2')); // false
有两种姿势,
Object.keys(obj)
、Object.getOwnPropertyNames(obj)
(他们都不检查原型链)。
返回所有
可枚举属性
的数组,只检查对象,不检查原型链
var obj = {
num1: 1,
num2: 2,
num3: 3
};
obj.__proto__.num0 = 0;
Object.defineProperty(obj, 'num3', {
enumerable: false
});
console.log(Object.keys(obj)); // [num1, num2]
// 一般开发使用此方法
返回所有属性
的数组,只检查对象,不检查原型链
var obj = {
num1: 1,
num2: 2,
num3: 3
};
obj.__proto__.num0 = 0;
Object.defineProperty(obj, 'num3', {
enumerable: false
});
console.log(Object.keys(obj)); // [num1, num2, num3]
遍历所有
可枚举
属性(包括原型链)。
数组按照下标顺序遍历,遍历拿到的是下标
。
对象按照随机顺序遍历,遍历拿到的是key(属性名)。
// 数组 for...in
var arr = [1, 2, 3];
for(idx in arr) {
console.log(idx);
} // 0 1 2
// 对象 for...in
var obj = {
num1: 1,
num2: 2
};
obj.__proto__.num3 = 3;
for(key in obj) {
console.log(key);
} // num1 num2 num3
遍历需要
迭代器
。
数组自带迭代器,可直接遍历,遍历拿到的是值
。
对象需要自己实现迭代器来进行迭代。
// 数组 for...of
var arr = [1, 2, 3];
for(val of arr) {
console.log(val);
} // 1 2 3
// 对象 for...of
var obj = {
num1: 1,
num2: 2,
num3: 3
};
// 实现一个迭代器
Object.defineProperty(obj, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function() {
var idx = 0;
var keys = Object.keys(this);
return {
next: () => {
return {
value: this[keys[idx++]],
done: (idx > keys.length)
};
}
};
}
});
for(val of obj) {
console.log(val);
} // 1 2 3