朋友们,美好的一天!
该注释没有特别的实用价值。另一方面,它探索了您可能会发现有趣的JavaScript的“边界线”功能。
Goggle的JavaScript样式指南建议您在可能的情况下优先考虑优先。
Airbnb JavaScript样式指南不鼓励使用迭代器。代替for-in和for-of循环,您应该使用高阶函数,例如map(),every(),filter(),find(),findIndex(),reduce(),some()遍历数组和Object .keys(),Object.values(),Object.entries()来迭代对象数组。以后再说。
让我们回到Google。“在可能的情况下”是什么意思?
让我们看几个例子。
假设我们有一个像这样的数组:
const users = ["John", "Jane", "Bob", "Alice"];
我们希望将其元素的值输出到控制台。我们如何做到这一点?
//
log = (value) => console.log(value);
// for
for (let i = 0; i < users.length; i++) {
log(users[i]); // John Jane Bob Alice
}
// for-in
for (const item in users) {
log(users[item]);
}
// for-of
for (const item of users) {
log(item);
}
// forEach()
users.forEach((item) => log(item));
// map()
// -
// forEach()
users.map((item) => log(item));
一切正常,无需我们付出任何额外的努力。
现在,假设我们有一个像这样的对象:
const person = {
name: "John",
age: 30,
job: "developer",
};
我们也想这样做。
// for
for (let i = 0; i < Object.keys(person).length; i++) {
log(Object.values(person)[i]); // John 30 developer
}
// for-in
for (const i in person) {
log(person[i]);
}
// for-of & Object.values()
for (const i of Object.values(person)) {
log(i);
}
// Object.keys() & forEach()
Object.keys(person).forEach((i) => log(person[i]));
// Object.values() & forEach()
Object.values(person).forEach((i) => log(i));
// Object.entries() & forEach()
Object.entries(person).forEach((i) => log(i[1]));
看到不同?我们必须诉诸其他技巧,其中包括以一种或另一种方式将对象转换为数组,因为:
for (const value of person) {
log(value); // TypeError: person is not iterable
}
这个异常告诉我们什么?它说对象“人”与其他任何对象一样,不是可迭代的,也不是可迭代的(可迭代的)实体。
关于什么可迭代和迭代器在Modern JavaScript Tutorial的这一部分中写得很好。经您允许,我不会复制粘贴。但是,我强烈建议您花20分钟阅读它。否则,进一步的介绍对您没有太大意义。
假设我们不喜欢对象不是可迭代的,而我们想改变它。我们如何做到这一点?
这是Ilya Kantor给出的示例:
//
const range = {
from: 1,
to: 5,
};
// Symbol.iterator
range[Symbol.iterator] = function () {
return {
//
current: this.from,
//
last: this.to,
//
next() {
//
if (this.current <= this.last) {
// ,
return { done: false, value: this.current++ };
} else {
// ,
return { done: true };
}
},
};
};
for (const num of range) log(num); // 1 2 3 4 5
// !
本质上,提供的示例是使用迭代器创建的生成器。但是回到我们的目标。将常规对象变成可迭代对象的函数可能如下所示:
const makeIterator = (obj) => {
// "size", "length"
Object.defineProperty(obj, "size", {
value: Object.keys(obj).length,
});
obj[Symbol.iterator] = (
i = 0,
values = Object.values(obj)
) => ({
next: () => (
i < obj.size
? { done: false, value: values[i++] }
: { done: true }
),
});
};
我们检查:
makeIterator(person);
for (const value of person) {
log(value); // John 30 developer
}
发生了!现在,我们可以轻松地将此类对象转换为数组,并通过“ size”属性获取其元素数:
const arr = Array.from(person);
log(arr); // ["John", 30, "developer"]
log(arr.size); // 3
我们可以通过使用生成器而不是迭代器来简化函数代码:
const makeGenerator = (obj) => {
//
//
Object.defineProperty(obj, "isAdult", {
value: obj["age"] > 18,
});
obj[Symbol.iterator] = function* () {
for (const i in this) {
yield this[i];
}
};
};
makeGenerator(person);
for (const value of person) {
log(value); // John 30 developer
}
const arr = [...person];
log(arr); // ["John", 30, "developer"]
log(person.isAdult); // true
创建可迭代对象后,我们可以立即使用“ next”方法吗?
log(person.next().value); // TypeError: person.next is not a function
为了使我们有这个机会,我们必须首先调用对象的Symbol.iterator:
const iterablePerson = person[Symbol.iterator]();
log(iterablePerson.next()); // { value: "John", done: false }
log(iterablePerson.next().value); // 30
log(iterablePerson.next().value); // developer
log(iterablePerson.next().done); // true
值得注意的是,如果需要创建一个可迭代的对象,最好立即在其中定义Symbol.iterator。以我们的对象为例:
const person = {
name: "John",
age: 30,
job: "developer",
[Symbol.iterator]: function* () {
for (const i in this) {
yield this[i];
}
},
};
继续。去哪儿?进入元编程。如果我们想通过索引来获取对象属性的值,例如在数组中怎么办?如果我们希望对象的某些属性是不可变的,那该怎么办?让我们使用proxy实现此行为。为什么要使用代理?好吧,如果仅仅是因为我们可以:
const makeProxy = (obj, values = Object.values(obj)) =>
new Proxy(obj, {
get(target, key) {
//
key = parseInt(key, 10);
// , 0
if (key !== NaN && key >= 0 && key < target.size) {
//
return values[key];
} else {
// ,
throw new Error("no such property");
}
},
set(target, prop, value) {
// "name" "age"
if (prop === "name" || prop === "age") {
//
throw new Error(`this property can't be changed`);
} else {
//
target[prop] = value;
return true;
}
},
});
const proxyPerson = makeProxy(person);
//
log(proxyPerson[0]); // John
//
log(proxyPerson[2]); // Error: no such property
//
log((proxyPerson[2] = "coding")); // true
//
log((proxyPerson.name = "Bob")); // Error: this property can't be changed
从这一切中我们可以得出什么结论?当然,您可以自己创建一个可迭代的对象(JavaScript,宝贝),但是问题是为什么。我们同意《 Airbnb指南》的观点,即有足够多的本机方法来解决与遍历对象的键和值有关的全部任务,因此无需``重新发明轮子''。可以通过以下事实来阐明Google的指南:for-of循环对于对象的数组和对象数组应该是首选的,对于这样的对象,可以使用for-in循环,但更好的是内置函数。
希望您发现自己感兴趣的东西。感谢您的关注。