Js操作string

Js操作string

String 里有什么

1
2
3
console.log(Object.getOwnPropertyNames(String))
console.log(Object.getOwnPropertyNames(String.prototype))
console.log(Object.getOwnPropertyNames(new String()))
1
2
3
(6) ['length', 'name', 'prototype', 'fromCharCode', 'fromCodePoint', 'raw']
(52) ['length', 'constructor', 'anchor', 'at', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'endsWith', 'fontcolor', 'fontsize', 'fixed', 'includes', 'indexOf', 'isWellFormed', 'italics', 'lastIndexOf', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'replaceAll', 'search', 'slice', 'small', 'split', 'strike', 'sub', 'substr', 'substring', 'sup', 'startsWith', 'toString', 'toWellFormed', 'trim', 'trimStart', 'trimLeft', 'trimEnd', 'trimRight', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toUpperCase', 'valueOf']
['length']

!String.png

String 自身属性

String.prototype

String.prototype 上有很多实用的方法,以下是一些常见的:

字符串查找方法 11

  • charAt(index)
    • 描述:返回指定索引处的字符。如果索引超出范围,返回空字符串。
    • 示例'hello'.charAt(1) 返回 'e'
  • charCodeAt(index)
    • 描述:返回指定索引处字符的 Unicode 编码值。如果索引超出范围,返回 NaN
    • 示例'hello'.charCodeAt(1) 返回 101(字符 'e' 的 Unicode 编码)
  • codePointAt(pos)
    • 描述:返回一个指定字符串位置的 Unicode 编码点值的非负整数。对于超出范围的索引,返回 undefined
    • 示例'a'.codePointAt(0) 返回 97
  • indexOf(searchString, position)
    • 描述:返回指定值(子字符串)首次出现的索引号,从指定位置开始搜索。如果未找到,则返回 -1
    • 示例'hello'.indexOf('l') 返回 2
  • lastIndexOf(searchString, position)
    • 描述:返回指定值(子字符串)最后一次出现的索引号,从指定位置开始搜索。如果未找到,则返回 -1
    • 示例'hello'.lastIndexOf('l') 返回 3
  • startsWith(searchString, position)
    • 描述:判断当前字符串是否以指定的子字符串开头。可指定开始搜索的位置。
    • 示例'hello'.startsWith('he') 返回 true
  • endsWith(searchString, length)
    • 描述:判断当前字符串是否以指定的子字符串结尾。可指定搜索的长度。
    • 示例'hello'.endsWith('lo') 返回 true
  • includes(searchString, position)
    • 描述:判断当前字符串是否包含指定的子字符串。可指定开始搜索的位置。
    • 示例'hello'.includes('ll') 返回 true
  • match
  • matchAll
  • search

字符串截取方法 3

  • slice(start, end)
    • 描述:提取字符串的某个部分,并以新的字符串返回被提取的部分。不改变原字符串。start 为开始提取的索引,end 为结束提取的索引(不包括该索引处的字符)。如果 end 省略,则提取到字符串末尾。
    • 示例'hello'.slice(1, 4) 返回 'ell'
  • substring(start, end)
    • 描述:返回一个字符串在开始索引到结束索引之间的一个子集,不包括结束索引处的字符。如果 start 大于 end,则自动交换这两个参数。不改变原字符串。
    • 示例'hello'.substring(1, 4) 返回 'ell'
  • substr(start, length)
    • 描述:从字符串中截取从 start 索引开始的指定 length 的字符。如果 length 为负数,则返回空字符串。
    • 示例'hello'.substr(1, 3) 返回 'ell'

字符串替换方法

  • replace(searchValue, replaceValue)
    • 描述:在字符串中查找匹配项,然后使用新的子字符串替换匹配项。searchValue 可以是字符串或正则表达式,replaceValue 可以是字符串或函数。如果 searchValue 是字符串,则只替换第一个匹配项;如果是正则表达式,使用全局标志(g)可以替换所有匹配项。
    • 示例'hello'.replace('l', 'a') 返回 'healo''hello'.replace(/l/g, 'a') 返回 'heaao'

字符串转换方法 7

  • toLowerCase()
    • 描述:将字符串转换为小写。
    • 示例'HeLLo'.toLowerCase() 返回 'hello'
  • toUpperCase()
    • 描述:将字符串转换为大写。
    • 示例'HeLLo'.toUpperCase() 返回 'HELLO'
  • toLocaleLowerCase()
    • 描述:根据本地环境的大小写规则,将字符串转换为小写。与 toLowerCase() 类似,但在某些语言中可能有细微差别。
    • 示例'HeLLo'.toLocaleLowerCase() 返回 'hello'
  • toLocaleUpperCase()
    • 描述:根据本地环境的大小写规则,将字符串转换为大写。与 toUpperCase() 类似,但在某些语言中可能有细微差别。
    • 示例'HeLLo'.toLocaleUpperCase() 返回 'HELLO'
  • trim()
    • 描述:去除字符串两端的空白字符,包括空格、制表符、换行符等。
    • 示例' hello '.trim() 返回 'hello'
  • trimStart() / trimLeft()
    • 描述:去除字符串左侧的空白字符。
    • 示例' hello'.trimStart() 返回 'hello'
  • trimEnd() / trimRight()
    • 描述:去除字符串右侧的空白字符。
    • 示例'hello '.trimEnd() 返回 'hello'

字符串分割方法 1

  • split(separator, limit)
    • 描述:根据指定的分隔符将字符串分割成数组。separator 可以是字符串或正则表达式,limit 用于指定返回数组的最大长度。
    • 示例'hello world'.split(' ') 返回 ['hello', 'world']

字符串填充方法

  • padStart(targetLength, padString)
    • 描述:用指定的字符串填充当前字符串,直到达到目标长度。填充在字符串的开头进行。
    • 示例'5'.padStart(3, '0') 返回 '005'
  • padEnd(targetLength, padString)
    • 描述:用指定的字符串填充当前字符串,直到达到目标长度。填充在字符串的末尾进行。
    • 示例'5'.padEnd(3, '0') 返回 '500'

字符串重复方法

  • repeat(count)
    • 描述:将当前字符串重复指定次数,并返回新的字符串。
    • 示例'ha'.repeat(3) 返回 'hahahaha'

字符串模板方法

  • anchor(name)
    • 描述:创建一个 HTML 锚点,用于创建超链接。
    • 示例'hello'.anchor('anchor1') 返回 '<a name="anchor1">hello</a>'
  • big()
    • 描述:创建一个大号文本的 HTML 字符串。
    • 示例'hello'.big() 返回 '<big>hello</big>'
  • blink()
    • 描述:创建一个闪烁文本的 HTML 字符串。
    • 示例'hello'.blink() 返回 '<blink>hello</blink>'
  • bold()
    • 描述:创建一个粗体文本的 HTML 字符串。
    • 示例'hello'.bold() 返回 '<b>hello</b>'
  • fixed()
    • 描述:创建一个等宽字体文本的 HTML 字符串。
    • 示例'hello'.fixed() 返回 '<tt>hello</tt>'
  • fontcolor(color)
    • 描述:创建一个指定颜色文本的 HTML 字符串。
    • 示例'hello'.fontcolor('red') 返回 '<font color="red">hello</font>'
  • fontsize(size)
    • 描述:创建一个指定大小文本的 HTML 字符串。
    • 示例'hello'.fontsize(4) 返回 '<font size="4">hello</font>'
  • italics()
    • 描述:创建一个斜体文本的 HTML 字符串。
    • 示例'hello'.italics() 返回 '<i>hello</i>'
  • link(url)
    • 描述:创建一个超链接文本的 HTML 字符串。
    • 示例'hello'.link('https://www.example.com') 返回 '<a href="https://www.example.com">hello</a>'
  • small()
    • 描述:创建一个小号文本的 HTML 字符串。
    • 示例'hello'.small() 返回 '<small>hello</small>'
  • strike()
    • 描述:创建一个删除线文本的 HTML 字符串。
    • 示例'hello'.strike() 返回 '<strike>hello</strike>'
  • sub()
    • 描述:创建一个下标文本的 HTML 字符串。
    • 示例'hello'.sub() 返回 '<sub>hello</sub>'
  • sup()
    • 描述:创建一个上标文本的 HTML 字符串。
    • 示例'hello'.sup() 返回 '<sup>hello</sup>'

字符串编码方法

  • anchor(name)
    • 描述:对字符串进行 URI 编码。
    • 示例'https://www.example.com?name=hello world'.encodeURI() 返回 'https://www.example.com?name=hello%20world'
  • encodeURIComponent()
    • 描述:对 URI 组件进行编码。
    • 示例'hello world'.encodeURIComponent() 返回 'hello%20world'
      这些方法为我们处理字符串提供了强大的功能,可以根据实际需求选择合适的方法来操作字符串。

JavaScript 字符串的操作

JavaScript 中对字符串(String)的操作非常丰富,以下是一些基本的字符串操作方法:

  1. 连接字符串
    • 使用 + 操作符:var str = "Hello" + "World";
    • 使用 concat() 方法:var str = "Hello".concat("World");
  2. 字符串长度
    • 使用 length 属性:var str = "Hello"; console.log(str.length); // 输出 5
  3. 字符串查找
    • indexOf():返回指定值第一次出现的索引,如果未找到则返回 -1。
    • lastIndexOf():返回指定值最后一次出现的索引,如果未找到则返回 -1。
    • includes():检查字符串是否包含指定的子字符串,返回布尔值。
    • startsWith():检查字符串是否以指定的子字符串开始,返回布尔值。
    • endsWith():检查字符串是否以指定的子字符串结束,返回布尔值。
  4. 字符串替换
    • replace():替换字符串中满足条件的部分。
    • replaceAll():替换字符串中所有满足条件的部分。
  5. 字符串分割
    • split():使用指定的分隔符将字符串分割成数组。
  6. 字符串转换
    • toLowerCase():转换为小写。
    • toUpperCase():转换为大写。
    • trim():去除字符串两端的空白字符。
  7. 字符串截取
    • slice():提取字符串的某个部分。
    • substring():提取字符串中的子串。
    • substr():提取字符串中指定位置的子串。
  8. 字符串填充
    • padStart():用另一个字符串填充当前字符串(可重复),以便结果字符串达到给定的长度。
    • padEnd():用另一个字符串填充当前字符串(可重复),以便结果字符串达到给定的长度。
  9. 字符串重复
    • repeat():返回一个新字符串,它是将原字符串重复指定次数后的结果。
  10. 字符串比较
  • localeCompare():用本地特定顺序进行字符串比较。

1 长度 & 查找单个字符

1
2
3
4
5
6
7
8
9
10
11
var str = "Hello world"
console.log(str.length)
//在控制台输出长度为11
console.log(str[0])
//输出H
console.log(str.charAt(0))
//输出H
console.log(str[20])
console.log(str.charAt(20))
//undefined
//(空字符串)

2 修改字符串

字符串定义之后是可以针对单个索引的值进行修改的

1
2
3
4
var str = "Hello world"
str[1] = "O"
console.log(str[1])
//输出Hello world

2.1 小写toLowerCase & ## 大写toUpperCase

1
2
3
4
5
var str = "Hello World"
console.log(str.toLowerCase());
//hello world
console.log(str.toUpperCase());
//HELLO WORLD

3 查找字符串

3.1 查找字符串所在位置indexOf

1
str.indexOf(searchValue [, fromIndex] )
  • 从fromIndex开始,查找searchValue的索引;
  • 如果没有找到,那么返回-1;
1
2
3
4
5
6
7
var str = "Hello World"
//找o,从索引值为1开始查找
console.log(str.indexOf("o", 1));
//4
//找jack,找不到
console.log(str.indexOf("jack", 0));
//-1

3.2 是否包含字符串includes

1
str.includes(searchString [, position] )
  • 从position位置开始查找searchString,
  • 根据情况返回 true 或 false
  • ES6 新特性
1
2
3
var str = "Hello World"
console.log(str.includes("o"))
//true

3.3 是否XXX字符串开头

1
str.startsWith(searchString[, postion])
  • 从position位置开始,判断字符串是否以searchString开头
  • 根据情况返回 true 或 false
  • ES6新特性

3.4 是否XXX字符串结尾

1
str.endsWith(searchString[, length])
  • 在length长度内,判断字符串是否以searchString结尾
  • 根据情况返回 true 或 false
  • ES6新特性
1
2
3
4
5
var msg = "Hello World"
console.log(msg.endsWith("o"))
//false 不是以o结尾的
console.log(msg.endsWith("o",5))
//true

4 替换字符串replace

1
stringObject.replace(regexp/substr,replacement)
  • 查找到对应的字符串,并且使用新的字符串进行替换;
  • 可以使用正则表达式
  • replacement 也可以传入函数来替换 ,其中 $ 需要符合正则语法
    • $1、$2、…、$99 与 regexp 中的第 1 到第 99 个子表达式相匹配的文本
    • $ 对应前面正则里的()
  • 返回新字符串,不修改原字符串
  • JavaScript replace() 方法
1
2
3
4
5
var msg = "Hello World"
console.log(msg.replace("Hello","HI"))
console.log(msg)
//HI World
//Hello World

使用正则交换2个单词

1
2
3
var msg = "Hello World"
console.log(msg.replace(/(\w+)\s* \s*(\w+)/g, "$2 $1"))
//World Hello

把字符串中所有单词的首字母都转换为大写

1
2
3
4
5
6
7
8
9
10
msg = 'aaa bbb ccc';
msg2 = name.replace(/\b\w+\b/g, function(word){
console.log(word)
return word.substring(0,1).toUpperCase()+word.substring(1);}
)
//aaa
//bbb
//ccc
console.log(msg,msg2)
//aaa bbb ccc Aaa Bbb Ccc

可以看到function里是正则找到一个字符串处理一个字符串。
str.search()str.match() 也可以使用正则表达式,详见RegExp

1
2
3
var str="Visit Runoob!"; 
var n=str.search("Runoob");
//6
1
2
3
var str="The rain in SPAIN stays mainly in the plain"
var n=str.match(/ain/g);
//['ain', 'ain', 'ain']

5 截取字符串subStr &substring &slice

方法 入参 负值参数
slice(start,end) 从start到end(不含end) 负值视为从字符串尾部开始算
substring(start,end) 从start到end(不含end) 负值视为0
substr(start,length) 从start开始获取length长度的字符串 start可以是负值

5.1 slice

1
string.slice(start,end)
  • 可提取字符串的某个部分,并以新的字符串返回被提取的部分
  • 入参可以负值,负值时,从字符串尾部开始计算
  • end省略时,默认为字符串尾部
1
2
3
4
5
6
7
8
9
10
11
//chrome控制台
var a = '0123456789'
a.slice(2,1)
//2所在的位置是'2',1所在的位置是'1',从2=>1,方向相反,返回为空
//""
a.slice(-2,1)
//-2所在的位置是'8',1所在的位置是'1',从8=>1,方向相反,返回为空
//""
a.slice(-2,-1)
//-2所在的位置是'8',-1所在的位置是'9',从8=>9,方向正常,返回为'8'
//"8"

5.2 substring

1
string.substring(start,end)
  • substring() 方法用于提取字符串中介于两个指定下标之间的字符
  • start end 非负整数 end比要提取的子串的最后一个字符在 string 中的位置多 1
  • end省略时,默认为字符串尾部
  • 如果 start 和 end 有负数,那么会把该参数自动转为0
  • 如果 start 比 end 大,那么该方法在提取子串之前会先交换这两个参数
  • 如果 start 和 end 有正小数,那么会把该参数向下取整
  • 如果 start 和 end 有字符串,那么会先进行parseInt(),如果转换结果为NaN,那么就转换为0,其余情况继续上述规则
1
2
3
4
//chrome控制台
var a = '0123456789'
a.substring(1,5)
"1234"

5.3 substr

1
string.substr(start,length)
  • substr() 方法可在字符串中抽取从 start 下标(包括)开始的指定数目的字符
  • start可以取负值,表示从字符串尾部往头部开始数(从右到左,注意是从1开始,比如-1 指字符串中最后一个字符),但是截取长度还是从左到右,如果超过最末端就到最末端结束
  • 如果length为负值,那么会直接当成0处理
  • 如果省略length参数,那么返回从string的开始位置到结尾的字串

6 拼接字符串concat

1
string.concat(string2, string3[, ..., stringN]);
  • 将两个或多个字符串,并返回一个新的单字符串
  • Array也有该方法
1
2
3
var str = "Hello".concat("-World","-aaa")
console.log(str)
//Hello-World-aaa

7 字符串分割split

1
str.split([separator[, limit] ] )
  • separator:以什么字符串就行分割,也可以是正则表达式
  • limit:限制返回片段的数量 ,忽略时返回全部
  • 返回一个包含结果集的数组
1
2
3
var msg = "Hello World"
console.log(msg.split(" ",2))
// ['Hello', 'World']

8 删除首尾空格trim

1
2
3
var msg = "           Hello World            "
console.log(msg.trim())
//Hello World

JavaScript中常见的字符串(String)方法

  1. String.prototype.charAt(index):返回指定位置的字符。
  2. String.prototype.concat(value):连接两个或多个字符串。
  3. String.prototype.slice(start, end):提取字符串的某个部分,返回一个新字符串。
  4. String.prototype.indexOf(searchValue, fromIndex):返回指定值在调用该方法的字符串中首次出现的位置。
  5. String.prototype.lastIndexOf(searchValue, fromIndex):返回指定值在调用该方法的字符串中最后出现的位置。
  6. String.prototype.toLowerCase():将所有大写字母转换为小写字母。
  7. String.prototype.toUpperCase():将所有小写字母转换为大写字母。
  8. String.prototype.trim():删除字符串两端的空白字符。
  9. String.prototype.trimStart():删除字符串开头的空白字符。
  10. String.prototype.trimEnd():删除字符串末尾的空白字符。
  11. String.prototype.replace(searchValue, newValue):在字符串中查找匹配的子字符串,并将其替换为新的子字符串。
  12. String.prototype.split(separator, limit):使用指定的分隔符将字符串分割成子字符串数组。
  13. String.prototype.substring(indexStart, indexEnd):提取字符串中两个指定索引之间的字符。
  14. String.prototype.includes(searchValue, fromIndex):检查一个字符串是否包含在另一个字符串中,并根据情况返回truefalse
  15. String.prototype.repeat(count):将字符串重复指定次数。
  16. String.prototype.startsWith(searchValue, position):检查字符串是否以指定的子字符串开始。
  17. String.prototype.endsWith(searchValue, length):检查字符串是否以指定的子字符串结束。

正则

以下是一些与正则表达式相关的字符串操作方法:

1. match(regexp)

  • 描述:对字符串执行匹配搜索,并将匹配的结果作为数组返回。如果正则表达式中包含全局标志(g),则返回所有匹配项的数组;如果不包含全局标志,则返回一个数组,其中第一个元素是匹配的字符串,后面的元素是匹配的捕获组。
  • 示例
    1
    2
    3
    4
    5
    let str = 'hello 123 world';
    let regex1 = /\d+/;
    let regex2 = /\d+/g;
    console.log(str.match(regex1)); // ['123', index: 6, input: 'hello 123 world', groups: undefined]
    console.log(str.match(regex2)); // ['123']

2. matchAll(regexp)

  • 描述:返回一个包含所有匹配项的迭代器。每次迭代返回一个数组,数组的第一个元素是匹配的字符串,后面的元素是匹配的捕获组。正则表达式必须包含全局标志(g)。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    let str = 'hello 123 world 456';
    let regex = /\d+/g;
    let matches = str.matchAll(regex);
    for (let match of matches) {
    console.log(match);
    }
    // ['123', index: 6, input: 'hello 123 world 456', groups: undefined]
    // ['456', index: 18, input: 'hello 123 world 456', groups: undefined]

3. replace(searchValue, replaceValue)

  • 描述:在字符串中查找匹配项,然后使用新的子字符串替换匹配项。searchValue 可以是正则表达式,如果使用正则表达式,可以实现更复杂的匹配和替换逻辑,如使用捕获组等。
  • 示例
    1
    2
    3
    4
    5
    6
    let str = 'hello 123 world';
    let regex = /(\d+)/;
    let newStr = str.replace(regex, 'abc');
    console.log(newStr); // 'hello abc world'
    let newStr2 = str.replace(regex, '$1-$1');
    console.log(newStr2); // 'hello 123-123 world'

4. search(regexp)

  • 描述:执行匹配搜索,返回匹配项的索引。如果没有找到匹配项,则返回 -1。与 match 方法不同,search 方法不返回匹配的字符串,只返回索引。
  • 示例
    1
    2
    3
    let str = 'hello 123 world';
    let regex = /\d+/;
    console.log(str.search(regex)); // 6

5. split(separator, limit)

  • 描述:根据指定的分隔符将字符串分割成数组。separator 可以是正则表达式,使用正则表达式可以实现更复杂的分割逻辑,如根据多个条件分割等。
  • 示例
    1
    2
    3
    4
    let str = 'hello 123,world 456';
    let regex = /[\s,]+/;
    let arr = str.split(regex);
    console.log(arr); // ['hello', '123', 'world', '456']
    这些方法结合正则表达式使用,可以极大地增强字符串操作的灵活性和功能,能够满足各种复杂的字符串处理需求。

Symbol

Symbol 对象

Symbol

在 JavaScript 中,Symbol 是一种基本数据类型,用于创建唯一的、不可变的值。Symbol 对象通常用于对象属性的键,以确保属性名的唯一性,避免命名冲突。以下是 Symbol 对象的一些主要特点和用法:

1. 创建 Symbol

使用 Symbol() 函数可以创建一个新的 Symbol 对象。Symbol 函数可以接受一个可选的描述字符串,用于描述 Symbol 的用途,但这个描述字符串并不影响 Symbol 的唯一性。

1
2
const mySymbol = Symbol('mySymbol');
console.log(mySymbol); // Symbol(mySymbol)

2. Symbol 的唯一性

每个通过 Symbol() 创建的 Symbol 都是唯一的,即使它们的描述字符串相同。

1
2
3
const symbol1 = Symbol('mySymbol');
const symbol2 = Symbol('mySymbol');
console.log(symbol1 === symbol2); // false

3. 使用 Symbol 作为对象属性键

Symbol 常用于对象属性的键,以确保属性名的唯一性,避免命名冲突。

1
2
3
4
5
6
7
const mySymbol = Symbol('mySymbol');
const obj = {
[mySymbol]: 'Hello',
name: 'Alice'
};
console.log(obj[mySymbol]); // Hello
console.log(obj.name); // Alice

4. Symbol 的全局注册表

Symbol.for() 方法可以用于从全局 Symbol 注册表中检索一个 Symbol,如果该 Symbol 不存在,则创建一个新的 Symbol 并将其添加到注册表中。Symbol.keyFor() 方法可以用于从全局注册表中检索一个 Symbol 的键名。

1
2
3
4
const symbol1 = Symbol.for('mySymbol');
const symbol2 = Symbol.for('mySymbol');
console.log(symbol1 === symbol2); // true
console.log(Symbol.keyFor(symbol1)); // mySymbol

5. Symbol 的内置值

JavaScript 提供了一些内置的 Symbol 值,这些值有特定的用途,例如 Symbol.iterator 用于定义对象的默认迭代器。

1
2
3
4
5
6
7
const myArray = [1, 2, 3];
// 使用 Symbol.iterator 获取数组的默认迭代器
const iterator = myArray[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

6. Symbol 的属性描述符

Symbol 属性的描述符与普通属性的描述符类似,但 Symbol 属性默认是不可枚举的(enumerable: false)。

1
2
3
4
5
const mySymbol = Symbol('mySymbol');
const obj = {
[mySymbol]: 'Hello'
};
console.log(Object.getOwnPropertyDescriptors(obj)); // { [Symbol(mySymbol)]: { value: 'Hello', writable: true, enumerable: false, configurable: true } }

7. Symbol 的使用场景

  • 避免命名冲突:在对象上定义私有属性,避免与外部代码或库中的属性名冲突。
  • 定义迭代器:使用 Symbol.iterator 定义对象的默认迭代器。
  • 定义钩子方法:使用 Symbol.toPrimitiveSymbol.toStringTag 等内置 Symbol 定义对象的钩子方法。

示例

假设有一个对象,我们希望在对象上定义一些私有属性,避免与外部代码冲突:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const _id = Symbol('id');
const _name = Symbol('name');
class User {
constructor(id, name) {
this[_id] = id;
this[_name] = name;
}
getId() {
return this[_id];
}
getName() {
return this[_name];
}
}
const user = new User(1, 'Alice');
console.log(user.getId()); // 1
console.log(user.getName()); // Alice
// 外部代码无法直接访问 _id 和 _name 属性
console.log(user[_id]); // undefined
console.log(user[_name]); // undefined

在这个示例中,使用 Symbol 定义了 _id_name 两个私有属性,外部代码无法直接访问这些属性,只能通过 getIdgetName 方法获取。

总结

Symbol 对象在 JavaScript 中提供了一种创建唯一值的方法,特别适用于对象属性的键,以确保属性名的唯一性,避免命名冲突。通过 Symbol,可以定义私有属性、自定义迭代器和钩子方法,增强代码的封装性和安全性。

Symbol.iterator

在 JavaScript 中,Symbol.iterator 是一个内置的 Symbol 值,用于定义对象的默认迭代器。它是实现 可迭代协议(Iterable Protocol) 的关键,使得对象可以通过 for...of 循环、... 扩展运算符或 Array.from() 等方式被遍历。

1. 核心概念

  • 可迭代对象(Iterable):实现了 Symbol.iterator 方法的对象,该方法返回一个迭代器对象
  • 迭代器对象(Iterator):必须包含一个 next() 方法,每次调用返回一个包含 valuedone 的对象。

2. 基本语法

1
2
3
4
5
6
7
8
9
10
const iterable = {
[Symbol.iterator]() {
// 返回一个迭代器对象
return {
next() {
// 返回 { value: any, done: boolean }
}
};
}
};

3. 示例:自定义可迭代对象

(1)简单数字范围迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
return {
next: () => {
if (current <= this.end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
};
// 使用 for...of 遍历
for (const num of range) {
console.log(num); // 输出 1, 2, 3, 4, 5
}

(2)生成斐波那契数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fibonacci = {
[Symbol.iterator]() {
let a = 0, b = 1;
return {
next() {
const value = a;
[a, b] = [b, a + b];
return { value, done: value > 100 }; // 输出小于 100 的斐波那契数
}
};
}
};
// 转换为数组
const fibNumbers = [...fibonacci]; // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

4. 内置可迭代对象

JavaScript 中许多内置对象默认实现了 Symbol.iterator,例如:

  • 数组for (const item of [1, 2, 3])
  • 字符串for (const char of 'hello')
  • Map/Setfor (const [key, value] of new Map())
  • NodeListfor (const node of document.querySelectorAll('div'))

5. 手动调用迭代器

可以直接调用 Symbol.iterator 方法获取迭代器并逐步执行:

1
2
3
4
5
const arr = ['a', 'b'];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 'a', done: false }
console.log(iterator.next()); // { value: 'b', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

6. 让普通对象可迭代

默认情况下,普通对象不是可迭代的(无法直接用 for...of 遍历)。通过实现 Symbol.iterator 可使其支持迭代:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const person = {
name: "Alice",
age: 30,
hobbies: ["coding", "hiking"],
[Symbol.iterator]() {
const entries = Object.entries(this);
let index = 0;
return {
next() {
if (index < entries.length) {
return { value: entries[index++], done: false };
} else {
return { done: true };
}
}
};
}
};
// 遍历对象的键值对
for (const [key, value] of person) {
console.log(key, value);
// 输出:
// name Alice
// age 30
// hobbies ["coding", "hiking"]
}

7. 与生成器(Generator)结合

生成器函数(function*)会自动返回一个迭代器,简化 Symbol.iterator 的实现:

1
2
3
4
5
6
7
8
9
10
const range = {
start: 1,
end: 3,
*[Symbol.iterator]() { // 生成器语法
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
};
console.log([...range]); // [1, 2, 3]

8. 注意事项

  1. 不可重复迭代:一个迭代器通常只能遍历一次,遍历完成后需重新获取迭代器。
  2. 性能优化:避免在迭代器中修改原对象结构(如增删元素),可能导致意外行为。
  3. 兼容性Symbol.iterator 是 ES6 特性,旧版浏览器需通过 Babel 等工具转译。

9. 实际应用场景

  • 自定义数据结构:为链表、树等结构实现迭代逻辑。
  • 分页数据遍历:逐页获取远程数据直到结束。
  • 惰性计算:按需生成值(如无限序列)。

总结

Symbol.iterator 是 JavaScript 迭代机制的核心,通过实现它可以让任何对象支持 for...of 等现代遍历语法。理解这一机制有助于编写更灵活、可扩展的代码,尤其适用于处理自定义数据集合或异步流式数据。

toString

toString

在 JavaScript 中,toString() 是一个非常常用的方法,几乎所有的内置对象和数据类型都提供了 toString() 方法。它用于将对象或值转换为字符串形式。不同类型的对象和数据类型在调用 toString() 方法时的行为可能会有所不同。以下是关于 toString() 的详细介绍:

1. toString() 的基本用法

toString() 是一个方法,可以被对象或原始值调用,用于将值转换为字符串形式。其语法如下:

1
value.toString()

2. 不同类型调用 toString() 的行为

(1)数字(Number)

调用数字的 toString() 方法时,会将数字转换为字符串形式。

1
2
let num = 123;
console.log(num.toString()); // "123"

(2)字符串(String)

字符串本身已经是字符串类型,调用 toString() 方法时,会返回字符串本身。

1
2
let str = "Hello";
console.log(str.toString()); // "Hello"

(3)布尔值(Boolean)

布尔值调用 toString() 方法时,会将布尔值转换为字符串 "true""false"

1
2
3
4
let bool = true;
console.log(bool.toString()); // "true"
let bool2 = false;
console.log(bool2.toString()); // "false"

(4)对象(Object)

对于普通对象,调用 toString() 方法时,会返回一个表示对象类型的字符串,格式为 "[object 类型]"

1
2
let obj = {};
console.log(obj.toString()); // "[object Object]"

(5)数组(Array)

数组调用 toString() 方法时,会将数组的每个元素转换为字符串,并用逗号连接起来。

1
2
let arr = [1, 2, 3];
console.log(arr.toString()); // "1,2,3"

(6)函数(Function)

函数调用 toString() 方法时,会返回函数的源代码字符串。

1
2
3
4
5
6
7
8
function myFunction() {
console.log("Hello");
}
console.log(myFunction.toString());
// 输出:
// function myFunction() {
// console.log("Hello");
// }

(7)特殊值

  • nullnull 没有 toString() 方法,调用时会报错。
    1
    console.log(null.toString()); // TypeError: Cannot call method 'toString' of null
  • undefinedundefined 也没有 toString() 方法,调用时会报错。
    1
    console.log(undefined.toString()); // TypeError: Cannot call method 'toString' of undefined

3. 自定义对象的 toString() 方法

可以通过覆盖对象的 toString() 方法来自定义其字符串表示形式。例如:

1
2
3
4
5
6
7
8
9
10
11
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
toString() {
return `Person: ${this.name}, Age: ${this.age}`;
}
}
let person = new Person("Alice", 30);
console.log(person.toString()); // "Person: Alice, Age: 30"

4. Object.prototype.toString.call()

Object.prototype.toString.call() 是一种更通用的方式来检测对象的类型。通过调用 Object.prototype.toString.call(value),可以获取一个更精确的类型字符串,格式为 "[object 类型]"。这种方法可以用于检测数组、函数、日期等类型。

1
2
3
4
5
6
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call(function () {})); // "[object Function]"
console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"

5. 总结

  • toString() 是一个非常通用的方法,用于将值或对象转换为字符串形式。
  • 不同类型的值调用 toString() 时的行为可能不同:
    • 数字、字符串、布尔值等会直接转换为字符串。
    • 对象会返回 "[object 类型]"
    • 数组会将元素连接为字符串。
    • 函数会返回函数的源代码。
  • 可以通过覆盖对象的 toString() 方法来自定义其字符串表示形式。
  • 使用 Object.prototype.toString.call() 可以更精确地检测对象的类型。
    希望这些内容能帮助你更好地理解和使用 toString() 方法!

全局函数

全局函数

全局函数

以下是关于 JavaScript 全局函数(Global Functions) 的详细介绍及整理表格。这些函数是 JavaScript 语言标准(ECMAScript)定义的内置函数,可以直接在全局作用域中调用,无需依赖宿主环境(如浏览器或 Node. js)。

什么是全局函数?

  • 全局作用域:这些函数属于全局对象(在浏览器中是 window,在 Node. js 中是 global),可直接调用。
  • 语言核心功能:用于数据转换、编码、数学运算等基础操作。
  • 与宿主函数的区别:全局函数是 JavaScript 语言标准的一部分,而宿主函数(如 document.getElementById)由宿主环境提供。

JavaScript 全局函数列表

函数 描述 参数 返回值 注意事项
parseInt() 将字符串转换为整数。 string(要转换的字符串)、radix(进制基数,可选,默认 10)。 解析后的整数或 NaN 忽略字符串开头的空格,遇到非数字字符停止解析。
parseFloat() 将字符串转换为浮点数。 string(要转换的字符串)。 解析后的浮点数或 NaN 类似 parseInt,但支持小数点和指数形式(如 2.5e3)。
isNaN() 检查值是否为 NaN(非数字)。 value(要检查的值)。 true(是 NaN)或 false 先尝试将参数转换为数字,再判断。建议使用 Number.isNaN() 更严格。
isFinite() 检查值是否为有限数字(非 InfinityNaN)。 value(要检查的值)。 true(有限)或 false 会先将参数转换为数字(如 isFinite("123") 返回 true)。
encodeURI() 对 URI 进行编码,保留合法字符(如 :/?#[]@)。 uri(要编码的完整 URI)。 编码后的字符串。 不编码 URI 保留字符,适合编码完整 URL。
encodeURIComponent() 对 URI 组件(如查询参数)进行编码,转义更多字符。 uriComponent(要编码的 URI 部分)。 编码后的字符串。 编码所有非标准字符(如 :/),适合编码查询参数。
decodeURI() 解码由 encodeURI 编码的 URI。 encodedURI(已编码的 URI)。 解码后的原始 URI。 无法解码 encodeURIComponent 编码的内容(可能报错)。
decodeURIComponent() 解码由 encodeURIComponent 编码的 URI 组件。 encodedURIComponent(已编码的 URI 部分)。 解码后的原始字符串。 可以解码 encodeURIencodeURIComponent 编码的内容。
eval() 执行字符串中的 JavaScript 代码。 string(包含代码的字符串)。 代码执行的返回值(若无返回则为 undefined)。 慎用:存在安全风险,性能差,且难以调试。

示例代码

1. 数值转换

1
2
console.log(parseInt("42px"));      // 42(忽略非数字部分)
console.log(parseFloat("3.14.5")); // 3.14(遇到第二个小数点停止)

2. URI 编码与解码

1
2
3
4
5
const url = "https://example.com/搜索?q=JavaScript";
const encoded = encodeURI(url); // "https://example.com/%E6%90%9C%E7%B4%A2?q=JavaScript"
const decoded = decodeURI(encoded); // 还原原始 URL
const param = "name=张三&age=20";
const encodedParam = encodeURIComponent(param); // "name%3D%E5%BC%A0%E4%B8%89%26age%3D20"

3. 检查特殊值

1
2
console.log(isNaN("Hello"));   // true(字符串转换为数字是 NaN)
console.log(isFinite(Infinity)); // false

4. 危险的 eval()

1
2
3
const x = 10;
eval("console.log(x + 5);"); // 15(可以访问当前作用域)
eval("let y = 20; console.log(y);"); // 20(但会污染作用域)

注意事项

  1. 避免使用 eval()
    • 容易导致代码注入攻击(如动态执行用户输入)。
    • 影响性能(无法被 JavaScript 引擎优化)。
    • 改用 Function 构造函数或模块化方案替代。
  2. isNaN vs Number.isNaN
    • isNaN("abc")true(因为 Number("abc")NaN)。
    • Number.isNaN("abc")false(严格判断是否为 NaN 类型)。
  3. URI 编码选择
    • 编码整个 URL → encodeURI()
    • 编码查询参数 → encodeURIComponent()

总结

JavaScript 全局函数为开发者提供了基础的数据处理能力,但在使用时需注意兼容性和安全性(尤其是 eval)。结合现代 API(如 URLSearchParams 替代 URI 编码)和严格模式("use strict"),可以编写更安全、高效的代码。

容器对象

Map和Set

map

Map 对象是 JavaScript 中的一种集合数据结构,用于存储键值对。与对象(Object)不同,Map 允许任何类型的值作为键,而不仅仅是字符串或符号。Map 也提供了更多的内置方法来操作键值对,使得它在处理键值对集合时更加灵活和高效。

创建 Map 对象

1
let myMap = new Map();

常用方法

1. set(key, value)

  • 用途:向 Map 中添加一个新的键值对。
  • 示例
    1
    2
    myMap.set("key1", "value1");
    myMap.set("key2", "value2");

2. get(key)

  • 用途:根据键获取对应的值。如果键不存在,返回 undefined
  • 示例
    1
    2
    console.log(myMap.get("key1")); // "value1"
    console.log(myMap.get("key3")); // undefined

3. has(key)

  • 用途:检查 Map 中是否存在指定的键。返回 truefalse
  • 示例
    1
    2
    console.log(myMap.has("key1")); // true
    console.log(myMap.has("key3")); // false

4. delete(key)

  • 用途:从 Map 中删除指定的键值对。返回 true 表示删除成功,false 表示键不存在。
  • 示例
    1
    2
    console.log(myMap.delete("key1")); // true
    console.log(myMap.has("key1")); // false

5. clear()

  • 用途:清空 Map 中的所有键值对。
  • 示例
    1
    2
    myMap.clear();
    console.log(myMap.size); // 0

6. size

  • 用途:返回 Map 中键值对的数量。
  • 示例
    1
    2
    3
    myMap.set("key1", "value1");
    myMap.set("key2", "value2");
    console.log(myMap.size); // 2

遍历 Map 对象

Map 对象提供了多种遍历方法,包括 for...of 循环、forEach 方法等。

1. for...of 循环

  • 用途:遍历 Map 中的每个键值对。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let [key, value] of myMap) {
    console.log(key, value);
    }
    // 输出:
    // key1 value1
    // key2 value2

2. forEach 方法

  • 用途:遍历 Map 中的每个键值对,并对每个键值对执行指定的函数。
  • 示例
    1
    2
    3
    4
    5
    6
    myMap.forEach((value, key) => {
    console.log(key, value);
    });
    // 输出:
    // key1 value1
    // key2 value2

键和值的迭代器

Map 对象还提供了 keys()values()entries() 方法,分别用于获取键、值和键值对的迭代器。

1. keys()

  • 用途:返回一个包含所有键的迭代器。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let key of myMap.keys()) {
    console.log(key);
    }
    // 输出:
    // key1
    // key2

2. values()

  • 用途:返回一个包含所有值的迭代器。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let value of myMap.values()) {
    console.log(value);
    }
    // 输出:
    // value1
    // value2

