对象
关于对象的枚举和遍历,我们通常有几种方式去进行操作:
'xxx' in myObjectin操作符会检查该属性是否在该对象上或者其[[Prototype]]原型链上,无论该属性是否可枚举。
let myObject = {
a: 'b'
}
console.log('a' in myObject) // true
console.log('b' in myObject) // false
function createAKey(params) {
this.a = 'a'
}
let aInstance = new createAKey()
createAKey.prototype.a = 'a'
aInstance.b = 'b'
// 通过构造函数原型链找到的 a 这个属性值
console.log('a' in aInstance); // true
console.log('b' in aInstance); // true
hasOwnPropertyObejct.hasOwnProperty()只会检查该属性是否在该对象上,而不会沿着[[Proptotype]]链继续往下查找
let myObject = {
a: 'b'
}
myObject.hasOwnProperty('a') // true
myObject.hasOwnProperty('b') // false
function createAKey(params) {
this.a = 'a'
}
let aInstance = new createAKey()
createAKey.prototype.a = 'a'
aInstance.b = 'b'
// aInstance这个实例对象上没有a这个属性 所以返回的是false
aInstance.hasOwnProperty('a') // false
aInstance.hasOwnProperty('b') // true
getOwnPropertyNamesObject.getOwnPropertyNames会返回一个数组,数组包含了当前对象所有直接包含的属性(不包括其[[Prototype]]原型链上的属性),无论该属性是否可枚举。
function createAKey(params) {
}
createAKey.prototype.a = 'a'
let aInstance = new createAKey()
aInstance.b = 'b' // 默认为可枚举
Object.defineProperty(aInstance, 'c', {
value: 'c',
enumerable: true // 设置为可枚举
})
Object.defineProperty(aInstance, 'd', {
value: 'd',
enumerable: false // 设置为不可枚举
})
console.log(Object.getOwnPropertyNames(aInstance)) // ['b', 'c', 'd']
-
keysObject.keys()会返回一个数组,数组包含了当前对象所有直接包含的可枚举属性(不包括其[[Prototype]]原型链上的属性) -
for...infor...in遍历对象,只能遍历出对象中可以访问的属性及其[[Prototype]]原型链上可以访问的属性,通过defineProperty定义的不可枚举的属性是没办法遍历出来的。
function createAKey(params) {
}
createAKey.prototype.a = 'a'
let aInstance = new createAKey()
aInstance.b = 'b' // 默认为可枚举
Object.defineProperty(aInstance, 'c', {
value: 'c',
enumerable: true // 设置为可枚举
})
Object.defineProperty(aInstance, 'd', {
value: 'd',
enumerable: false // 设置为不可枚举
})
for (let v in aInstance) {
console.log(v);
}
// b c a
tips: for...in最好是用在对象上进行遍历,如果你在数组上用for...in进行循环遍历可能会出现不可预料的结果,因为这种遍历不仅会包含所有数组的索引,还会包含所有可枚举的属性,如果要去对数组进行遍历,那么最好用for...of来操作。
数组
对于数组的遍历我们通常用for...of、Array.forEach等一系列相关的操作方法
let arr = [1,4,2,3]
for (let val of arr) {
console.log(val);
}
// 1 4 2 3
for...in去遍历对象只能拿到对象的属性,而无法直接拿到对象的值,但是我们用for...of去遍历数组可以直接拿到索引对应的值,这其中是因为for...of会默认调用被访问对象的迭代器对象,然后通过调用迭代器对象的next方法来遍历所有返回值。
数组有内置的@@iterator,因此for...of可以直接用在数组上,我们也可以手动的内置的@@iterator来手动遍历数组:
let arr = [1,4,2,3]
let arrIt = arr[Symbol.iterator]()
console.log(arrIt.next()) // {value: 1, done: false}
console.log(arrIt.next()) // {value: 4, done: false}
console.log(arrIt.next()) // {value: 2, done: false}
console.log(arrIt.next()) // {value: 3, done: false}
console.log(arrIt.next()) // {value: undefined, done: true}
这里的@@iterator本身不是一个迭代器对象而是一个返回迭代器对象的函数,因为同一个数组生成的不同迭代器对象之间是互干扰的:
let arr = [1,4,2,3]
let arrItOne = arr[Symbol.iterator]()
let arrItTwo = arr[Symbol.iterator]()
console.log(arrItOne.next()) // {value: 1, done: false}
console.log(arrItTwo.next()) // {value: 1, done: false}
console.log(arrItOne.next()) // {value: 4, done: false}
console.log(arrItOne.next()) // {value: 2, done: false}
console.log(arrItTwo.next()) // {value: 4, done: false}
通过这个逻辑,我们可以给对象定义一个生成迭代器对象的方法,使得我们可以在对象上使用for...of
let aObj = {
a: 'aValue',
b: 'bValue',
[Symbol.iterator]: function () {
let o = this, idx = 0, ks = Object.keys(o), ksLength = ks.length
return {
next: () => {
return {
value: o[ks[idx++]],
done: (idx > ksLength)
}
}
}
}
}
Object.defineProperty(aObj, 'c', {
value: 'cValue',
enumerable: true // 设置为可枚举
})
Object.defineProperty(aObj, 'd', {
value: 'dValue',
enumerable: false // 设置为不可枚举
})
for (let v of aObj) {
console.log(v);
}
// aValue bValue cValue
由于我们用的Object.keys来获取的属性,因此这里的for...of只能返回该对象自己直接包含的可枚举属性的值
小结
对于对象,in 操作符和Object.hasOwnproperty()都会返回一个boolean值判断该属性是否存在于对象上,而这两者的区别在于是否会通过[[Prototype]]去进行查找。而Obeject.getOwnPropretyNames()和Object.keys都会反一个该对象直接包含的所有属性的数组,这两者的区别在于是否属性是否可以枚举。目前js中并没有内置的方法可以让我们获取所有in操作符能访问到的属性列表,我们只能去递归遍历某个对象的整条[[Prototye]]链并通过Object.keys()保存该层的所有属性。同时我们可以通过给对象定义一个他自己的@@iterator,让这个对象可以用for...of进行遍历
对于数组,我们通过通常通过for...of和Object.propertype上的一些列方法去进行遍历,在数组上我们最好不要用for...in去进行遍历,因为for...in不仅仅会遍历出数组所有的索引值,还会遍历出该数组的所有可枚举属性。
改文章是本人看了《你不知道的JavaScript》上卷的第三章后所记录的总结,所以文章中大量引用了书中的文字片段,如果读者觉得哪些地方写的不好可以直接提出来,同时也建议读者可以看看《你不知道的JavaScript》这本书,写的真的很好!。