
let 是在代码块内有效,var 是在全局范围内有效:
{
let a = 0;
a // 0
}
a // 报错 ReferenceError: a is not defined
let 只能声明一次 var 可以声明多次:
let a = 1;
let a = 2;
var b = 3;
var b = 4;
a // Identifier 'a' has already been declared
b // 4
变量 i 是用 var 声明的,在全局范围内有效,所以全局中只有一个变量 i, 每次循环时,setTimeout 定时器里面的 i 指的是全局变量 i ,而循环里的十个 setTimeout 是在循环结束后才执行,所以此时的 i 都是 10。
变量 j 是用 let 声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 12345。(若每次循环的变量 j 都是重新声明的,如何知道前一个循环的值?这是因为 JavaScript 引擎内部会记住前一个循环的值)。
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i);
})
}
// 输出十个 10
for (let j = 0; j < 10; j++) {
setTimeout(function(){
console.log(j);
})
}
// 输出 0123456789
let 不存在变量提升,var 会变量提升:
变量 b 用 var 声明存在变量提升,所以当脚本开始运行的时候,b 已经存在了,但是还没有赋值,所以会输出 undefined。
变量 a 用 let 声明不存在变量提升,在声明变量 a 之前,a 不存在,所以会报错。
console.log(a); //ReferenceError: a is not defined
let a = "apple";
console.log(b); //undefined
var b = "banana";
const 声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。
const PI = "3.1415926";
PI // 3.1415926
const MY_AGE; // SyntaxError: Missing initializer in const declaration
ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。
var PI = "a";
if(true){
console.log(PI); // ReferenceError: PI is not defined
const PI = "3.1415926";
}
const 如何做到变量在声明初始化之后不允许改变的?其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。此时,你可能已经想到,简单类型和复合类型保存值的方式是不同的。是的,对于简单类型(数值 number、字符串 string 、布尔值 boolean),值就保存在变量指向的那个内存地址,因此 const 声明的简单类型变量等同于常量。而复杂类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。
解构的源,解构赋值表达式的右边部分。
解构的目标,解构赋值表达式的左边部分
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
let [a, , b] = [1, 2, 3];
// a = 1
// b = 3
let [a = 1, b] = []; // a = 1, b = undefined
let [a, ...b] = [1, 2, 3];
//a = 1
//b = [2, 3]
在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据。
let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'
let [a = 2] = [undefined]; // a = 2
当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。
let [a = 3, b = a] = []; // a = 3, b = 3
let [a = 3, b = a] = [1]; // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
以上三个方法都可以接受两个参数,需要搜索的字符串,和可选的搜索起始位置索引。
let string = "apple,banana,orange";
string.includes("banana"); // true
string.startsWith("apple"); // true
string.endsWith("apple"); // false
string.startsWith("banana",6) // true
注意点:这三个方法只返回布尔值,如果需要知道子串的位置,还是得用 indexOf 和 lastIndexOf 。
这三个方法如果传入了正则表达式而不是字符串,会抛出错误。而 indexOf 和 lastIndexOf 这两个方法,它们会将正则表达式转换为字符串并搜索它。
console.log("Hello,".repeat(2)); // "Hello,Hello,"
console.log("Hello,".repeat(3.2)); // "Hello,Hello,Hello,"
console.log("Hello,".repeat(-0.5)); // ""
console.log("Hello,".repeat(NaN)); // ""
console.log("Hello,".repeat(-1));
// RangeError: Invalid count value
console.log("Hello,".repeat(Infinity));
// RangeError: Invalid count value
console.log("Hello,".repeat("hh")); // ""
console.log("Hello,".repeat("2")); // "Hello,Hello,"
以上两个方法接受两个参数,第一个参数是指定生成的字符串的最小长度,第二个参数是用来补全的字符串。如果没有指定第二个参数,默认用空格填充。
console.log("h".padStart(5,"o")); // "ooooh"
console.log("h".padEnd(5,"o")); // "hoooo"
console.log("h".padStart(5)); // " h"
遍历数组,对每个元素执行回调函数
const numbers = [1, 2, 3, 4];
numbers.forEach((item, index, array) => {
console.log(`索引 ${index}: ${item}`);
});
// 输出:
// 索引 0: 1
// 索引 1: 2
// 索引 2: 3
// 索引 3: 4
创建新数组,包含原数组每个元素调用函数后的结果
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6]
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
];
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob']
创建新数组,包含所有通过测试的元素
const numbers = [1, 2, 3, 4, 5, 6];
const even = numbers.filter(num => num % 2 === 0);
console.log(even); // [2, 4, 6]
const products = [
{ name: '手机', inStock: true },
{ name: '电脑', inStock: false },
{ name: '平板', inStock: true }
];
const availableProducts = products.filter(product => product.inStock);
console.log(availableProducts); // [{name: '手机', inStock: true}, {name: '平板', inStock: true}]
查找第一个满足条件的元素/索引
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const user = users.find(user => user.id === 2);
console.log(user); // {id: 2, name: 'Bob'}
const index = users.findIndex(user => user.name === 'Charlie');
console.log(index); // 2
检查数组中是否至少有一个/所有元素满足条件
const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true
将数组缩减为单个值(从左到右/从右到左)
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15
// 分组
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 }
];
const groupedByAge = people.reduce((groups, person) => {
const age = person.age;
if (!groups[age]) {
groups[age] = [];
}
groups[age].push(person);
return groups;
}, {});
console.log(groupedByAge);
// {25: [{name: 'Alice', age: 25}, {name: 'Charlie', age: 25}], 30: [{name: 'Bob', age: 30}]}
扁平化数组
// flat()
const nested = [1, 2, [3, 4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]
// flatMap()
const sentences = ["Hello world", "Good morning"];
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ["Hello", "world", "Good", "morning"]
检查数组是否包含某个元素
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.includes('banana')); // true
console.log(fruits.includes('grape')); // false
从类数组或可迭代对象创建新数组
// 从字符串
const fromString = Array.from('hello');
console.log(fromString); // ['h', 'e', 'l', 'l', 'o']
// 从Set
const fromSet = Array.from(new Set([1, 2, 2, 3, 3]));
console.log(fromSet); // [1, 2, 3]
// 带映射函数
const squares = Array.from([1, 2, 3], x => x * x);
console.log(squares); // [1, 4, 9]
根据参数创建新数组
const numbers = Array.of(1, 2, 3, 4);
console.log(numbers); // [1, 2, 3, 4]
// 与 Array 构造函数的区别
console.log(Array(3)); // [empty × 3]
console.log(Array.of(3)); // [3]
用固定值填充数组
const arr = new Array(5).fill(0);
console.log(arr); // [0, 0, 0, 0, 0]
const numbers = [1, 2, 3, 4, 5];
numbers.fill('*', 1, 3);
console.log(numbers); // [1, '*', '*', 4, 5]
在数组内部复制元素序列
const numbers = [1, 2, 3, 4, 5];
numbers.copyWithin(0, 3);
console.log(numbers); // [4, 5, 3, 4, 5]
返回数组的迭代器对象
const fruits = ['apple', 'banana', 'orange'];
// entries() - 键值对
for (const [index, fruit] of fruits.entries()) {
console.log(index, fruit);
}
// keys() - 键
for (const key of fruits.keys()) {
console.log(key); // 0, 1, 2
}
// values() - 值
for (const value of fruits.values()) {
console.log(value); // 'apple', 'banana', 'orange'
}
// 数据处理管道
const data = [
{ name: 'Alice', age: 25, salary: 50000, department: 'IT' },
{ name: 'Bob', age: 30, salary: 60000, department: 'HR' },
{ name: 'Charlie', age: 35, salary: 70000, department: 'IT' },
{ name: 'David', age: 28, salary: 55000, department: 'Finance' }
];
// 计算IT部门员工的平均薪资
const itAvgSalary = data
.filter(employee => employee.department === 'IT')
.map(employee => employee.salary)
.reduce((sum, salary, index, array) => {
sum += salary;
if (index === array.length - 1) {
return sum / array.length;
}
return sum;
}, 0);
console.log(`IT部门平均薪资: ${itAvgSalary}`); // IT部门平均薪资: 60000
在 ES6 中,有以下数组方法会直接影响原数组:
copyWithin()const arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3); // 将索引3开始的元素复制到索引0
console.log(arr); // [4, 5, 3, 4, 5]
fill()const arr = [1, 2, 3, 4, 5];
arr.fill(0, 1, 3); // 将索引1-3的元素填充为0
console.log(arr); // [1, 0, 0, 4, 5]
push() / pop()const arr = [1, 2, 3];
arr.push(4); // [1, 2, 3, 4]
arr.pop(); // [1, 2, 3]
shift() / unshift()const arr = [1, 2, 3];
arr.shift(); // [2, 3]
arr.unshift(0); // [0, 2, 3]
splice()const arr = [1, 2, 3, 4, 5];
arr.splice(1, 2, 'a', 'b'); // 从索引1删除2个元素,并插入新元素
console.log(arr); // [1, 'a', 'b', 4, 5]
reverse()const arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1]
sort()const arr = [3, 1, 2];
arr.sort(); // [1, 2, 3]
以下方法会返回新数组,不会改变原数组:
map()filter()slice()concat()flat()flatMap()const arr = [1, 2, 3];
const newArr = arr.map(x => x * 2);
console.log(arr); // [1, 2, 3] - 原数组不变
console.log(newArr); // [2, 4, 6] - 新数组
总结:在 ES6 新增方法中,只有 copyWithin() 和 fill() 会直接修改原数组。
生成中...
感谢您的支持与鼓励
您的支持是我们前进的动力

期待您的精彩留言!
成为第一个评论的人吧 ✨