3. entries()

  • 用途:返回一个包含所有键值对的迭代器。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let [key, value] of myMap.entries()) {
    console.log(key, value);
    }
    // 输出:
    // key1 value1
    // key2 value2

示例:使用 Map 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let myMap = new Map();
// 添加键值对
myMap.set("name", "John");
myMap.set("age", 30);
myMap.set("isStudent", false);
// 获取值
console.log(myMap.get("name")); // "John"
console.log(myMap.get("age")); // 30
// 检查键是否存在
console.log(myMap.has("isStudent")); // true
// 删除键值对
myMap.delete("isStudent");
console.log(myMap.has("isStudent")); // false
// 遍历 Map
for (let [key, value] of myMap) {
console.log(key, value);
}
// 输出:
// name John
// age 30
// 清空 Map
myMap.clear();
console.log(myMap.size); // 0

总结

Map 对象是 JavaScript 中处理键值对集合的强大工具,提供了丰富的方法来操作和遍历键值对。与普通对象相比,Map 允许任何类型的值作为键,并且提供了更多的内置方法,使得它在处理复杂数据结构时更加灵活和高效。

map和object的区别

MapObject 都是 JavaScript 中用于存储键值对的数据结构,但它们在功能、性能和使用场景上有一些显著的区别。以下是对 MapObject 的详细对比:

