红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 前端学习笔记
  3. 正文

JavaScript 学习笔记 1:Object

2026年4月7日 4点热度 0人点赞 0条评论

可迭代对象

for...of

可以用 for...of遍历数组:

let numbers = [1, 2, 3, 4, 5]
​
for (let num of numbers) {
    console.log(num)
}
​
// 1
// 2
// 3
// 4
// 5

或者遍历对象属性:

let person = {
    name: 'John',
    age: 30
}
for (let key in person) {
    console.log(key, person[key])
}
// name John
// age 30

还可以遍历字符串:

let message = 'Hello';
for (let char of message) {
    console.log(char)
}
// H
// e
// l
// l
// o

迭代器

实际上我们可以定义对象的迭代器器,以及具体的迭代行为:

let range = {
    from: 1,
    to: 5
}
​
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 (let num of range) {
    console.log(num)
}
​
// 1
// 2
// 3
// 4
// 5

就像示例中的那样,迭代器的 key 是符号类型Symbol.iterator,值是一个返回迭代器的对象,迭代器对象必须实现一个next方法,这个方法返回一个表示每次迭代结果的对象,改对象有两个属性(done和value),done 表示是否结束迭代,value 表示当前迭代获取到的值。

上面的实现是标准的迭代器设计模式,将迭代器与原始对象分离,当然也可以简单地使用原始对象作为迭代器:

let range2 = {
    from: 1,
    to: 5,
    [Symbol.iterator]() {
        this.current = this.from
        return this;
    },
    next() {
        if (this.current <= this.to) {
            return { done: false, value: this.current++ }
        } else {
            return { done: true }
        }
    }
}
​
for (let num of range2) {
    console.log(num)
}
​
// 1
// 2
// 3
// 4
// 5

从细节上讲,两者是有区别的,第一种每次迭代开始时,获取到的是全新的迭代器对象,第二种则获取到的是同一个迭代器对象(原始对象),只不过重置了迭代标识(current)罢了。这个区别在单线程迭代的时候时没问题的,但如果多线程迭代,两个线程就会互相影响,导致bug。在 JS 中这个问题似乎几乎没有影响,因为 JS 是单线程的(大多数情况下)。

可迭代对象和类数组

可迭代对象和类数组虽然有点类似,但它们的定义不同:

  • 可迭代对象:实现了 Symbol.iterator 方法的对象。

  • 类数组:有数字索引和 length 属性的对象。

对于可迭代对象,可以使用for..of进行遍历,类数组则不行,除非它同时也是一个可迭代对象。比如字符串,即是可迭代对象,又是类数组。

Array.from

使用Array.from可以将一个可迭代对象或类数组转换为数组:

let array = Array.from(range2);
console.log(array);
// [ 1, 2, 3, 4, 5 ]
​
let arrayLike = {
    '0': 'Hello',
    '1': 'World',
    '2': '!',
    length: 3
}
​
let arr = Array.from(arrayLike);
console.log(arr);
// [ 'Hello', 'World', '!' ]

遍历属性

Object.keys

let salaries = {
  "John": 100,
  "Pete": 300,
  "Mary": 250
};
​
console.log(Object.keys(salaries));
// [ 'John', 'Pete', 'Mary' ]
console.log(Object.values(salaries));
// [ 100, 300, 250 ]
console.log(Object.entries(salaries));
// [ [ 'John', 100 ], [ 'Pete', 300 ], [ 'Mary', 250 ] ]

就像示例中展示的,可以用这些方法遍历属性:

  • Object.keys,返回对象属性的 key 组成的数组

  • Object.values,返回对象属性的值组成的数组

  • Object.entries,返回对象属性的键值对组成的数组

可以利用这些方法获取对象的属性个数:

function count(obj) {
  return Object.keys(obj).length;
}
​
console.log(count(salaries));
// 3

再看一个示例,遍历属性计算总和:

function sumSalaries(salaries) {
  let sum = 0;
  for (let salary of Object.values(salaries)) {
    sum += salary;
  }
  return sum;
}
​
console.log(sumSalaries(salaries));
console.log(sumSalaries({}));

Object.fromEntries

使用Object.fromEntries可以将键值对数组“转换”为对象:

let person = Object.fromEntries([["name", "John"], ["age", 30]]);
console.log(person);
// { name: 'John', age: 30 }

比如利用这一点获取一个金额翻倍的新对象:

let salaries2 = {
  "John": 100,
  "Pete": 300,
  "Mary": 250
};
​
function doubleSalaries(salaries) {
  let entries = Object.entries(salaries).map(([name, salary]) => [name, salary * 2])
  return Object.fromEntries(entries);
}
​
console.log(doubleSalaries(salaries2));
// { John: 200, Pete: 600, Mary: 500 }

