let
和 const
ES6 引入了 let
和 const
,有效解决了 var
定义变量时的变量提升和作用域问题。这两种方式严格按照上下文执行,增强了代码的可读性和安全性。
1. let
—— 可更改的变量
let
用于定义变量,其值可以在后续代码中被更改,同时具有块级作用域。
let a = 6;
a = 7; // 合法,可以重新赋值
特性:
- 块级作用域:变量的作用域限制在所在的代码块
{}
内。 - 不会变量提升:
let
声明的变量必须在声明后使用,否则会报错。 - 不允许重复声明:同一个作用域内,不能使用
let
声明同名变量。
{
let a = 10;
console.log(a); // 输出 10
}
console.log(a); // 报错:a is not defined
2. const
—— 不可更改的常量
const
用于定义常量,其值在声明后不可更改。不过,const
定义的对象或数组,其属性或元素是可以被更改的。
const a = 6;
以下是替换为更易理解示例的内容:
字符串与正则表达式
1. Unicode 的码元与码点
背景
在计算机中,为了表示和显示文字,早期引入了 Unicode 编码,其中每个字符由 16 位(即 2 字节)组成,这 16 位的数字称为 码元(Code Unit)。
然而,随着字符集的扩展,16 位的范围已无法容纳所有字符,于是 Unicode 扩展到了 32 位。某些字符需要两个码元表示,这时引入了 码点(Code Point)的概念,表示字符的真实值。
ES6 的改进
在计算字符串长度时,传统方法是基于码元的数量计算。当字符由两个码元组成时,会导致长度计算不准确。为了解决这个问题,ES6 提供了以下两个方法:
str.charCodeAt(index); // 返回字符串中指定位置字符的码元值(16位)
str.codePointAt(index); // 返回字符串中指定位置字符的码点值(32位)
示例
const s = '🎉a'; // "🎉" 是一个 32 位字符
console.log(s.length); // 输出 3(两个码元表示 🎉,一个码元表示 a)
console.log(s.charCodeAt(0)); // 输出 55356(🎉 的高位码元)
console.log(s.codePointAt(0)); // 输出 127881(🎉 的真实码点)
2. 正则表达式的 y
粘连标记
特性
y
表示 粘连匹配,即匹配必须从正则对象的 lastIndex
位置开始,并且匹配成功后更新 lastIndex
。如果当前位置无法匹配,匹配失败。
示例
const str = '123-456-789';
const reg = /\d{3}/y;
reg.lastIndex = 0; // 设置起始位置
console.log(reg.test(str)); // 输出 true(从第 0 位匹配 "123")
console.log(reg.lastIndex); // 输出 3(匹配成功后,lastIndex 更新为 3)
reg.lastIndex = 4; // 起始位置错误(不在 "-456" 的开头)
console.log(reg.test(str)); // 输出 false
对比 g
标记
g
:从lastIndex
开始查找,但允许跳过未匹配的部分。y
:从lastIndex
开始,要求匹配必须从此处开始,否则匹配失败。
3. 模板字符串与标记函数
模板字符串允许在字符串中嵌入变量,并支持使用 标记函数(Tag Function)自定义模板的处理逻辑。
标记函数的参数
- 第一个参数:模板字符串分割后的字符串数组(不包括插值)。
- 后续参数:所有插值的值,按照出现顺序依次传入。
示例
const name = '小明';
const age = 18;
const hobby = '打篮球';
const result = ff`你好,我是${
name},今年${
age}岁,爱好是${
hobby}。`;
function ff(strings, ...values) {
let result = strings[0];
for (let i = 0; i < values.length; i++) {
result += `[${
values[i]}]${
strings[i + 1]}`;
}
return result;
}
console.log(result);
// 输出:你好,我是[小明],今年[18]岁,爱好是[打篮球]。
标记函数的用途
- 模板字符串格式化:自定义插值的表现形式,例如加上特定符号或前缀。
- 转义字符:防止插值内容中的字符被误解析(如 HTML 转义)。
- 国际化支持:根据不同的语言环境动态生成内容。
以下是优化和补充后的内容:
函数的变化
1. new.target
ES6 引入了一个特殊的 API:new.target
,用于判断一个函数是否通过 new
调用。
特性
- 如果没有使用
new
调用函数,则new.target
为undefined
。 - 如果使用
new
调用函数,则new.target
返回调用时紧随new
的函数本身。
示例
function Example() {
if (!new.target) {
throw new Error('必须使用 new 调用该函数!');
}
console.log('使用了 new 调用');
}
new Example(); // 输出:使用了 new 调用
Example(); // 抛出错误:必须使用 new 调用该函数!
应用场景
- 构造函数保护:确保某些函数只能通过
new
调用。 - 继承构造函数的判断:在类的构造函数中使用,判断当前类是否被直接调用或继承调用。
箭头函数
1. this
的回顾
在传统函数中,this
的指向是动态的,取决于调用方式:
-
对象调用:通过对象调用函数,
this
指向对象本身。const obj = { name: 'Alice', greet() { console.log(this.name); } }; obj.greet(); // 输出:Alice
-
普通函数调用:
this
指向全局对象(严格模式下为undefined
)。function greet() { console.log(this); } greet(); // 浏览器中输出:window(严格模式下为 undefined)
-
new
调用:this
指向新创建的对象。function Person(name) { this.name = name; } const person = new Person('Bob'); console.log(person.name); // 输出:Bob
-
call
/apply
/bind
调用:this
被显式绑定到指定的值。function greet() { console.log(this.name); } const obj = { name: 'Charlie' }; greet.call(obj); // 输出:Charlie
-
DOM 事件函数:
this
指向触发事件的 DOM 元素(事件源)。const button = document.querySelector('button'); button.addEventListener('click', function () { console.log(this); // 输出触发事件的按钮元素 });
2. 箭头函数的语法
箭头函数是 ES6 提供的一种简洁写法,主要用于表达函数。
语法形式
-
完整语法:
(参数1, 参数2, ...) => { // 函数体 };
-
单个参数时省略小括号:
参数 => { // 函数体 };
-
单行返回值时省略大括号和
return
:参数 => 返回值;
示例
const square = x => x * x;
console.log(square(4)); // 输出:16
const add = (a, b) => a + b;
console.log(add(2, 3)); // 输出:5
3. 注意事项
-
没有自己的
this
箭头函数内部的this
是静态的,继承自定义它的外层作用域。const obj = { name: 'Dave', greet: () => { console.log(this.name); // this 指向全局对象,非 obj } }; obj.<