1. 键的类型

  • Object
    • 键的类型:键必须是字符串或符号(Symbol)。其他类型的键会被转换为字符串。
    • 示例
      1
      2
      3
      4
      let obj = {};
      obj[1] = "one"; // 键 1 被转换为字符串 "1"
      obj[true] = "true"; // 键 true 被转换为字符串 "true"
      console.log(obj); // { '1': 'one', 'true': 'true' }
  • Map
    • 键的类型:键可以是任何类型的值,包括对象、函数、数组等。
    • 示例
      1
      2
      3
      4
      5
      6
      let map = new Map();
      map.set(1, "one");
      map.set(true, "true");
      map.set("key", "value");
      map.set({}, "object");
      console.log(map); // Map(4) { 1 => 'one', true => 'true', 'key' => 'value', {} => 'object' }

2. 键的顺序

  • Object
    • 键的顺序:键的顺序是根据键的类型和插入顺序来决定的。字符串键按插入顺序排序,而数字键按升序排序。
    • 示例
      1
      2
      3
      4
      5
      6
      let obj = {};
      obj[10] = "ten";
      obj[2] = "two";
      obj[1] = "one";
      obj["key"] = "value";
      console.log(Object.keys(obj)); // ['1', '2', '10', 'key']
  • Map
    • 键的顺序:键的顺序严格按照插入顺序排列。
    • 示例
      1
      2
      3
      4
      5
      6
      let map = new Map();
      map.set(10, "ten");
      map.set(2, "two");
      map.set(1, "one");
      map.set("key", "value");
      console.log([...map.keys()]); // [10, 2, 1, 'key']