Map

Map 与其他语言中的用法类似:

let map = new Map();
map.set('name', 'John');
map.set('age', 30);
console.log(map.get('name'));
console.log(map.get('age'));
// John
// 30
console.log(map.size);
// 2

区别是 Map 对于 key 的要求更为宽泛,比如可以使用对象作为 key,这在对象属性定义时是不行的:

let emails = new Map();
let john = { name: 'John' };
let jack = { name: 'Jack' };
emails.set(john, 'john@mail');
emails.set(jack, 'jack@mail');
console.log(emails.get(john));
console.log(emails.get(jack));
// john@mail
// jack@mail

与 Java 实现 HashMap 时使用对象的hashCode()和equals()方法比对不同,javascript 使用===比较判断 Map 的 key 是否相同。

此外,Map 的 key key 如果是数字,它们不会被转换为字符串(像对象的 key 那样):

let map2 = new Map();
map2.set(1, 'one');
map2.set('1', 'two');
console.log(map2.size);
console.log(map2.get(1));
console.log(map2.get('1'));
// 2
// one
// two

基于上面的原因,不建议使用map[...]的方式访问 Map 的属性,这样做会把 Map 对象作为一个普通对象进行处理,推荐使用map.get(...)获取 Map 的属性。

Map 的 Set 方法返回的是 Map 对象自己,因此可以链式调用:

let map3 = new Map();
map3.set(true, 'boolean')
    .set(1, 'number');
console.log(map3.get(true));
console.log(map3.get(1));
// boolean
// number

遍历 Map

Map 是一个可迭代对象,迭代器返回的值是一个键值对:

let map4 = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three']
]);
​
for (let [key, value] of map4) {
    console.log(key + ': ' + value);
}
​
// 1: one
// 2: two
// 3: three

与遍历对象属性类似,Map 也有类似的方法:

  • map.keys

  • map.values

  • map.entries

let map5 = new Map([
    ['1', 'one'],
    ['2', 'two'],
    ['3', 'three']
]);
for (let [key, value] of map5.entries()) {
    console.log(key + ': ' + value);
}
​
// 1: one
// 2: two
// 3: three

效果与之前相同,只不过这里不是隐式使用迭代器进行遍历,而是显式通过entries方法获取到的键值对迭代器进行遍历。需要注意的是,Map 的这些方法返回的并不是数组,而是可迭代对象,虽然在使用forEach遍历时两者表现是相同的,但它们的数据类型并不相同。

将对象转换为 Map

Map 的构造器可以接收一个键值对数组用以初始化 Map:

let map6 = new Map([
    ["name", "jack"],
    ["age", 16]
]);
console.log(map6.get("name"));

可以利用这一点实现将普通对象转换为 Map 对象:

function getMapFromObj(obj){
    if(!obj){
        return null;
    }
    return new Map(Object.entries(obj));
}
​
let map7 = getMapFromObj({name: 'jack', age: 16});
console.log(map7.get('name'));
console.log(map7.get('age'));
// jack
// 16

将 Map 转换为对象

类似的,也可以利用这一点将 Map 转换为普通对象:

function getObjFromMap(map){
    if(!map){
        return null;
    }
    return Object.fromEntries(map.entries());
}
​
let map8 = new Map([
    ['name', 'jack'],
    ['age', 16]
]);
​
let obj = getObjFromMap(map8);
console.log(obj.name);
console.log(obj.age);
// jack
// 16

实际上这里可以简化一点,可以使用Object.fromEntries(map),因为fromEntries方法除了接收一个键值对数组外,还可以接收一个可迭代对象。

Set

Set 与 Map 类似,不过只存储不重复的值:

let set = new Set();
let person1 = { name: 'jack' };
let person2 = { name: 'tom' };
let person3 = { name: 'lucy' };
set.add(person1).add(person2).add(person3);
set.add(person1);
console.log(set.size);
// 3

可以使用 for...of遍历 Set:

for (let person of set) {
    console.log(person.name);
}
// jack
// tom
// lucy

也可以使用forEach方法,不过有点奇怪:

set.forEach((person, personAgain, set) => {
    console.log(person.name);
});
// jack
// tom
// lucy

方法中被遍历的元素在匿名函数列表中出现了两次(person 和 personAgain),这么做是为了和 Map 的 forEach 方法保持形式上的兼容。

The End.

本文的所有示例代码可以从这里获取。

参考资料

  • 现代 JavaScript 教程

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2026年4月7日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号