console.log('\u1f436');
console.log('\u{1f436}');
这段代码是在控制台中输出表情符号 "🐕"。
第一行代码使用了 Unicode 编码转义字符"\u",其后跟着四个十六进制数字 "1f436",它代表 "" 的 Unicode 编码,这个编码超出了 ASCII 码表中的范围,所以需要使用 Unicode 编码转义字符进行表示。
第二行代码使用了 Unicode Code Point 表示法,其中 "\u{" 表示开头,"}" 表示结尾,中间是十六进制编码 "1f436",即 "" 的 Unicode Code Point。
需要注意的是,输出表情符号需要你的终端或控制台支持 Unicode 码,否则可能无法正确显示。
const regexp1 = /^a/g;
const regexp2 = new RegExp('^a', 'g');
const regexp3 = new RegExp(/a/g);
const regexp4 = new RegExp(/a/);
console.log('aabbcc'.match(regexp1));
console.log('babbcc'.match(regexp1));
console.log('aabbccaabbaa'.match(regexp3));
console.log('aabbccaabbaa'.match(regexp4));
第一行用正则表达式字面量创建一个正则表达式,等价于 `new RegExp('^a', 'g')`,用来匹配以字母"a"为开头的字符串。
第二行用 `new RegExp` 构造函数创建一个正则表达式,第一个参数`'^a'`表示以"a"字母开头,第二个参数`'g'`表示全局匹配。
第三行用 `new RegExp` 构造函数创建一个正则表达式,使用正则表达式字面量`/a/g`的方式作为参数创建。
第四行用 `new RegExp` 构造函数创建一个正则表达式,只传递`/a/`作为参数,这种方式创建的正则表达式只会匹配首次出现的"a"字母。
第五行将正则表达式`regexp1`应用于字符串`'aabbcc'`,得到两次匹配结果`['a']`。因为`regexp1`使用了`g`标志,会在整个字符串中查找和匹配所有以字母"a"开头的子串。
第六行将正则表达式`regexp1`应用于字符串`'babbcc'`,由于字符串不以字母"a"开头,因此返回`null`。
第七行将正则表达式`regexp3`应用于字符串`'aabbccaabbaa'`,得到匹配结果`['a', 'a', 'a']`,可以匹配到所有字母"a"。
第八行将正则表达式`regexp4`应用于字符串`'aabbccaabbaa'`,得到匹配结果`['a']`,只能匹配到首次出现的字母"a"。
正则表达式字面量和 `new RegExp` 构造函数都可以创建正则表达式,但是它们有一些区别。字面量的方式创建更加简洁,但是它不能够接收变量,而且无法实现一些动态构建正则表达式的功能,`new RegExp` 则可以接收参数,可以动态构建正则表达式。
const r1 = /imooc/g;
const r2 = /imooc/y;
const str = 'imoocimooc-imooc';
console.log(r1.exec(str));
console.log(r1.exec(str));
console.log(r1.exec(str));
console.log(r1.exec(str));
console.log('-----------------');
console.log(r2.exec(str));
console.log(r2.exec(str));
console.log(r2.exec(str));
这段代码展示了正则表达式中的全局匹配 `/g` 和粘连匹配`/y` 的差异,以及对于两种匹配模式下 `exec()` 方法的行为。
第一行和第二行分别创建两个正则表达式,`/imooc/g` 是全局匹配,`/imooc/y`是粘连匹配。全局匹配可以多次匹配,而粘连匹配只能连续匹配。
第四行至第七行打印使用全局匹配 `/imooc/g` 的正则表达式的多次匹配结果。方法 exec() 可以匹配字符串中的下一个匹配项,并返回一个包含匹配信息的数组,若不存在匹配项则返回null。在全局匹配下,每次调用 exec() 方法可以匹配到字符串中下一次符合正则表达式的匹配项。但是这里都打印不出结果,是因为正则表达式中的 pattern "imooc" 不是唯一的字符或字符串,与下方第 4 行解释成分组 `/(imooc)/g` 不同,无法通过单纯的 `imooc` 来匹配字符串。
- 第九行至第十一行打印使用粘连匹配 `/imooc/y` 的正则表达式的多次匹配结果,使用粘连匹配的时候需要注意的是匹配的上一个结果必须是匹配结果的末尾,否则匹配将停止。而后续的匹配将从最近的一个匹配结果的末尾开始。因此在这里只有第一次匹配能够成功,后面两次因为不符合上一个匹配结果的末尾,都返回了 null。而这里的 pattern "imooc" 仍然因为是全文唯一子串而得到了匹配。
需要注意,在实际编程中,全局匹配比粘连匹配用得更普遍,并且全局匹配模式下,更常常使用 String 对象的 replace 方法和 String 对象中的 matchAll 方法,而不是使用 exec() 方法。正则表达式粘连匹配模式的应用场景更特殊。
在这段代码中,“imoocimooc-imoo c”字符串中确实有两个连续的“imooc”,所以使用粘连匹配 `/imooc/y` 的时候,第一个匹配结果的结尾位置就是下一个匹配的开始位置,因此后续的匹配也能够完成。所以在第九至第十一行,都会打印出匹配结果 `['imooc']` 并且匹配结束后返回 null。
对于全局匹配 `/imooc/g`,由于匹配不到任何东西,所以前面的四次 `exec()` 方法都返回了 null。这是因为在全局模式下,如果字符串中没有找到符合正则表达式的项,`exec()` 方法返回 null,而不是返回空数组。
console.log(0o16);
console.log(0b1111);
这段代码中使用了数值的不同进制表示方式来输出不同的结果。
- 第一行输出的是8进制数0o16,它的值等于10进制数的14,因为0o16代表的是0*8^1 + 1*8^0 = 14。
- 第二行输出的是2进制数0b1111,它的值等于10进制数的15,因为0b1111代表的是1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = 15。
console.log(window.parseInt('1.23'));
console.log(parseFloat('1.23'));
console.log(Number.parseInt(1.23));
console.log(Number.parseFloat(1.23));
第一行代码使用全局的 parseInt() 方法将字符串'1.23'转换为整数,结果是1,因为 parseInt() 方法在遇到非数字字符时会停止转换并返回已转换的结果。不过,因为此时传入的值有小数点,parseInt() 方法会将其作为不正确的格式解析,因此只会取整数部分1。
- 第二行代码使用全局的 parseFloat() 方法将字符串'1.23'转换为浮点数,结果是1.23。这个方法可以正确处理小数点。
- 第三行代码使用 Number.parseInt() 方法与第一行代码做了相同的事情,将浮点数1.23转换为整数1。
- 第四行代码使用 Number.parseFloat() 方法与第二行代码做了相同的事情,将数值1.23转换为浮点数1.23。
console.log(Number.isNaN(NaN));
console.log(Number.isNaN(-NaN));
console.log(Number.isNaN(1));
console.log(Number.isNaN('1'));
console.log(Number.isNaN(true));
function isNaN(value) {
return value !== value;
}
console.log(isNaN(NaN));
console.log(isNaN(-NaN));
console.log(isNaN(1));
console.log(isNaN('1'));
console.log(isNaN(true));
这段代码中使用了全局的 isNaN() 方法和自定义的 isNaN() 函数来判断值是否为 NaN (非数字)。这两种方法的区别在于对于一些特殊值的处理不同。
第一行代码调用的是 Number.isNaN() 方法,它将 NaN 视为唯一的非数字值。因此,第一行代码返回 true。
- 第二行代码调用的是 Number.isNaN() 方法,它将 -NaN 也视为 NaN,因此返回 true。
- 第三行代码调用的是 Number.isNaN() 方法,因为传入的参数是一个数字,不是 NaN,所以返回 false。
- 第四行代码调用的是 Number.isNaN() 方法,它只会对于数字类型的值返回 false,因此将字符串转换为数字后返回 false。
- 第五行代码调用的是 Number.isNaN() 方法,它只会对于数字类型的值返回 false,true 在数字类型使用时转换为 1,因此返回 false。
自定义的 isNaN() 函数实现了与 Number.isNaN() 方法类似的功能:
- 第七行代码将 NaN 与任何值进行比较,结果都是 false,因此传入 NaN 返回 true。
- 第八行代码与第七行代码的结果相同,因为 -NaN 与任何值比较结果都是 false,也将其判断为 NaN,返回 true。
- 第九行代码将 1 与 NaN 进行比较,结果是 false,因此传入数字 1 返回 false。
- 第十行代码将字符串 '1' 转换为数字后与 NaN 进行比较,结果是 false,因此传入字符串 '1' 返回 false。
- 第十一行代码与第五行代码的结果相同,因为传入 boolean 值时,它会被转换为数字类型,true 转换为 1,与 NaN 比较结果是 false,因此返回 false。
console.log(Number.isFinite(Infinity));
console.log(Number.isFinite(2 / 0));
console.log(Number.isFinite(2 / 4));
console.log(Number.isFinite(1234));
console.log(Number.isFinite('1234'));
console.log(Number.isFinite(true));
console.log(Number.isFinite(NaN));
这段代码中使用了 Number.isFinite() 方法来判断传入的值是否为有限数(非 Infinity、-Infinity 和 NaN)。
- 第一行代码传入 Infinity,结果是 false,因为 Infinity 是无限大的数,不是有限数。
- 第二行代码传入 2 / 0,结果是 false,因为 2 / 0 得到的值为 Infinity,同样不是有限数。
- 第三行代码传入 2 / 4,结果是 true,因为它是一个有限的数,可以表示为简单的分数 1/2。
- 第四行代码传入整数 1234,结果是 true,因为所有整数都是有限的数。
- 第五行代码传入字符串 '1234',结果是 false,因为字符串需要先转换为数字,如果不能转换,则判断为非有限数。
- 第六行代码传入布尔值 true,它被转换为数字 1,因此结果是 true,true 是一个有限的数字。
- 第七行代码传入 NaN,结果是 false,因为 NaN 是一个特殊的非数字类型,不是有限数。
Number.isSafeInteger();
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MIN_SAFE_INTEGER);
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER - 1));
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1));
这段代码中展示了用于检查一个数值是否安全的操作的方法,以及 JavaScript 环境中可以表示的最小和最大的安全整数。
- 第一行代码调用了 Number.isSafeInteger() 方法,它用于判断一个整数是否为安全整数。安全整数是指绝对值不大于 2^53 - 1,或者说在 JavaScript 环境中,能够精确表示的整数范围。如果传入的值是一个安全整数,方法返回 true,否则返回 false。
- 第二行代码输出了 JavaScript 中可以表示的最大安全整数 Number.MAX_SAFE_INTEGER,它等于 2^53 - 1,即 9007199254740991。
- 第三行代码传入一个小于最大安全整数的整数(MAX_SAFE_INTEGER - 1),判断它是否为安全整数,结果是 true。
- 第四行代码传入一个大于最大安全整数的整数(MAX_SAFE_INTEGER + 1),判断它是否为安全整数,结果是 false。
let a = (2 ** 10) ** 0;
console.log(a);
这段代码演示了如何使用 ES7 中添加的指数运算符 ** 来进行幂运算。
- 第一行代码使用指数运算符 ** 计算 2 的 10 次幂,即 2^10,得到的结果是 1024。
- 第二行代码将计算结果再次使用指数运算符进行幂运算,其指数为 0,即将结果再次乘以 1。因为任何数的 0 次幂都等于 1,所以 a 会是 1。
因此,最终会输出 1。同时,需要注意的是,指数运算符具有右结合性,即上述代码实际上相当于 `(2 ** 10) ** 0`。
function People({ name, age = 38 } = {name: 1}) {
console.log(name, age);
};
People({ name: 3 });
这段代码定义了一个名为 People() 的函数,并将一个参数对象解构赋值给参数列表。该函数的参数对象具有两个属性:name 和 age。其中,name 属性是必需的(没有默认值),而 age 属性有一个默认值 38。此外,函数的整个参数对象也有一个默认值 {name: 1}。
当函数调用时,实参传入的是一个对象,对象的 name 属性值为 3,同时没有 age 属性。在这种情况下,函数参数对象的解构赋值会用传入的实参对象为其匹配属性值。由于实参对象没有定义 age 属性,因此 age 属性会被解构成 undefined,而 People 中 age 的默认值 38 会被忽略。最终,函数会输出 "3 undefined"。
需要注意的是,如果实参对象中也没有 name 属性,那么函数在解构是会使用它的第二个参数对象 {name: 1} 作为参数,并在解构时将其作为 name 属性值。这种方式不建议使用,因为它会在函数调用时给参数列表添加一个非必需的默认值。
如果要输出age为38, people里面的函数应该怎么写?
要输出 age 为 38,你可以将 `age = 38` 的默认值放到解构赋值语句外面:
```javascript
function People({ name, age } = { name: 1, age: 38 }) {
function add(a, b = 999 + b, c = 1) {
console.log(a, b);
}
add(1);
这段代码展示了一个函数的默认参数值的赋值过程。函数 add() 定义了三个参数:a,b 和 c,其中 b 和 c 拥有默认值,分别为表达式 "999 + b" 和常量 1。
在调用 add() 函数时,只提供了一个实际输入参数值 1,因此 a 参数被赋为输入值 1,而 b 参数和 c 参数都没有提供实际参数值,因此会使用它们的默认参数值。具体而言,b 参数的默认值为表达式 "999 + b",这是一个自引用的表达式,其中使用了 b 这个变量。由于 b 尚未被定义,其类型是 undefined,因此在计算表达式时会将 undefined 视为 NaN。因此,b 参数的默认值实际上是 NaN (Not a Number)。
因此,最终输出的结果是 "1 NaN",反映了函数的输入和默认参数值的真实情况。需要注意,在实际编程中尽量避免在默认参数值的表达式中引用它本身,以免出现难以预测的逻辑错误。
function sum(...args) {
let args = Array.prototype.slice.call(arguments);
let args = [...arguments];
let [...args] = arguments;
console.log(args);
}
sum(1, 2, 321, 4354, 'fdafsd');
这段代码是一个JavaScript函数,用于计算任意数量参数的总和。它使用了3种不同的方式来获取函数的所有参数,并将它们转换成具有数字和字符串的数组。
第1个 let 语句尝试将 arguments 对象转换为一个数组,使用的是传统的 Array.prototype.slice 方法,该方法将 arguments 的每个元素复制到新数组中。
第2个 let 语句尝试使用 扩展运算符(...) 将 arguments 对象转换为一个数组,它是ES6中新的方法。
第3个 let 语句使用ES6中提供的另一种方式,使用解构赋值语法将 arguments 对象中的元素分配给数组。这是一种更简洁的方法,也是无需使用扩展符法就可以将所有参数放入数组的方法。
最终,当函数被调用时,控制台将输出一个数组,其中包含所有按顺序传递给函数的参数。如果在参数中有非数字元素,则它们也会包含在数组中。
function op(type, b, ...nums) {
console.log(type);
console.log(nums);
}
op('sum', 1, 23, 454, 3, 67, 234);
这段代码是一个JavaScript函数,名为"op",用于执行特定类型的数学运算(加、减、乘除等)。该函数有三个参数:type(运算类型)、b(第二个操作数)和 nums(其余的操作数)。
在这个函数中,使用了 rest parameters(...)来处理所有传递给函数的额外参数,这意味着可以传递任意数量的操作数并将它们传递给 nums 数组。 所有余下的参数都将依次放入 nums 数组中。
然后将 type 和 nums 变量打印在控制台上,用于检查函数是否按预期接收了参数。在这个例子中,type的值为 "sum" ,即求和运算,b的值为1,nums的值是一个数组,其中包含四个额外的参数:23、454、3 和 67。
总的来说,这个函数展示了如何在JavaScript中使用rest parameter语法接收可变参数,同时也展示了如何将参数传递给其他部分进行处理,这在一些常见的函数操作中非常常见。
function sum(...numbers) {
return numbers.reduce(function(a, b) {
return a + b;
}, 0);
}
console.log(sum(1, 2, 3, 4));
这段代码是一个JavaScript函数,名为 "sum",这个函数用于计算传递给它的任意数量的参数的和。参数的数量不限,使用了 rest parameter 语法(...numbers)来表明这一点。
在函数体中,使用了数组方法 reduce 来将所有参数相加起来。reduce 方法接收一个合并函数作为参数,这个函数将一个累加器(变量 a)和每个参数(变量 b)相加,并将结果返回;最后一个参数表示累加器的初始值,这里设置为 0。
在这个例子中,将传递参数1、2、3、4给函数,函数内部的 reduce 函数将所有四个参数相加,并返回相加后的总和,也就是10。最后,在控制台上使用 console.log 输出这个总和值。
总之,这个函数演示了如何使用JavaScript中的 rest parameter 语法,并使用reduce方法来计算任意数量的参数总和。
这段代码的ruduce是怎么运作的?
reduce() 方法有两个参数:回调函数和optional initial value,即可选的初始值。如果设置了初始值,则在第一次迭代时使用它,否则将使用数组的第一个元素作为初始值,并跳过第一次元素的迭代。
在这个例子中,sum(1, 2, 3, 4) 将产生 sum() 函数的传递参数 1、2、3、4 并相加它们。reduce() 方法使用回调函数将相邻数字相加并返回总和 10。该值被返回,并通过 console.log() 打印出来。
箭头函数
const add1 = (a, b) => {
a += 1;
return a + b;
};
这段代码定义了一个名为 "add1" 的箭头函数,它接受两个参数 "a" 和 "b",并将 "a" 的值增加 1,然后将 "a" 和 "b" 相加并返回结果。
这个函数使用了箭头函数的语法来定义它。箭头函数的语法是 ES6 中新增的语法特性,它可以简写传统的函数表达式。
箭头函数的定义通过将参数放在括号 () 内,接着是箭头符号 =>,并在其之后放置函数体。在这个例子中,箭头函数具有两个参数 "a" 和 "b",箭头符号后面是一个函数体,它表示该函数有一个任务,即将 "a" 增加 1,然后将结果和 "b" 相加并返回。
当调用该函数时,它将返回将 "a" 增加 1 的值与 "b" 相加后的结果。例如,如果调用 add1(1, 2),它将返回 4,即将传递的第一个参数 1 增加 1 后再加上第二个参数 2 的结果。
总之,这个函数简单地将 "a" 向前移动了一个单位,并将它与 "b" 相加,演示了箭头函数和变量操作的基本语法。
const pop = arr => void arr.pop();
console.log(pop([1, 2, 3]));
const log = () => {
console.log(arguments);
};
log(1, 2, 3);
这段代码展示了箭头函数的不同特点和与普通函数的区别。
第一个箭头函数 "pop" 的第一个参数 "arr" 是一个用于数组对象的箭头函数。在这个例子中,它使用了 void 运算符将数组的最后一个元素弹出并返回 undefined。
void 运算符是用于将其操作数转换成undefined的一元运算符,它返回表达式的结果是undefined的原因,又因为无需使用 return 返回值,所以在这个箭头函数中使用 void 运算符可以简化代码。
第二个箭头函数 "log" 没有参数,并且使用 arguments 对象来访问调用该函数时传递的参数。这是普通函数的一种记忆,但在箭头函数中 arguments 对象是不可用的。
在类似普通函数的箭头函数内部访问 arguments 对象会抛出一个 ReferenceError,因为箭头函数没有自己的 arguments 对象。因此在这个例子中,当尝试在箭头函数 "log" 中使用 arguments 对象来访问传递给该函数的参数时,将导致执行错误。
为什么pop会弹出数组的最后一个元素3?
pop() 是 JavaScript 数组的一个方法,用于从数组中删除最后一个元素并返回它。当调用 pop() 方法时,它会修改原始数组切断它的最后一个元素,并将该元素作为值返回。
在这个例子中,pop函数实际上是一个简单的箭头函数,它的参数是数组 "arr",并在函数体中使用了该数组的 pop() 方法。
具体来说,void arr.pop() 方法将删除 "arr" 数组的最后一项,并返回该项的值。因此,如果数组 "arr" 是 [1, 2, 3] ,调用 pop() 方法将删除数字 3 并将其返回,数组变成 [1, 2],返回值 void 运算符将该值转换为 undefined。
所以,在控制台输出 pop 函数的返回值 undefined,实际上是通过在控制台输出删除列表最后一个项,即数字 3 的结果,而这个列表现在只包含两个项目,即数字 1 和数字 2。
所以,这就是为什么 pop 函数会弹出列表的最后一个元素 3,即删除列表中的最后一个元素。
const xiaoming = {
name: '小明',
say1: function() {
console.log(this);
},
say2: () => {
console.log(this);
}
}
xiaoming.say1();
xiaoming.say2();
这段代码定义了一个对象 "xiaoming",该对象包含名称属性和两个方法 "say1" 和 "say2"。
方法 "say1" 使用函数表达式的语法定义,它是一个普通函数,并且在方法体内使用了 "this" 关键字。当在对象上调用"say1()" 方法时,"this" 指向该对象 "xiaoming",因为它是函数调用的上下文。因此在控制台上输出 "xiaoming" 对象的完整属性集。
相比之下,方法 "say2" 使用了箭头函数的语法来定义,它也在函数体内使用了 'this' 关键字。当在对象上调用 "say2()" 方法时,"this" 指向它的上一个作用域,即全局作用域,在这里是 window 对象,因为箭头函数不会创建自己的作用域。因此,它会在控制台上输出全局对象 window。
当在控制台上运行这段代码时,首先调用 xiaoming 对象的 "say1()" 方法,这将输出对象 "xiaoming"。接着调用 "say2()" 方法,这也将在控制台上输出 window 对象,而不是期望的 xiaoming 对象,展示了定义箭头函数的语法和与使用普通函数的区别。
因此,这段代码演示了箭头函数与普通函数的区别,如何使用普通函数的 this 关键字,以及使用了箭头函数的 this 关键字的效果。
const xiaoming = {
name: 'xiaoming',
age: null,
getAge: function() {
let _this = this;
setTimeout(function() {
_this.age = 14;
console.log(_this);
}, 1000);
}
};
xiaoming.getAge();
这段代码定义了一个名为 xiaoming 的对象,该对象包含了名称和年龄属性以及 "getAge" 方法。
在 "getAge" 方法内部,首先定义变量 "_this" 并将其设置为 "this",以便可以在setTimeout()的回调函数中引用当前对象。setTimeout() 函数被用于模拟一个异步操作,它将调用包含函数的函数,该函数将在1000毫秒(即1秒)之后运行。
在 setTimeout() 回调函数中,当前对象的年龄将被设置为14。然后通过控制台上的console.log()函数将this关键字指向的变量(即此处的_this)输出到控制台上。在这个例子中,将输出包含name和age属性的xiaoming对象。
当调用 xiaoming.getAge()方法时,它将异步地在 1000 毫秒后输出带"age"属性的xiaoming对象,其中 "age"属性现在是 14 而非默认的null。"age" 属性被设置为对象方法内部定义的异步回调函数(通过 _this)的执行结果。
总体来说,这段代码展示了 JavaScript 中经典的this用法,旨在让将this关键字的正确内容传递到异步代码的回调函数中,以便正确地引用对象。
如果不设置_this变量,直接用this是什么结果?
如果不设置_this变量,直接使用this,则this指向的是setTimeout函数内部的this,而不是xiaoming对象。这意味着_age将不会被定义为xiaoming对象的属性,而将被定义为全局对象上的属性(使用var声明的变量将被定义在全局对象上)。因此,在控制台输出xiaoming对象时,age属性将为null,而不是14。
xiaoming.getAge();
const xiaoming = {
name: 'xiaoming',
age: null,
getAge: function() {
setTimeout(() => {
this.age = 14;
console.log(this);
}, 1000);
}
};
xiaoming.getAge();
这段代码定义了一个名为xiaoming的对象,并设置了三个属性:name, age和一个方法getAge,该方法使用setTimeout函数异步地设置xiaoming对象的age属性为14。不同于上面提到的代码,getAge方法中使用了箭头函数,而不是常规的函数表达式。箭头函数没有它们自己的this值,而是使用了它们被创建在的上下文中的this值。在这种情况下,箭头函数中的this指向xiaoming对象,而不是setTimeout函数内部的this。这使得xiaoming.age属性被正确地设置为14,因此在控制台中输出xiaoming对象时,age属性将为14,而不是null。
const getUserInfo = (id = 1) => {
const name = 'xiaoming';
const age = 10;
return {
name: name,
age: age,
say: function() {
console.log(this.name + this.age);
}
};
};
const xiaoming = getUserInfo();
这段代码定义了一个名为`getUserInfo`的函数,该函数接受一个参数`id`,默认值为1。函数内部定义了两个变量`name`和`age`,并返回一个对象,该对象有属性`name`和`age`,属性值分别为`name`和`age`变量的值。同时,对象还有一个方法`say()`,当调用该方法时,会将`name`和`age`连接起来输出。
接下来,又定义了一个变量`xiaoming`,它调用了`getUserInfo()`函数,这将返回一个包含`name`、`age`和`say()`属性的对象,并将这个对象赋值给`xiaoming`。此时可以通过`xiaoming.name`和`xiaoming.age`来访问`getUserInfo()`返回的对象的属性,也可以通过`xiaoming.say()`来调用`say()`方法来输出`name`和`age`的值。
需要注意的是,虽然在`say()`方法中使用了`this`来访问`name`和`age`属性的值,但是实际上`this`指向的是`say()`方法被调用时所在的对象,即返回的那个包含`name`、`age`和`say()`属性的对象。
对于对象中被定义为常量的属性值,不能在对象创建后被修改;但是,如果这些属性的值指向了引用类型的值,那么它们所引用的对象,其本身的属性值依然可以进行修改。
引用类型是什么?
引用类型,是JavaScript中除了基本类型(如 String、Number、Boolean、null 和 undefined)以外的所有类型。相对于基本类型的值,引用类型的值是对象,数组或函数等等。引用类型的值,不是直接存储在变量或常量中的,而是存储在内存中,通过变量或常量指向内存中的对象。这样就可以通过引用值来访问所引用的对象。
初始化一个变量或常量时,我们赋予的是指向引用类型值内存地址的指针。通常情况下当变量或常量被重新赋值时,新值会覆盖旧值。但是,如果旧值是一个指针,并且被用来引用复杂的引用类型(例如:对象,数组),那么旧值指向的对象仍然存在于内存中,新的赋值其实是将一个新的指针赋给变量或常量。
const person1 = {
name: 'Tom',
age: 20
};
const person2 = person1; //对 person2 = person1只是复制了地址
person2.age = 30;
console.log(person1.age); //输出 30
console.log(person2.age); //输出 30
```
在这个例子中,`person1` 和 `person2` 都是指向同一个对象的引用指针。当 `person2.age` 改变时,`person1.age` 也随之改变了。所以,当常量中的一个属性指向引用类型的值时,我们仍然可以修改该引用类型的值所包含的属性。
const getUserInfo = (id = 1) => {
const name = 'xiaoming';
const age = 10;
return {
name,
age,
say() {
console.log(this.name + this.age);
}
};
};
const xiaoming = getUserInfo();
const obj = {
a: 1,
$abc: 2,
'FDASFHGFgfdsgsd$#$%^&*%$#': 3
};
const key = 'age';
const xiaoming = {
name: 'xiaoming',
[`${key}123`]: 14
};
这段代码定义了一个名为 `obj` 的对象,它包含了三个属性:`a`、`$abc` 和一个非常规的属性`FDASFHGFgfdsgsd$#$%^&*%$#`,值分别是 `1`、`2` 和 `3`。其中,为了支持一些非 ASCII 字符,第三个属性名需要用引号括起来。
接下来,定义了一个常量 `key`,它的值为字符串 `'age'`。
然后,定义了一个名为 `xiaoming` 的对象,该对象包含了两个属性,一个是 `name` 属性,值为字符串 `'xiaoming'`;另一个是通过计算属性名语法来创建的属性,键名是 `'age123'`(其中 `123` 是通过计算动态生成的),值为 `14`。
const obj1 = {
a: 1,
b: 2,
d: {
aa: 1,
bb: 2
}
};
const obj2 = {
c: 3,
a: 9
};
const cObj1 = { ...obj1 };
console.log(cObj1.d.aa);
cObj1.d.aa = 999;
console.log(cObj1.d.aa);
console.log(obj1.d.aa);
这段代码定义了两个对象obj1和obj2,obj1包含键a、b和d,其中d是具有键aa和bb的嵌套对象,obj2包含键c和a。然后,通过展开运算符(...)将obj1的内容复制到另一个变量cObj1中。
接下来,可以看到在输出cObj1.d.aa之前,此属性的值为1。然后,将cObj1.d.aa的值更改为999,并再次输出cObj1.d.aa和obj1.d.aa。此时,两个值都被更改为999,因为cObj1和obj1共享同一个嵌套对象d。因此,更改其中一个变量中d的aa属性将同时更改另一个变量中d的aa属性。
const newObj = {
...obj2,
...obj1
};
newObj.d.aa = 22;
console.log(obj1);
这段代码定义了一个新的对象newObj,通过使用展开运算符(...),将obj2和obj1合并到newObj中。由于obj1在展开之后先出现了,因此obj2中的属性仅在obj1中不存在的情况下才会添加到newObj中。因此,在此示例中,newObj包含所有四个属性(a、b、c和d),其中a属性的值由obj2中的属性覆盖掉了obj1中的属性。然后,将newObj.d.aa的值更改为22。由于newObj和obj1共享同一个嵌套对象d,因此更改newObj中的d对象的aa属性也会更改obj1中的d对象的aa属性。因此,当使用console.log打印obj1时,可以看到d.aa的值已更改为22。
Object.is
===
+0 -0
NaN
console.log(Object.is(+0, -0));
console.log(+0 === -0);
console.log(Object.is(NaN, NaN));
console.log(NaN === NaN);
console.log(Object.is(true, false));
console.log(Object.is(true, true));
这段代码主要比较了`Object.is`方法和`===`运算符在比较几种特殊情况下的表现。
Object.is`方法是ES6引入的一种用于比较两个值是否相等的方法,其比较规则与`===`运算符略有不同。
在比较 +0 和 -0 的时候,`Object.is`方法返回false,因为+0 和 -0 表示相同的数值,但是带有不同的符号。
而`===`运算符在比较 +0 和 -0 的时候会返回true,因为它们表示相同的数值。
在比较 NaN 的时候,`Object.is`方法和`===`运算符都返回false,因为NaN 与任何值都不相等,包括其本身。
最后两个比较是用 非特殊数据类型 进行的测试。它表示了一个布尔值与另一个布尔值之间的比较。 对于这种情况,`Object.is`方法与`===`运算符实际上会产生相同的结果:false(true与false不相等) 和 true(true与true相等)。
const obj = Object.assign({a: 1}, {b: 2}, {c: 3}, {d: 4, e: 5});
let newObj = Object.assign({a: 3}, obj);
console.log(newObj.b.c);
newObj.b.c = 100;
console.log(obj.b.c);
这段代码主要使用了`Object.assign()`方法将多个对象合并为一个新的对象,并且通过引用传递修改了嵌套的对象属性。
首先,第一行代码使用了`Object.assign()`方法将四个对象合并成一个新的对象,并通过变量`obj`进行引用。此时,该对象中的属性包括a、b、c、d与e。其中b、d与e属性是最后一个对象{d:4, e:5}中的属性。这样,我们得到了一个新的`obj`对象,它包含了所有这些属性。
接下来的代码创建了一个名为`newObj`的新对象,使用了`Object.assign()`方法。我们首先初始化了一个包含属性a的对象,并将`obj`对象合并到`newObj`中。就像之前一样,此时的`newObj`对象中的属性a被更新为3,而`newObj`中的其余所有属性都与`obj`对象相同。
在后续代码的第一个`console.log()`中,我们可以看到`newObj.b.c`的值为`undefined`。这是因为在合并到`newObj`之前,`obj`对象中并没有属性b或c,添加了一个属性`b:2`之后`obj`对象才有了`b`属性,所以`newObj.b.c`无法访问。
然后我们将`newObj.b.c`的值赋为100,在第二个`console.log()`中,通过`obj.b.c`输出该属性的值。由于在`newObj`与`obj`对象之间共享一个引用,因此当我们修改`newObj.b.c`时,`obj.b.c`的值也因为相同的引用而被修改。
因此,最后一个`console.log()`输出的是100.值被修改了。
const obj = {
a: 1,
b: 2,
c: 3,
d: 4
};
console.log(Object.keys(obj));
console.log(Object.values(obj));
console.log(Object.entries(obj));
for (let [k, v] of Object.entries(obj)) {
console.log(k, v);
}
console.log({a: 1});
这段代码展示了如何使用Object对象的keys、values、entries方法以及for...of循环来遍历一个包含属性的对象,并输出对应的键、值或键值对。同时最后一行代码展示了如何创建一个包含属性的对象。
- Object.keys(obj)方法返回obj对象中所有属性的键组成的数组,输出为 ["a", "b", "c", "d"];
Object.values(obj)方法返回obj对象中所有属性的值组成的数组,输出为 [1, 2, 3, 4];
Object.entries(obj)方法返回一个包含obj对象中所有属性的键值对的数组,每个键值对都以数组形式表示,输出为[["a", 1], ["b", 2], ["c", 3], ["d", 4]];
for...of循环在每次遍历时将键值对数组中的每个元素解构成键和值,依次输出 a 1、b 2、c 3、d 4;
最后一行代码创建一个包含属性a和值1的对象,并输出为 { a: 1 }。
const obj1 = {
a: 1
};
const obj2 = {
b: 1
}
const obj = Object.create(obj1);
console.log(obj.__proto__);
Object.setPrototypeOf(obj, obj2);
console.log(obj.__proto__);
const obj1 = {a: 1};
const obj = Object.create(obj1);
console.log(obj.__proto__);
console.log(Object.getPrototypeOf(obj));
console.log(obj.__proto__ === Object.getPrototypeOf(obj));
这段代码展示了如何使用 Object.create() 方法来创建一个新的对象并设置它的原型链,并演示了如何使用 Object.setPrototypeOf() 方法来更改对象的原型链。
- 第一组代码,对象 obj1 包含属性 a,值为 1。
- 对象 obj2 包含属性 b,值为 1。
- 在第 3 行中,使用 Object.create(obj1) 创建一个新的对象 obj,并将 obj 的 prototype 指向 obj1。这意味着 obj1 现在是 obj 的原型对象。
- 在第 5 行中,使用 Object.setPrototypeOf() 方法将 obj 的原型链更改为 obj2,因此现在 obj1 是 obj2 的原型对象。
- 第 7 行输出 obj 的原型对象(即 obj.__proto__),输出结果为 obj1 对象。
- 第 9 行输出 obj 的原型对象(即 Object.getPrototypeOf(obj)),输出结果为 obj1 对象。
- 第 10 行用比较运算符比较 obj 的原型对象(即 obj.__proto__)和 Object.getPrototypeOf(obj) 获取到的结果是否相等,输出结果为 true。
总的来说,这段代码突出了 Object.create() 和 Object.setPrototypeOf() 方法的作用,并演示了如何使用对象的原型链。同时,它阐明了__proto__和 Object.getPrototypeOf() 方法在 JavaScript 中的作用和用法。
const obj = {name: 'xiaoming'};
const cObj = {
say() {
console.log(`My name is ${super.name}`);
}
}
Object.setPrototypeOf(cObj, obj);
cObj.say();
这段代码展示了如何使用 super 关键字来访问一个对象的原型属性。
- 第一行代码创建了一个包含名为“name”的属性的对象 obj,值为“xiaoming”。
- 第三到六行代码创建一个对象 cObj,该对象含有一个名为 say 的方法,该方法使用 super.name 访问 obj 对象的原型属性并将其输出。
- 第八行使用 Object.setPrototypeOf() 方法将 cObj 的原型对象设置为 obj。
- 第九行调用方法 cObj.say(),输出 My name is xiaoming。
总的来说,这段代码演示了如何使用 super 关键字来访问对象的原型属性,并且在创建时通过 Object.setPrototypeOf() 方法来指定对象的原型。这个例子非常简单,但 super 关键字用于访问对象的原型属性在更复杂的代码中将非常有用。
function foo(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}
foo(...[1, 3, 2]);
这段代码展示了如何使用 JavaScript 中的扩展运算符(spread operator)来将数组中的元素作为参数传递给函数。
- 第一行定义了一个函数 foo,该函数具有三个参数 a、b 和 c。
- 在第三行中,一个数组 [1, 3, 2] 被创建,并且借助于扩展运算符(...),该数组被拆分成 1,3 和 2 三个元素,并作为参数传递给函数 foo。
- 当函数 foo 被调用时,数值 1,3,2 会分别被传递给函数 foo 的参数 a、b 和 c。
- 第四到六行为函数 foo 内的代码,分别输出参数 a、b 和 c 的值。
- 因此,最终会输出以下内容:
```
1
3
2
```
总的来说,这段代码演示了如何使用 JavaScript 中的扩展运算符来简单快速地将数组中的元素作为参数传递给函数。这在编写具有很多参数的函数时非常方便。
使用扩展运算符...可以有助于将数组拆分成单个数字传递给函数(数组的解构)
const user = ['小明', 14, ['吃饭', '打游戏'], '我没有女朋友'];
function say(name, age, hobby, desc) {
console.log(`我叫${ name }, 我今年${ age } 岁, 我喜欢${ hobby.join('和') }, ${ desc }`);
}
say(user[0], user[1], user[2], user[3]);
say(...user);
在第一行中定义了一个数组 user,包含字符串、数字和数组这些数据类型的元素。
在第三行中定义了一个名为 say 的函数,该函数可以接受四个参数:name、age、hobby 和 desc。
在第四行中,调用 say 函数,将 user 数组中的四个元素作为参数传递给该函数。注意,在这里需要用数组下标来获取每个元素,即 user[0]、user[1]、user[2] 和 user[3]
第六行使用扩展运算符(...)将数组 user 中的所有元素展开成为四个独立的参数,传递给 say 函数。
在函数内部,hobby 数组是用 join() 方法来连接其中的2个元素,因为该数组是 user 中的一个元素;其余的参数都可以直接使用。
say.apply(null, user);
const arr = [1, 2, 233, 3, 4, 5];
这段代码演示了使用 apply() 方法将函数参数数组展开为函数的多个单独的参数。
在第一行中,使用 apply() 方法将函数 say 应用到一个对象上,由于这里不需要传递一个特定的对象,该参数可以设置为 null。而第二个参数 user 是一个数组,其中的每个元素将被展开为单独的参数传递给 say 函数。
- 在第四行中,定义了一个包含几个数字的数组 arr。
- 定义数组的方法很简单,只要在方括号中使用逗号分隔不同的元素就可以。JavaScript 允许在数组中包含任何数据类型的元素,包括数字、字符串、布尔值、对象、函数等等。
总的来说,这段代码展示了 apply() 方法在 JavaScript 中的使用,能够将数组作为参数展开到函数中;同时还演示了如何定义一个包含数字的数组并且说明数组中的元素可以包含不同类型的数据类型
say.apply(null, user);
const arr = [1, 2, 233, 3, 4, 5];
console.log(Math.max(...arr));
console.log(Math.max.apply(null, arr));
console.log(Math.min(...arr));
console.log(Math.min.apply(null, arr));
- 在第六行中,使用扩展运算符将数组 arr 中的所有元素展开为 Math.max() 方法的参数,即 Math.max(...arr)。该方法返回 arr 中的最大值,输出结果为 233。
- 在第七行中,使用 apply() 方法将数组 arr 作为参数传递给 Math.max() 方法,即 Math.max.apply(null, arr)。该方法也返回 arr 中的最大值,输出结果同样为 233。
- 在第九行和第十行中,执行与前面类似的操作,但是查找的是数组 arr 中的最小值。前者使用扩展运算符 Math.min(...arr),输出结果为 1;而后者使用 apply() 方法 Math.min.apply(null, arr),输出结果同样为 1。
function *g() {
console.log(1);
yield 'hi~';
console.log(2);
yield 'imooc~';
}
const arr = [...g()];
const gg = g();
gg.next();
setTimeout(function() {
gg.next();
}, 1000);
这段代码展示了如何使用 ES6 中的生成器函数(generator functions)来创建迭代器,以及如何逐个访问它们的值。
- 在第一到五行代码中,定义了一个生成器函数 g,它用 console.log() 语句输出数字 1 和 2,并结合 yield 返回字符串 "hi~" 和 "imooc~",表示执行到哪个位置,并等待使用迭代器的 next() 方法来获取下一个值。
- 在第七行代码中,使用扩展运算符(...)将生成器函数 g 返回的迭代器展开为一个数组 arr,此时该数组中包含了两个字符串:"hi~" 和 "imooc~"。
- 在第九行中,创建了一个迭代器 gg,并使用其 next() 方法来获取第一个值,即执行生成器函数 g 中的第一个 console.log() 语句,输出数字 1,然后等待获取下一个值。
- 在第十二行中,setTimeout() 模拟了一个异步操作,设置 1 秒的延迟,等待 1 秒后再使用 gg.next() 来获取生成器函数 g 的下一个值,即输出数字 2,此时该生成器函数已经执行完毕。
总的来说,这段代码展示了如何使用生成器函数来创建可迭代的对象,以及如何使用 next() 方法从该迭代器对象中按需获取值。在这个例子中,setTimeout() 模拟了异步情况,说明生成器函数可以在异步编程中使用,利用 yield 等待异步操作执行完毕后再返回结果。
let set = new Set([1, 2, 2, 3]);
console.log([...set]);
这段代码演示了如何使用 ES6 中的 Set 数据结构去除一个数组中的重复元素。
- 第一行创建了一个 Set 对象 set,并传入一个数组 [1, 2, 2, 3] 作为初始化参数。
- 在创建 Set 对象时,重复元素会被自动去除,即 Set 对象只会保存
const obj = {
0: 1,
1: '22',
2: false,
length: 2
};
console.log(Array.from(obj, item => item * 2));
Array.prototype.slice.call();
[].slice.call();
[...]
1. `console.log(Array.from(obj, item => item * 2));`
此代码将一个类数组对象`obj`转换为一个新数组,并对新数组中的每个元素都进行了乘2的操作。其中`Array.from`方法的第一个参数为源对象,第二个参数为对每个元素的处理函数。
2. `Array.prototype.slice.call()`
此代码是利用`call()`方法来调用`Array.prototype.slice`方法对一个类似数组的对象进行截取操作,返回一个新的数组。`slice`方法用于从源数组中选取一部分元素组成新数组,返回一个新数组。
3. `[].slice.call()`
这行代码和上面一行作用相同,也是通过`call()`方法来将源对象转换为数组进行截取。
4. `[...xxx]`
这行代码是将可迭代对象(比如数组、字符串等)转换成一个新数组的快捷方式。`[...]`中的省略号即为展开运算符。对于类数组对象,可以先用`Array.prototype.slice`方法或者`[].slice.call`方法将其转换为数组,再使用展开运算符来生成新数组。
console.log(Array.of(1, 2, '123', false));
这行代码使用了`Array.of()`方法创建一个数组,并将参数中的所有值作为数组的元素。它的用法和`Array`构造函数类似,但是处理一些特殊的情况时有不同的行为。
它的语法是`Array.of(element0[, element1[, ...[, elementN]]])`,其中`element0...elementN`是要填充到数组中的元素。它会根据这些参数返回一个新的数组,这个数组会直接以参数中的元素形式创建出来。
在这段代码中,`Array.of`传入`1, 2, '123', false`作为参数,这些参数就成为了数组的元素,返回的结果为`[1, 2, '123', false]`,也就是一个包含了所有传入参数作为元素的数组。需要注意的是,在这个数组中,所有元素的类型都可以不同。
let arr = new Array(10).fill(0, 0, 3);
console.log([1, 2, 3].fill(0));
1.此代码创建一个长度为10的数组,并调用`fill()`方法将数组索引从0开始到2结束的位置全部填充为0。这里的`fill()`方法接收3个参数,第一个参数是要填充的值,第二个和第三个参数分别是起始和截止位置。
2.
此代码创建一个长度为3的数组,里面包含3个由数字1, 2, 3组成的元素。并调用`fill()`方法将数组中的所有元素全部填充为0.这里的`fill()`方法只传了一个参数,即要填充的值,它会将整个数组填充为这个值。
需要注意的是,对于`fill()`方法来说,它是直接修改原数组,而不是创建一个新数组。对原数组的修改是永久生效的,因此需要注意控制修改的范围和内容。同时,在这段代码中,可以发现`Array`对象也有一种表示法,直接用一组中括号括起来,其中通过逗号分隔每个元素,与使用`new Array()`构造函数得到的效果是相同的。但需要注意的是,如果只需带一个数字参数,则表示数组的长度,而不是数组的元素。
var arr = [1, 2, 3, 4];
console.log(arr.includes(1));
console.log(arr.includes(55));
这段代码使用了数组的`includes()`方法,对给定的数组进行查询,返回一个布尔值。
1. `var arr = [1, 2, 3, 4];`
此代码创建一个数组,包含了4个数字元素。这里使用了一种直接在代码中赋值的方式创建数组。
2. `console.log(arr.includes(1));`
`includes()`方法接收一个参数,表示要查询的元素。此行代码查询数组`arr`中是否包含数字1,返回的结果是`true`。因为数组`arr`中的第一个元素即为数字1。
3. `console.log(arr.includes(55));`
此行代码查询数组`arr`中是否包含数字55,返回的结果是`false`。因为数组`arr`中并没有元素55。
总的来说,`includes()`方法可以用来判断一个数组中是否包含某一元素。如果包含,则返回`true`;否则返回`false`。需要注意的是,`includes()`方法是从头到尾线性查找数组,时间复杂度为O(n),因此对于大数组要谨慎使用。同时,`includes()`方法也接收一个可选的第二个参数,表示要从数组的哪个位置开始查找。
const arr = [1, 2, 3, 444];
console.log(arr.keys());
for (let i of arr.keys()) {
console.log(i);
}
2. `console.log(arr.keys());`
`keys()`方法会返回当前数组的索引的迭代器对象。这里的`console.log()`语句会直接输出这个迭代器对象。需要注意的是,迭代器对象并不是数组本身,它是一种可以用`for...of`语句进行遍历的对象。
for (let i of arr.keys()) {console.log(i);}`
这行代码使用了`for...of`语句来遍历数组中所有的索引。在这个循环体中,将迭代器对象传递给`of`运算符,然后它会迭代这个对象,并把每个索引值一个个地传递给`i`变量。在循环体内部,将当前的索引值输出到控制台中。
需要注意的是,`keys()`方法返回的迭代器对象包括了所有的索引,而不仅仅是数组中的元素,这意味着在这个迭代器对象上可以调用`next()`方法,获取迭代器的下一个引用位置。
什么是迭代器?
迭代器是一种对象,它可以依次访问一个容器对象中的各个元素,而不需要了解容器对象的内部结构。在JavaScript中,迭代器通常是由实现了`next()`方法的对象所表示,从而可以自定义迭代器的逻辑。
迭代器通常具有以下两个特点:
1. 它是一种对象,拥有自己的状态信息,能够保存当前迭代位置。
2. 它实现了`next()`方法,每次调用该方法能够返回容器对象中的下一个元素。
在实现自己的迭代器时,需要遵循迭代器协议规范。在JavaScript中,迭代器对象必须包含一个名为Symbol.iterator的属性,这个属性返回一个对象,该对象必须实现`next()`方法,每次调用该方法都会返回一个包含`value`属性和`done`属性的对象,`value`属性表示当前元素的值,`done`属性表示是否已经到达迭代器的结尾。
for (let v of arr.values()) {
console.log(v);
}
这段代码遍历了数组`arr`中所有的值,并将每个值依次输出到控制台中。这里用到了ES6中的`for...of`语句和`Array.prototype.values()`方法。
1. `for (let v of arr.values()) { console.log(v); }`
这里使用`for...of`语句来遍历数组中所有的值。在循环体中,将`arr.values()`作为迭代器传递给`of`运算符,它会迭代这个对象,并将每个数组值一个个地传递给`v`变量。然后在循环体内部,将`v`变量的值输出到控制台中。
2. `Array.prototype.values()`
这个方法返回一个新的数组迭代器对象,其中包含数组中所有的元素值,依次按索引顺序排序。
需要注意的是,和`keys()`和`entries()`方法相比,`values()`方法是默认的迭代器,因此在使用迭代器遍历数组时,可以直接使用数组对象的默认迭代器。
这个循环和使用`for...in`循环是不同的,后者遍历的是所有的可枚举属性,包括索引和原型链上的属性。而前者只遍历数组自有的元素值,不包括它的原型链属性。因此,在遍历数组时,使用`for...of`语句会更加安全和可靠。
for (let [i, v] of arr.entries()) {
console.log(i, v);
}
这段代码使用了数组的`entries()`方法来获取数组迭代器,以及`for...of`语句对这个迭代器进行遍历输出。
1. `for (let [i, v] of arr.entries()) { console.log(i, v); }`
这里使用`for...of`语句遍历了`arr.entries()`提供的迭代器对象。在循环体内,用`[i, v]`解构赋值来分别取到当前元素的索引和对应的值,并将它们用`console.log()`函数打印到控制台中。
2. `Array.prototype.entries()`
该方法返回一个新的数组迭代器,其中包含数组中的所有元素,每个元素分别由其索引和值组成,索引与值以数组形式返回。
需要注意的是,`entries()`方法返回的是一个包含数组索引和元素值的迭代器,可以通过`for...of`循环来遍历。在`for...of`循环语句中使用解构变量对迭代器中的索引和元素值进行赋值,然后在循环体中将索引和元素值分别输出。
const res = [1, 7, 6, 3].find((value, index, arr) => value % 2 === 0);
console.log(res);
find 根据条件(回调) 按顺序遍历数组 当回调返回true时 就返回当前遍历到的值
const res = [1, 7, 6, 3, NaN].findIndex((value, index, arr) => Number.isNaN(value));
console.log(res);
findIndex 根据条件(回调) 按顺序遍历数组 当回调返回true时 就返回当前遍历到的下标