3. 性能

  • Object
    • 性能:在频繁添加和删除键值对时,性能可能会下降,因为 Object 的键是字符串,需要进行哈希映射。
    • 示例
      1
      2
      3
      4
      5
      let obj = {};
      for (let i = 0; i < 1000000; i++) {
      obj[i] = i;
      }
      delete obj[500000];
  • Map
    • 性能:在频繁添加和删除键值对时,性能更稳定。Map 专门设计用于高效地处理键值对,特别是在键的类型多样时。
    • 示例
      1
      2
      3
      4
      5
      let map = new Map();
      for (let i = 0; i < 1000000; i++) {
      map.set(i, i);
      }
      map.delete(500000);

4. 方法和属性

  • Object
    • 方法和属性Object 提供了一些方法和属性,如 Object.keys()Object.values()Object.entries() 等,但这些方法返回的是数组,需要额外的处理。
    • 示例
      1
      2
      3
      4
      let obj = { a: 1, b: 2, c: 3 };
      console.log(Object.keys(obj)); // ['a', 'b', 'c']
      console.log(Object.values(obj)); // [1, 2, 3]
      console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]
  • Map
    • 方法和属性Map 提供了丰富的内置方法,如 set()get()has()delete()clear()size 等,这些方法和属性直接操作键值对,使用起来更方便。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      let map = new Map();
      map.set("a", 1);
      map.set("b", 2);
      map.set("c", 3);
      console.log(map.size); // 3
      console.log(map.has("a")); // true
      console.log(map.get("a")); // 1
      map.delete("a");
      console.log(map.has("a")); // false
      map.clear();
      console.log(map.size); // 0

5. 遍历

  • Object
    • 遍历:使用 for...in 循环遍历对象的键。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      let obj = { a: 1, b: 2, c: 3 };
      for (let key in obj) {
      console.log(key, obj[key]);
      }
      // 输出:
      // a 1
      // b 2
      // c 3
  • Map
    • 遍历:使用 for...of 循环遍历 Map 的键值对,也可以使用 forEach 方法。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      let map = new Map();
      map.set("a", 1);
      map.set("b", 2);
      map.set("c", 3);
      for (let [key, value] of map) {
      console.log(key, value);
      }
      // 输出:
      // a 1
      // b 2
      // c 3
      map.forEach((value, key) => {
      console.log(key, value);
      });
      // 输出:
      // a 1
      // b 2
      // c 3

6. 总结

  • Object
    • 用途:适合存储键为字符串或符号的键值对,适用于简单的数据结构。
    • 优点:语法简洁,广泛使用。
    • 缺点:键的类型有限,性能在频繁操作时可能下降,方法返回的是数组,需要额外处理。
  • Map
    • 用途:适合存储键为任意类型的键值对,特别是在键的类型多样且需要高效操作时。
    • 优点:键的类型多样,性能稳定,提供丰富的内置方法,遍历顺序严格按插入顺序。
    • 缺点:语法稍复杂,不如 Object 使用广泛。

7. 选择建议

  • 使用 Object
    • 当键为字符串或符号,且不需要频繁添加和删除键值对时。
    • 当需要利用对象的原型链或方法时。
  • 使用 Map
    • 当键的类型多样,特别是包含对象、数组等时。
    • 当需要高效地添加和删除键值对时。
    • 当需要严格按插入顺序遍历时。
      理解 MapObject 的这些区别可以帮助你根据具体需求选择合适的数据结构。

Map 和 Object 在实际应用中的区别

1. 存储用户信息

假设我们需要存储用户信息,每个用户有一个唯一的 ID 和一些其他信息,如姓名和年龄。

使用 Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let users = {};
// 添加用户
users[1] = { name: "Alice", age: 30 };
users[2] = { name: "Bob", age: 25 };
users[3] = { name: "Charlie", age: 35 };
// 获取用户信息
console.log(users[1]); // { name: "Alice", age: 30 }
// 删除用户
delete users[2];
// 遍历用户
for (let id in users) {
console.log(id, users[id]);
}
// 输出:
// 1 { name: "Alice", age: 30 }
// 3 { name: "Charlie", age: 35 }

使用 Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let users = new Map();
// 添加用户
users.set(1, { name: "Alice", age: 30 });
users.set(2, { name: "Bob", age: 25 });
users.set(3, { name: "Charlie", age: 35 });
// 获取用户信息
console.log(users.get(1)); // { name: "Alice", age: 30 }
// 删除用户
users.delete(2);
// 遍历用户
for (let [id, user] of users) {
console.log(id, user);
}
// 输出:
// 1 { name: "Alice", age: 30 }
// 3 { name: "Charlie", age: 35 }

2. 处理唯一值集合

假设我们需要处理一个数组,去除其中的重复值。

使用 Object

1
2
3
4
5
6
7
let array = [1, 2, 2, 3, 4, 4, 5];
let unique = {};
for (let item of array) {
unique[item] = true;
}
let uniqueArray = Object.keys(unique).map(key => parseInt(key));
console.log(uniqueArray); // [1, 2, 3, 4, 5]

使用 Set

1
2
3
4
let array = [1, 2, 2, 3, 4, 4, 5];
let uniqueSet = new Set(array);
let uniqueArray = Array.from(uniqueSet);
console.log(uniqueArray); // [1, 2, 3, 4, 5]

3. 存储复杂键值对

假设我们需要存储一些复杂对象作为键,例如存储用户对象和他们的权限。

使用 Object

1
2
3
4
5
6
7
let userPermissions = {};
let user1 = { id: 1, name: "Alice" };
let user2 = { id: 2, name: "Bob" };
// 由于对象键会被转换为字符串,这里会出问题
userPermissions[user1] = ["read", "write"];
userPermissions[user2] = ["read"];
console.log(userPermissions); // { '[object Object]': ['read'] }

使用 Map

1
2
3
4
5
6
7
let userPermissions = new Map();
let user1 = { id: 1, name: "Alice" };
let user2 = { id: 2, name: "Bob" };
userPermissions.set(user1, ["read", "write"]);
userPermissions.set(user2, ["read"]);
console.log(userPermissions.get(user1)); // ['read', 'write']
console.log(userPermissions.get(user2)); // ['read']

4. 频繁添加和删除键值对

假设我们需要频繁地添加和删除键值对,例如在一个缓存系统中。

使用 Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let cache = {};
// 添加缓存
cache["key1"] = "value1";
cache["key2"] = "value2";
// 删除缓存
delete cache["key1"];
// 检查缓存是否存在
console.log(cache.hasOwnProperty("key1")); // false
// 遍历缓存
for (let key in cache) {
console.log(key, cache[key]);
}
// 输出:
// key2 value2

使用 Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let cache = new Map();
// 添加缓存
cache.set("key1", "value1");
cache.set("key2", "value2");
// 删除缓存
cache.delete("key1");
// 检查缓存是否存在
console.log(cache.has("key1")); // false
// 遍历缓存
for (let [key, value] of cache) {
console.log(key, value);
}
// 输出:
// key2 value2

总结

  • Object
    • 优点:语法简洁,广泛使用,适合存储键为字符串或符号的简单数据结构。
    • 缺点:键的类型有限,性能在频繁操作时可能下降,方法返回的是数组,需要额外处理。
  • Map
    • 优点:键的类型多样,性能稳定,提供丰富的内置方法,遍历顺序严格按插入顺序,适合存储复杂键值对和频繁操作的场景。
    • 缺点:语法稍复杂,不如 Object 使用广泛。
      根据具体需求选择合适的数据结构可以提高代码的可读性和性能。

Set

Set 对象是 JavaScript 中的一种集合数据结构,用于存储唯一的值。Set 中的每个值都只能出现一次,这使得它在处理唯一性数据时非常有用。以下是对 Set 对象的详细解释:

创建 Set 对象

1
let mySet = new Set();

常用方法

1. add(value)

  • 用途:向 Set 中添加一个新的值。如果值已经存在,则不会重复添加。
  • 示例
    1
    2
    3
    mySet.add(1);
    mySet.add(2);
    mySet.add(1); // 1 已经存在,不会重复添加

2. has(value)

  • 用途:检查 Set 中是否存在指定的值。返回 truefalse
  • 示例
    1
    2
    console.log(mySet.has(1)); // true
    console.log(mySet.has(3)); // false

3. delete(value)

  • 用途:从 Set 中删除指定的值。返回 true 表示删除成功,false 表示值不存在。
  • 示例
    1
    2
    console.log(mySet.delete(1)); // true
    console.log(mySet.has(1)); // false

4. clear()

  • 用途:清空 Set 中的所有值。
  • 示例
    1
    2
    mySet.clear();
    console.log(mySet.size); // 0

5. size

  • 用途:返回 Set 中值的数量。
  • 示例
    1
    2
    3
    mySet.add(1);
    mySet.add(2);
    console.log(mySet.size); // 2

遍历 Set 对象

Set 对象提供了多种遍历方法,包括 for...of 循环、forEach 方法等。

1. for...of 循环

  • 用途:遍历 Set 中的每个值。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let value of mySet) {
    console.log(value);
    }
    // 输出:
    // 1
    // 2

2. forEach 方法

  • 用途:遍历 Set 中的每个值,并对每个值执行指定的函数。
  • 示例
    1
    2
    3
    4
    5
    6
    mySet.forEach((value) => {
    console.log(value);
    });
    // 输出:
    // 1
    // 2

示例:使用 Set 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let mySet = new Set();
// 添加值
mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(1); // 1 已经存在,不会重复添加
// 获取值的数量
console.log(mySet.size); // 3
// 检查值是否存在
console.log(mySet.has(2)); // true
console.log(mySet.has(4)); // false
// 删除值
mySet.delete(2);
console.log(mySet.has(2)); // false
// 遍历 Set
for (let value of mySet) {
console.log(value);
}
// 输出:
// 1
// 3
// 清空 Set
mySet.clear();
console.log(mySet.size); // 0

总结

Set 对象是 JavaScript 中处理唯一值集合的强大工具,提供了丰富的方法来操作和遍历值。与数组相比,Set 自动处理值的唯一性,使得它在处理去重数据时更加高效和方便。以下是一些常见的使用场景:

  • 去重:从数组中去除重复值。
    1
    2
    3
    let array = [1, 2, 2, 3, 4, 4, 5];
    let uniqueArray = Array.from(new Set(array));
    console.log(uniqueArray); // [1, 2, 3, 4, 5]
  • 集合操作:进行集合的并集、交集、差集等操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let set1 = new Set([1, 2, 3]);
    let set2 = new Set([2, 3, 4]);
    // 并集
    let union = new Set([...set1, ...set2]);
    console.log(union); // Set(4) {1, 2, 3, 4}
    // 交集
    let intersection = new Set([...set1].filter(x => set2.has(x)));
    console.log(intersection); // Set(2) {2, 3}
    // 差集
    let difference = new Set([...set1].filter(x => !set2.has(x)));
    console.log(difference); // Set(1) {1}
    理解 Set 对象的这些特性和方法可以帮助你更高效地处理唯一值集合。

set和map区别

JavaScript中的SetMap都是ES6新增的数据结构,它们在功能和用途上有一些区别,以下是详细的对比:
一、存储结构方面

  1. Set
    • Set是一个简单的键的集合,其中的元素是唯一的,没有键值对的概念。它的存储结构类似于一个数组,但不允许重复的元素存在。例如:
      1
      2
      3
      4
      5
      let mySet = new Set();
      mySet.add(1);
      mySet.add(2);
      mySet.add(2); // 重复的元素不会被添加
      console.log(mySet); // Set(2) {1, 2}
      Set中,每个元素都是唯一的,它通过内部机制来保证元素的唯一性。
  2. Map
    • Map是一个键值对的集合,类似于对象(Object),但是它的键可以是任何类型,包括对象、函数等。例如:
      1
      2
      3
      4
      5
      let myMap = new Map();
      myMap.set('key1', 'value1');
      myMap.set(2, 'value2');
      myMap.set({objKey: 'objValue'}, 'value3');
      console.log(myMap); // Map(3) {'key1' => 'value1', 2 => 'value2', {objKey: 'objValue'} => 'value3'}
      Map中,每个键都有一个对应的值,而且键可以是任意类型,这使得Map在处理复杂数据结构时更加灵活。

二、操作方法方面

  1. Set
    • 常用方法有add()(添加元素)、delete()(删除元素)、has()(判断元素是否存在)、clear()(清空所有元素)等。例如:
      1
      2
      3
      4
      5
      let mySet = new Set();
      mySet.add('apple');
      console.log(mySet.has('apple')); // true
      mySet.delete('apple');
      console.log(mySet.has('apple')); // false
      这些方法主要围绕元素的增删查改进行操作,比较简单直接。
  2. Map
    • 常用方法有set()(设置键值对)、get()(获取键对应的值)、has()(判断键是否存在)、delete()(删除键值对)、clear()(清空所有键值对)等。例如:
      1
      2
      3
      4
      5
      6
      let myMap = new Map();
      myMap.set('fruit', 'apple');
      console.log(myMap.get('fruit')); // 'apple'
      console.log(myMap.has('fruit')); // true
      myMap.delete('fruit');
      console.log(myMap.has('fruit')); // false
      Map的方法主要围绕键值对进行操作,功能更加丰富,可以方便地处理键和值之间的关系。

三、遍历方面

  1. Set
    • 可以使用for...of循环进行遍历,遍历的顺序是元素插入的顺序。例如:
      1
      2
      3
      4
      5
      let mySet = new Set(['apple', 'banana', 'orange']);
      for (let item of mySet) {
      console.log(item);
      }
      // 输出:apple banana orange
      也可以使用forEach()方法遍历:
      1
      2
      3
      mySet.forEach((item) => {
      console.log(item);
      });
  2. Map
    • 同样可以使用for...of循环遍历,遍历的顺序也是键值对插入的顺序,每次遍历得到的是一个数组,数组的第一个元素是键,第二个元素是值。例如:
      1
      2
      3
      4
      5
      let myMap = new Map([['fruit', 'apple'], ['color', 'red']]);
      for (let [key, value] of myMap) {
      console.log(key + ': ' + value);
      }
      // 输出:fruit: apple color: red
      也可以使用forEach()方法遍历:
      1
      2
      3
      myMap.forEach((value, key) => {
      console.log(key + ': ' + value);
      });
      此外,Map还可以通过keys()(获取所有键的迭代器)、values()(获取所有值的迭代器)、entries()(获取所有键值对的迭代器)等方法进行更灵活的遍历。

四、用途方面

  1. Set
    • 适用于需要存储唯一元素的场景。例如,去除数组中的重复项:
      1
      2
      3
      let array = [1, 2, 2, 3, 4, 4, 5];
      let uniqueArray = [...new Set(array)];
      console.log(uniqueArray); // [1, 2, 3, 4, 5]
    • 也可以用于判断元素是否存在于集合中,进行一些集合运算(如并集、交集等)。
  2. Map
    • 适用于需要存储键值对,并且键可以是任意类型的情况。例如,存储用户信息,其中键是用户对象,值是用户的一些额外信息:
      1
      2
      3
      4
      5
      6
      let user1 = {name: 'Alice'};
      let user2 = {name: 'Bob'};
      let userInfoMap = new Map();
      userInfoMap.set(user1, {age: 25, gender: 'female'});
      userInfoMap.set(user2, {age: 30, gender: 'male'});
      console.log(userInfoMap.get(user1)); // {age: 25, gender: 'female'}
    • 在处理复杂的数据结构和需要频繁访问键值对应关系的场景中非常有用。

WeakSet 和 WeakMap

1. WeakSet

定义: WeakSet 是一个集合,其中的元素都是对象,并且这些对象是弱引用的。这意味着如果这些对象没有其他强引用,垃圾回收机制可以自动回收这些对象,WeakSet 中的对应项也会自动被移除。
主要方法:

  • add(value):向 WeakSet 中添加一个对象。
  • delete(value):从 WeakSet 中删除一个对象。
  • has(value):检查 WeakSet 中是否存在某个对象。
    应用场景:
  1. 避免内存泄漏:存储一组 DOM 节点,当这些节点被移除后,可以自动释放内存。
    1
    2
    3
    4
    5
    6
    const domNodes = new WeakSet();
    const button = document.createElement('button');
    button.textContent = 'Click me';
    document.body.appendChild(button);
    domNodes.add(button);
    document.body.removeChild(button);
  2. 跟踪动态数据:跟踪频繁修改的对象,而不会因为长时间保持引用而导致内存泄漏。
    1
    2
    3
    4
    5
    6
    7
    8
    const dataPoints = new WeakSet();
    function addDataPoint(point) {
    dataPoints.add(point);
    }
    const point1 = { x: 10, y: 20 };
    const point2 = { x: 30, y: 40 };
    addDataPoint(point1);
    addDataPoint(point2);
  3. 存储临时对象:存储临时对象,当这些对象不再需要时,可以自动释放内存。
    1
    2
    3
    4
    5
    6
    7
    const myWeakSet = new WeakSet();
    function createTemporaryObject(id) {
    const obj = { id: id, data: '/* ... 一些临时数据 ... */' };
    myWeakSet.add(obj);
    }
    createTemporaryObject(1);
    createTemporaryObject(2);

2. WeakMap

定义: WeakMap 是一个键值对的集合,其中的键必须是对象,值可以是任意类型。键是弱引用的,这意味着如果这些键对象没有其他强引用,垃圾回收机制可以自动回收这些对象,WeakMap 中的对应键值对也会自动被移除。
主要方法:

  • set(key, value):向 WeakMap 中添加或更新一个键值对。
  • get(key):获取对应键的值。
  • delete(key):从 WeakMap 中删除一个键值对。
  • has(key):检查 WeakMap 中是否存在某个键。
    应用场景:
  1. 缓存:缓存计算结果或频繁访问的数据,以提高性能。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const cache = new WeakMap();
    function calculation(obj) {
    if (cache.has(obj)) {
    return cache.get(obj);
    }
    const result = Math.random();
    cache.set(obj, result);
    return result;
    }
    const keyObj = {};
    console.log(calculation(keyObj));
    console.log(calculation(keyObj));
    keyObj = null;
  2. 避免内存泄漏:存储监听事件的引用,当监听事件被移除后,可以自动释放内存。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const btn1 = document.querySelector("#btn1");
    const btn2 = document.querySelector("#btn2");
    const btnMap = new WeakMap();
    function handleBtn1Click() {}
    function handleBtn2Click() {}
    btnMap.set(btn1, handleBtn1Click);
    btnMap.set(btn2, handleBtn2Click);
    btn1.addEventListener("click", btnMap.get(btn1));
    btn2.addEventListener("click", btnMap.get(btn2));
    btn1.remove();
    btn2.remove();
  3. 存储私有属性:保护私有属性不被外部访问。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    const privateProps = new WeakMap();
    class Person {
    constructor(name) {
    this.name = name;
    privateProps.set(this, { age: 0 });
    this.incrementAge = () => {
    const props = privateProps.get(this);
    props.age++;
    };
    this.getAge = () => {
    const props = privateProps.get(this);
    return props.age;
    };
    }
    }
    const alice = new Person('Alice');
    console.log(alice.name); // Alice
    console.log(alice.getAge()); // 0
    alice.incrementAge();
    console.log(alice.getAge()); // 1
    console.log(alice.age); // undefined
  4. 优化深拷贝:解决循环引用问题。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function deepCopy(obj, visited = new WeakMap()) {
    if (obj === null || typeof obj !== 'object') {
    return obj;
    }
    if (visited.has(obj)) {
    return visited.get(obj);
    }
    if (obj instanceof Array) {
    let copy = [];
    visited.set(obj, copy);
    for (let i = 0; i < obj.length; i++) {
    copy[i] = deepCopy(obj[i], visited);
    }
    return copy;
    }
    if (obj instanceof Object) {
    let copy = {};
    visited.set(obj, copy);
    for (let key in obj) {
    copy[key] = deepCopy(obj[key], visited);
    }
    return copy;
    }
    return obj;
    }

总结

  • WeakSet:适用于需要存储一组对象引用的场景,这些对象在没有其他强引用时可以被自动回收。常用于避免内存泄漏、跟踪动态数据和存储临时对象。
  • WeakMap:适用于需要存储键值对的场景,键是对象,值可以是任意类型。键是弱引用的,当键对象没有其他强引用时,可以被自动回收。常用于缓存、避免内存泄漏、存储私有属性和优化深拷贝。

特点对比

特点 WeakSet WeakMap
键类型 只能是对象 键必须是对象,值可以是任意类型
引用类型 弱引用 弱引用
可迭代 不可迭代 不可迭代
常见方法 add, delete, has set, get, delete, has
应用场景 避免内存泄漏、跟踪动态数据、存储临时对象 缓存、避免内存泄漏、存储私有属性、优化深拷贝
希望这些信息对你有所帮助!

宿主函数

宿主函数

宿主函数

以下是关于 JavaScript 宿主函数(Host Functions) 的详细介绍及整理表格。宿主函数是由 JavaScript 的宿主环境(如浏览器或 Node. js)提供的 API,而非 ECMAScript 标准的一部分。下面以浏览器环境为例进行说明:

什么是宿主函数?

  • 宿主环境:JavaScript 的运行环境(如浏览器、Node. js)。
  • 宿主对象/函数:由宿主环境提供的对象或函数,用于与外部系统交互(如操作 DOM、处理网络请求、访问硬件等)。
  • 与内置对象的区别:内置对象(如 ArrayMath)是 ECMAScript 标准定义的,而宿主对象(如 documentXMLHttpRequest)由宿主环境实现。

浏览器中的常见宿主函数及方法

以下是浏览器环境中常见的宿主函数及其核心方法,按功能分类整理:

1. DOM 操作

宿主对象/函数 常用方法/属性 描述
document getElementById(), querySelector(), createElement() 访问或创建 DOM 元素。
Element appendChild(), removeChild(), setAttribute(), classList 操作元素属性、子元素或类名。
EventTarget addEventListener(), removeEventListener() 绑定或移除事件监听器(如点击、键盘事件)。

2. BOM(浏览器对象模型)

宿主对象/函数 常用方法/属性 描述
window alert(), setTimeout(), setInterval(), fetch() 控制浏览器窗口、定时任务、网络请求。
navigator userAgent, geolocation, clipboard 获取浏览器信息、地理位置或剪贴板权限。
location href, reload(), search 操作或获取当前页面的 URL 信息。
history pushState(), replaceState(), back() 管理浏览历史(用于单页应用路由)。

3. 存储与缓存

宿主对象/函数 常用方法/属性 描述
localStorage setItem(), getItem(), removeItem() 持久化存储键值对数据(关闭浏览器后保留)。
sessionStorage setItem(), getItem(), removeItem() 会话级存储键值对数据(关闭标签页后清除)。
IndexedDB open(), createObjectStore(), transaction() 浏览器端数据库,支持复杂数据存储。

4. 网络请求

宿主对象/函数 常用方法/属性 描述
XMLHttpRequest open(), send(), onreadystatechange 传统 AJAX 请求(逐渐被 fetch 取代)。
fetch() fetch(url).then().catch() 基于 Promise 的现代网络请求方法。
WebSocket new WebSocket(), send(), onmessage 实现客户端与服务器的双向实时通信。

5. 多媒体

宿主对象/函数 常用方法/属性 描述
<audio>/<video> play(), pause(), currentTime, volume 控制音视频播放。
CanvasRenderingContext2D fillRect(), drawImage(), beginPath() 通过 <canvas> 绘制 2D 图形或动画。

6. 其他 Web API

宿主对象/函数 常用方法/属性 描述
console log(), error(), warn(), table() 控制台输出调试信息。
Geolocation getCurrentPosition(), watchPosition() 获取用户地理位置(需授权)。
MutationObserver observe(), disconnect() 监听 DOM 树变化。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用宿主函数 setTimeout(属于 window 对象)
setTimeout(() => {
console.log("延迟执行");
}, 1000);
// 使用宿主函数 fetch
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data));
// 使用 DOM 操作宿主函数
const button = document.getElementById("myButton");
button.addEventListener("click", () => {
alert("按钮被点击!");
});

总结

  • 宿主函数依赖环境:不同宿主环境(浏览器、Node. js)提供的 API 不同。
  • 功能丰富性:通过宿主函数,JavaScript 可以操作页面、发送请求、访问硬件等。
  • 兼容性注意:部分宿主函数可能不被旧浏览器支持(如 fetch),需使用 Polyfill 或降级方案。

图片懒加载

图片懒加载

Vue中实现图片懒加载

在Vue中实现图片懒加载,主要有以下几种方法:

1. 使用Vue-Lazyload插件

安装插件
对于Vue 2,可以使用vue-lazyload插件:

1
npm i vue-lazyload@1.2.3 -S

对于Vue 3,应使用vue-lazyload-next

1
npm i vue-lazyload-next -S

注册插件
main.js中注册插件,并配置相关参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Vue 2
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
loading: 'xxx.png', // 懒加载默认加载图片
error: 'xxx.png', // 加载失败后加载的图片
preLoad: 1.3, // 预加载高度的比例
attempt: 3 // 尝试加载次数
})

// Vue 3
import VueLazyloadNext from 'vue-lazyload-next';
app.use(VueLazyloadNext, {
loading: 'xxx.png',
error: 'xxx.png',
preLoad: 1.3,
attempt: 3
});

使用插件
在组件中,使用v-lazy指令来实现图片懒加载:

1
2
3
4
5
<!-- 懒加载img标签 -->
<img v-lazy="imgUrl" />

<!-- 懒加载背景图片 -->
<div v-lazy:background-image="bgUrl"></div>

v-lazy指令接收一个字符串类型的值,表示图片的地址。如果是背景图片,需要在指令后加上:background-image修饰符。

2. 使用Intersection Observer API

实现懒加载
可以手动使用IntersectionObserver API来实现图片懒加载:

1
2
3
4
5
6
7
8
9
10
11
12
13
export const lazyLoad = {
mounted(el, binding) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
})
observer.observe(el)
}
}

img标签上使用v-lazy指令:

1
<img v-lazy="imageUrl" />

这种方式不依赖于任何外部库,完全使用原生JavaScript API实现懒加载。

3. VueUse库

使用useIntersectionObserver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div ref="target">
<img v-if="isVisible" :src="props.src" :alt="props.alt">
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
const target = ref(null)
const isVisible = ref(false)
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }]) => {
if (isIntersecting) {
isVisible.value = true
stop()
}
}
)
</script>

这种方式利用了@vueuse/core库中的useIntersectionObserver函数来实现懒加载。

以上是Vue中实现图片懒加载的几种常见方法,可以根据项目需求和个人喜好选择合适的实现方式。

自定义监听器

有一定的防抖措施

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function lazyLoad() {
const images = document.querySelectorAll(".lazy");
const scrollTop = window.pageYOffset;
images.forEach((img) => {
if (img.offsetTop < window.innerHeight + scrollTop) {
img.src = img.dataset.src;
img.classList.remove("lazy");
}
});
}
let lazyLoadThrottleTimeout;
document.addEventListener("scroll", function () {
if (lazyLoadThrottleTimeout) clearTimeout(lazyLoadThrottleTimeout);
lazyLoadThrottleTimeout = setTimeout(lazyLoad, 20);
});
1
2
3
<!-- 占位符⽰例 -->
<div class="lazy-placeholder" style="background-color: #ddd;height: 500px;"</div <!-- ⽚⽰ -->
<img class="lazy" data- src="path/to/image.jpg" alt="预览图" />

数组去重

数组去重

去重

在 JavaScript 中,数组去重有多种实现方式,每种方法都有其特点和适用场景。以下是常见的数组去重方法:

1. 使用 Set

Set 是一种数据结构,它会自动去除重复的值。这是最简单且效率较高的去重方法。

示例代码

1
2
3
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]

优点

  • 简洁易读。
  • 性能较好,时间复杂度为 O(n)。

缺点

  • 不兼容旧版本浏览器(IE 等)。

2. 使用 filter 和 indexOf

利用 indexOf 的特性,只有第一次出现的元素的索引才会与当前索引相等。

示例代码

1
2
3
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = arr.filter((item, index) => arr.indexOf(item) === index);
console.log(uniqueArr); // [1, 2, 3, 4, 5]

优点

  • 兼容性好,所有现代浏览器都支持。
  • 逻辑清晰。

缺点

  • 时间复杂度较高,为 O(n²),因为 indexOf 本身是 O(n) 的操作。

3. 使用 Map

Map 是一种键值对集合,可以利用它的键唯一性来去重。

示例代码

1
2
3
4
const arr = [1, 2, 2, 3, 4, 4, 5];
const map = new Map();
const uniqueArr = arr.filter(item => !map.has(item) && map.set(item, true));
console.log(uniqueArr); // [1, 2, 3, 4, 5]

优点

  • 性能较好,时间复杂度为 O(n)。
  • 兼容性好。

缺点

  • 代码稍显复杂。

4. 使用对象(哈希表)

利用对象的键唯一性来去重。

示例代码

1
2
3
4
5
6
const arr = [1, 2, 2, 3, 4, 4, 5];
const hash = {};
const uniqueArr = arr.filter(item => {
return hash.hasOwnProperty(item) ? false : (hash[item] = true);
});
console.log(uniqueArr); // [1, 2, 3, 4, 5]

优点

  • 性能较好,时间复杂度为 O(n)。
  • 兼容性好。

缺点

  • 代码稍显复杂。

5. 手动去重(循环 + 判断)

通过嵌套循环逐个检查元素是否重复。

示例代码

1
2
3
4
5
6
7
8
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [];
for (let i = 0; i < arr.length; i++) {
if (!uniqueArr.includes(arr[i])) {
uniqueArr.push(arr[i]);
}
}
console.log(uniqueArr); // [1, 2, 3, 4, 5]

优点

  • 不依赖现代 JavaScript 的新特性,兼容性极好。

缺点

  • 时间复杂度较高,为 O(n²)。
  • 代码较冗长。

6. 使用 reduce 和 Set

结合 reduceSet 实现去重。

示例代码

1
2
3
4
5
6
7
8
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = arr.reduce((acc, item) => {
if (!acc.includes(item)) {
acc.push(item);
}
return acc;
}, []);
console.log(uniqueArr); // [1, 2, 3, 4, 5]

优点

  • 使用了函数式编程的思想,代码更简洁。
  • 兼容性好。

缺点

  • 时间复杂度较高,为 O(n²)。

7. 使用 Object.keys

利用对象的键唯一性,通过 Object.keys 去重。

示例代码

1
2
3
4
5
6
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = Object.keys(arr.reduce((acc, item) => {
acc[item] = true;
return acc;
}, {}));
console.log(uniqueArr); // ["1", "2", "3", "4", "5"]

优点

  • 利用对象的键唯一性,代码简洁。
  • 性能较好,时间复杂度为 O(n)。

缺点

  • 返回的结果是字符串数组,需要额外处理。

8. 使用 ES6 的 Array.from

结合 SetArray.from 去重。

示例代码

1
2
3
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = Array.from(new Set(arr));
console.log(uniqueArr); // [1, 2, 3, 4, 5]

优点

  • 代码简洁。
  • 性能较好,时间复杂度为 O(n)。

缺点

  • 不兼容旧版本浏览器。

总结

  • 推荐使用方法:如果兼容性不是问题,推荐使用 SetMap,因为它们的性能较好且代码简洁。
  • 兼容性优先:如果需要兼容旧版本浏览器,可以使用 filterindexOf 或手动去重方法。
  • 函数式编程:如果喜欢函数式编程,可以使用 reduceSet 的组合。
    根据你的具体需求选择合适的方法即可!

生成器和迭代器

生成器和迭代器

GeneratorFunction和Generator

GeneratorFunction 和 Generator

在JavaScript中,GeneratorFunctionGenerator是ES6引入的两个重要概念,它们主要用于处理异步编程和迭代器的创建。

1. GeneratorFunction

GeneratorFunction是一种特殊的函数,通过在function关键字后面加上一个星号(*)来定义。这种函数不会立即执行其内部代码,而是返回一个Generator对象。GeneratorFunction使用yield关键字来暂停和恢复函数的执行。
基本语法:

1
2
3
4
5
6
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
return 4;
}

示例:

1
2
3
4
5
6
7
8
9
10
11
function* generatorFunction() {
console.log("Start");
yield 1;
console.log("Resume");
yield 2;
console.log("End");
}
const gen = generatorFunction();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undefined, done: true }

输出:

1
2
3
4
5
6
Start
{ value: 1, done: false }
Resume
{ value: 2, done: false }
End
{ value: undefined, done: true }

2. Generator

GeneratorGeneratorFunction返回的迭代器对象。通过调用Generator对象的next()方法,可以控制函数的执行流程,每次调用next()方法会执行到下一个yield语句并返回相应的值。
基本特性:

  1. 定义方式:使用function*语法定义。
  2. 返回值:调用GeneratorFunction时返回一个Generator对象。
  3. yield关键字:用于生成值并暂停函数执行。
  4. next()方法:用于恢复函数执行并获取下一个值。
    示例:
1
2
3
4
5
6
7
8
function* generatorFunction() {
yield 'hello';
yield 'world';
}
const gen = generatorFunction();
console.log(gen.next().value); // 'hello'
console.log(gen.next().value); // 'world'
console.log(gen.next().value); // undefined

3. Generator的异步编程应用

Generator在异步编程中非常有用,可以通过yield关键字暂停和恢复异步任务的执行。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function fetch(url) {
return new Promise((resolve, reject) => {
console.log('异步操作执行中:' + url);
setTimeout(() => {
console.log('异步操作完成:' + url);
resolve({ status: 1, msg: '异步操作完成:' + url, data: null });
}, 2000);
});
}
function* taskGenerator() {
const task1 = yield fetch('https://api.example.com/task1');
const task2 = yield fetch('https://api.example.com/task2');
const task3 = yield fetch('https://api.example.com/task3');
console.log(task1);
console.log(task2);
console.log(task3);
}
function executeGenerator(generator) {
const iterator = generator();
function handle(iteratorResult) {
if (iteratorResult.done) {
return Promise.resolve(iteratorResult.value);
}
return Promise.resolve(iteratorResult.value)
.then(res => handle(iterator.next(res)))
.catch(err => iterator.throw(err));
}
try {
return handle(iterator.next());
} catch (err) {
return Promise.reject(err);
}
}
executeGenerator(taskGenerator)
.then(() => {
console.log('所有任务完成');
})
.catch(err => {
console.error('执行过程中出错:', err);
});

4. 与async/await结合使用

Generatorasync/await可以很好地结合使用,以简化异步编程,并使代码更具可读性和可维护性。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function getData() {
return new Promise(resolve => {
setTimeout(() => {
resolve('数据');
}, 1000);
});
}
function* fetchAsyncData() {
try {
const data = yield getData();
console.log('获取到的数据:', data);
const moreData = yield getData();
console.log('更多的数据:', moreData);
return '完成';
} catch (error) {
console.error('发生错误:', error);
return '出错';
}
}
async function fetchData() {
try {
const generator = fetchAsyncData();
let result = generator.next();
while (!result.done) {
result = await result.value;
result = generator.next(result);
}
console.log('最终结果:', result.value);
} catch (error) {
console.error('发生错误:', error);
}
}
fetchData();

通过结合使用 Generatorasync/await,可以以顺序和分步的方式处理异步操作,使代码更清晰、易读,并且能够很好地处理异常情况。

迭代器(Iterator)

迭代器是一种设计模式,用于顺序访问一个聚合对象中的各个元素,而不暴露其内部的表示形式。在JavaScript中,迭代器是一个对象,它实现了Iterator接口,该接口定义了两个方法:next()return()

1. 迭代器接口

基本方法:

  • next():返回一个包含两个属性的对象:valuedone
    • value:当前迭代的值。
    • done:一个布尔值,表示迭代是否已经完成。如果donetrue,则value通常为undefined
  • return(value):提前结束迭代器,并返回一个donetrue的对象。value是可选的返回值。

2. 创建迭代器

可以通过实现Iterator接口来创建自定义迭代器。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const myIterator = {
data: [1, 2, 3],
next: function() {
if (this.data.length > 0) {
return { value: this.data.shift(), done: false };
} else {
return { value: undefined, done: true };
}
}
};
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { value: undefined, done: true }

3. 使用Symbol.iterator方法

为了使对象可迭代,可以实现Symbol.iterator方法,该方法返回一个迭代器对象。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (let value of myIterable) {
console.log(value); // 1, 2, 3
}

4. 原生可迭代对象

JavaScript中许多内置对象已经实现了Symbol.iterator方法,因此它们是可迭代的,可以直接使用for...of循环。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const array = [1, 2, 3];
for (let value of array) {
console.log(value); // 1, 2, 3
}
const string = 'hello';
for (let char of string) {
console.log(char); // h, e, l, l, o
}
const map = new Map([[1, 'one'], [2, 'two']]);
for (let [key, value] of map) {
console.log(key, value); // 1 one, 2 two
}
const set = new Set([1, 2, 3]);
for (let value of set) {
console.log(value); // 1, 2, 3
}

5. 迭代器的高级用法

生成器作为迭代器: 生成器函数(function*)返回一个迭代器对象,可以非常方便地创建复杂的迭代逻辑。
示例:

1
2
3
4
5
6
7
8
9
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const gen = generatorFunction();
for (let value of gen) {
console.log(value); // 1, 2, 3
}

自定义迭代器: 可以自定义迭代器来处理复杂的集合或数据结构。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
let end = this.end;
return {
next: () => {
if (current <= end) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
}
const range = new Range(1, 5);
for (let value of range) {
console.log(value); // 1, 2, 3, 4, 5
}

总结

迭代器是一种强大的设计模式,可以用于顺序访问集合中的元素,而不暴露其内部结构。在JavaScript中,通过实现Symbol.iterator方法或使用生成器函数,可以轻松创建自定义迭代器,从而实现更灵活和高效的集合遍历。

生成器(Generator)和迭代器(Iterator)

在JavaScript中,生成器(Generator)和迭代器(Iterator)是两个密切相关但又有所不同的概念。它们共同的目标是提供一种顺序访问集合或数据流的方式,但实现和使用方式有所不同。

1. 迭代器(Iterator)

定义: 迭代器是一种设计模式,用于顺序访问一个聚合对象中的各个元素,而不暴露其内部的表示形式。在JavaScript中,迭代器是一个对象,它实现了Iterator接口,该接口定义了两个方法:next()return()
基本方法:

  • next():返回一个包含两个属性的对象:valuedone
    • value:当前迭代的值。
    • done:一个布尔值,表示迭代是否已经完成。如果donetrue,则value通常为undefined
  • return(value):提前结束迭代器,并返回一个donetrue的对象。value是可选的返回值。
    示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const myIterator = {
data: [1, 2, 3],
next: function() {
if (this.data.length > 0) {
return { value: this.data.shift(), done: false };
} else {
return { value: undefined, done: true };
}
}
};
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { value: undefined, done: true }

使用Symbol.iterator方法: 为了使对象可迭代,可以实现Symbol.iterator方法,该方法返回一个迭代器对象。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (let value of myIterable) {
console.log(value); // 1, 2, 3
}

2. 生成器(Generator)

定义: 生成器是一种特殊的函数,通过在function关键字后面加上一个星号(*)来定义。这种函数不会立即执行其内部代码,而是返回一个Generator对象。Generator对象是一个迭代器,可以通过调用其next()方法来控制函数的执行流程,每次调用next()方法会执行到下一个yield语句并返回相应的值。
基本语法:

1
2
3
4
5
6
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
return 4;
}

示例:

1
2
3
4
5
6
7
8
9
10
11
function* generatorFunction() {
console.log("Start");
yield 1;
console.log("Resume");
yield 2;
console.log("End");
}
const gen = generatorFunction();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undefined, done: true }

输出:

1
2
3
4
5
6
Start
{ value: 1, done: false }
Resume
{ value: 2, done: false }
End
{ value: undefined, done: true }

3. 生成器和迭代器的关系

生成器函数返回的Generator对象是一个迭代器,因此生成器可以看作是迭代器的一种更高级的实现方式。生成器通过yield关键字来暂停和恢复函数的执行,使得编写复杂的迭代逻辑变得更加简单和直观。
示例:

1
2
3
4
5
6
7
8
9
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const gen = generatorFunction();
for (let value of gen) {
console.log(value); // 1, 2, 3
}

4. 生成器的异步编程应用

生成器在异步编程中非常有用,可以通过yield关键字暂停和恢复异步任务的执行。结合async/await,可以进一步简化异步编程的复杂性。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function fetch(url) {
return new Promise((resolve, reject) => {
console.log('异步操作执行中:' + url);
setTimeout(() => {
console.log('异步操作完成:' + url);
resolve({ status: 1, msg: '异步操作完成:' + url, data: null });
}, 2000);
});
}
function* taskGenerator() {
const task1 = yield fetch('https://api.example.com/task1');
const task2 = yield fetch('https://api.example.com/task2');
const task3 = yield fetch('https://api.example.com/task3');
console.log(task1);
console.log(task2);
console.log(task3);
}
function executeGenerator(generator) {
const iterator = generator();
function handle(iteratorResult) {
if (iteratorResult.done) {
return Promise.resolve(iteratorResult.value);
}
return Promise.resolve(iteratorResult.value)
.then(res => handle(iterator.next(res)))
.catch(err => iterator.throw(err));
}
try {
return handle(iterator.next());
} catch (err) {
return Promise.reject(err);
}
}
executeGenerator(taskGenerator)
.then(() => {
console.log('所有任务完成');
})
.catch(err => {
console.error('执行过程中出错:', err);
});

5. 与async/await结合使用

Generatorasync/await可以很好地结合使用,以简化异步编程,并使代码更具可读性和可维护性。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getData() {
return new Promise(resolve => {
setTimeout(() => {
resolve('数据');
}, 1000);
});
}
async function fetchData() {
const data1 = await getData();
console.log('获取到的数据1:', data1);
const data2 = await getData();
console.log('获取到的数据2:', data2);
return '完成';
}
fetchData().then(result => {
console.log('最终结果:', result); // 完成
});

总结

  • 迭代器:是一种设计模式,用于顺序访问集合中的元素,不暴露内部结构。通过实现Symbol.iterator方法,可以使对象可迭代。
  • 生成器:是一种特殊的函数,通过function*定义,返回一个迭代器对象。生成器通过yield关键字暂停和恢复函数的执行,简化了复杂迭代逻辑的编写。
  • 结合使用:生成器和async/await可以结合使用,进一步简化异步编程,使代码更清晰、易读,并且能够很好地处理异常情况。
    通过理解和使用生成器和迭代器,可以更高效地处理集合和异步任务,提升代码的可维护性和可读性。

返回值是迭代器

在JavaScript中,有多种方式可以获取迭代器。以下是一些常见的函数和方法,它们的返回值是迭代器:

1. 生成器函数(Generator Function)

生成器函数通过在function关键字后面加上一个星号(*)来定义。调用生成器函数会返回一个Generator对象,该对象是一个迭代器。
示例:

1
2
3
4
5
6
7
8
9
10
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const gen = generatorFunction();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

2. 数组的[Symbol.iterator]方法

数组对象实现了Symbol.iterator方法,调用该方法会返回一个迭代器。
示例:

1
2
3
4
5
6
const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

3. 字符串的[Symbol.iterator]方法

字符串对象也实现了Symbol.iterator方法,调用该方法会返回一个迭代器。
示例:

1
2
3
4
5
6
7
8
const string = 'hello';
const iterator = string[Symbol.iterator]();
console.log(iterator.next()); // { value: 'h', done: false }
console.log(iterator.next()); // { value: 'e', done: false }
console.log(iterator.next()); // { value: 'l', done: false }
console.log(iterator.next()); // { value: 'l', done: false }
console.log(iterator.next()); // { value: 'o', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

4. Map对象的[Symbol.iterator]方法

Map对象实现了Symbol.iterator方法,调用该方法会返回一个迭代器,迭代器的每个值是一个键值对数组。
示例:

1
2
3
4
5
const map = new Map([[1, 'one'], [2, 'two']]);
const iterator = map[Symbol.iterator]();
console.log(iterator.next()); // { value: [1, 'one'], done: false }
console.log(iterator.next()); // { value: [2, 'two'], done: false }
console.log(iterator.next()); // { value: undefined, done: true }

5. Set对象的[Symbol.iterator]方法

Set对象实现了Symbol.iterator方法,调用该方法会返回一个迭代器,迭代器的每个值是集合中的一个元素。
示例:

1
2
3
4
5
6
const set = new Set([1, 2, 3]);
const iterator = set[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

6. arguments对象的[Symbol.iterator]方法

在函数内部,arguments对象也实现了Symbol.iterator方法,调用该方法会返回一个迭代器,迭代器的每个值是函数的参数。
示例:

1
2
3
4
5
6
7
8
function test() {
const iterator = arguments[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
}
test(1, 2, 3);

7. 自定义对象的[Symbol.iterator]方法

可以通过在自定义对象上实现Symbol.iterator方法,使其返回一个迭代器。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
const iterator = myIterable[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

8. Array.prototype.entries()Array.prototype.keys()Array.prototype.values()

这些方法返回的都是迭代器,分别用于迭代数组的键值对、键和值。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const array = [1, 2, 3];
// 键值对迭代器
const entriesIterator = array.entries();
console.log(entriesIterator.next()); // { value: [0, 1], done: false }
console.log(entriesIterator.next()); // { value: [1, 2], done: false }
console.log(entriesIterator.next()); // { value: [2, 3], done: false }
console.log(entriesIterator.next()); // { value: undefined, done: true }
// 键迭代器
const keysIterator = array.keys();
console.log(keysIterator.next()); // { value: 0, done: false }
console.log(keysIterator.next()); // { value: 1, done: false }
console.log(keysIterator.next()); // { value: 2, done: false }
console.log(keysIterator.next()); // { value: undefined, done: true }
// 值迭代器
const valuesIterator = array.values();
console.log(valuesIterator.next()); // { value: 1, done: false }
console.log(valuesIterator.next()); // { value: 2, done: false }
console.log(valuesIterator.next()); // { value: 3, done: false }
console.log(valuesIterator.next()); // { value: undefined, done: true }

9. Map.prototype.entries()Map.prototype.keys()Map.prototype.values()

这些方法返回的都是迭代器,分别用于迭代Map的键值对、键和值。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const map = new Map([[1, 'one'], [2, 'two']]);
// 键值对迭代器
const entriesIterator = map.entries();
console.log(entriesIterator.next()); // { value: [1, 'one'], done: false }
console.log(entriesIterator.next()); // { value: [2, 'two'], done: false }
console.log(entriesIterator.next()); // { value: undefined, done: true }
// 键迭代器
const keysIterator = map.keys();
console.log(keysIterator.next()); // { value: 1, done: false }
console.log(keysIterator.next()); // { value: 2, done: false }
console.log(keysIterator.next()); // { value: undefined, done: true }
// 值迭代器
const valuesIterator = map.values();
console.log(valuesIterator.next()); // { value: 'one', done: false }
console.log(valuesIterator.next()); // { value: 'two', done: false }
console.log(valuesIterator.next()); // { value: undefined, done: true }

10. Set.prototype.values()

Set.prototype.values()方法返回一个迭代器,用于迭代Set中的值。注意,Setvalues()方法和[Symbol.iterator]方法是等价的。
示例:

1
2
3
4
5
6
7
const set = new Set([1, 2, 3]);
// 值迭代器
const valuesIterator = set.values();
console.log(valuesIterator.next()); // { value: 1, done: false }
console.log(valuesIterator.next()); // { value: 2, done: false }
console.log(valuesIterator.next()); // { value: 3, done: false }
console.log(valuesIterator.next()); // { value: undefined, done: true }

总结

这些方法和函数返回的都是迭代器,可以通过调用next()方法来逐个获取集合中的元素。迭代器在处理集合和数据流时非常有用,尤其是在需要逐个处理元素或处理异步数据时。