ES6相关芝士点

ES6相关芝士点

image-20250217160942325

1、let、const、var的区别

作用域

var 声明的变量具有函数作用域或全局作用域。

函数作用域:如果在函数内使用 var 声明变量,则该变量只在这个函数内有效。

let 和 const 声明的变量具有块级作用域。

块级作用域:由大括号 {} 包含的区域。

变量提升

变量提升:将变量是或者函数声明提升到所在作用域顶部的行为。

var 声明的变量会被提升到函数作用域或全局作用域顶部,但只提升声明,不提升赋值。

1
2
console.log(a); // 输出: undefined
var a = 10;

let 和 const 声明的变量也会被提升到块级作用域的顶部,但处于暂时性死区状态!不会被初始化,因此这时候访问它会抛出ReferenceError 错误!

暂时性死区:变量处于不可访问状态。

全局属性

在全局作用域中用 var 声明的变量,会自动成为全局对象的一个属性。
容易与其他全局属性冲突,增加全局命名空间的负担。

在全局作用域中用 let 和 const 声明的变量,不会附加到全局对象上。

重复声明

在同一作用域内,使用 var 声明的变量,可以重复声明,后面会覆盖前面的值。

使用 let 和 const 声明的变量,如果在同一块级作用域内,重复声明会导致语法错误。如果在不同的块级作用域内,可以重复声明相同的变量名,不会冲突,因为作用域不同。

初始化

const 声明的变量必须在声明的同时初始化,否则会报语法错误。而且一旦声明和初始化后,变量的引用不能再被改变。如果引用的是一个对象或者数组,内部的属性或者元素仍然可以修改,但是变量本身不能重新被赋值。

var 和 let:可以不立即初始化,声明后默认值为 undefined,并且后续可以赋值。

image-20250216124540443

2、new操作符的原理?可以new箭头函数吗

1、创建一个空对象

2、设置原型:将新对象的内部[[Prototype]](也就是__proto__)指向构造函数的prototype属性。

3、执行构造函数:将新对象作为 this 上下文,调用构造函数,并将参数传递给构造函数。

4、返回值处理:如果构造函数显式返回的对象,直接返回。否则,默认返回新对象。

不能new箭头函数的原因

箭头函数没有自己的 this,它们继承自外部作用域,因此不具备构造函数所需要的特性。

箭头函数没有 prototype 属性,构造函数需要这个属性来构建实例的原型链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person() {
this.age = 0;

// 定时器中使用箭头函数
setInterval(() => {
// 箭头函数没有自己的 this,
// 这里的 this 会继承自 Person 构造函数中的 this,
// 所以 this.age 指向的是当前 Person 实例的 age 属性
this.age++;
console.log(this.age);
}, 1000);
}

const p = new Person();

3、箭头函数与普通函数的区别

this的绑定

  • 箭头函数

    箭头函数没有自己的this,是词法绑定,即继承它定义时候的所在上下文(词法环境)的this值。

    无论怎么调用,箭头函数的this是固定的,固定的,不会再变。

    1
    2
    3
    4
    5
    6
    7
    const obj = {
    value: 42,
    arrow: () => {
    console.log(this); // this 指向外部环境(例如全局对象或父函数的 this),而不是 obj
    }
    };
    obj.arrow(); // 可能输出 window(在浏览器中)或 undefined(在严格模式下)
  • 普通函数

    普通函数的this是动态绑定,取决于它的调用方式。

    对象的方法中调用,this指向对象;

    函数通过new关键字调用,this绑定到新创建的实例对象;

    直接调用,指向全局对象(非严格)或者undefiend(严格);

    通过call、apply、bind调用,显式指定调用时的this值。

    1
    2
    3
    4
    5
    6
    7
    const obj = {
    value: 42,
    method: function() {
    console.log(this); // this 指向 obj
    }
    };
    obj.method(); // 输出 obj

this是什么

在JavaScript中,this是一个非常重要的关键字,它在函数被调用时被赋予一个值,用来指向函数执行的上下文环境(即“谁”在调用这个函数)。

它的值通常是一个对象,但具体指向哪个对象取决于函数的调用方式。

  • 作为方法调用this指向调用它的对象。
  • 作为普通函数调用this指向全局对象(非严格模式)或undefined(严格模式)。
  • 作为构造函数调用this指向新创建的对象实例。
  • 通过callapplybind调用this指向显式指定的对象。
  • 箭头函数中this捕获定义时所在上下文的this值。

arguments对象

  • 箭头函数

    没有自己的 arguments 对象。

    箭头函数内部的arguments会从其定义时所在的词法作用域继承而来。

    1
    2
    3
    4
    5
    6
    7
    function outer() {
    const arrow = () => {
    console.log(arguments); // 引用的是 outer 函数的 arguments
    };
    arrow();
    }
    outer(1, 2, 3); // 输出 [1, 2, 3]
  • 普通函数

    有自己的 arguments 对象,包含了传入函数的所有参数。

    1
    2
    3
    4
    function normal() {
    console.log(arguments); // 输出传入的参数对象
    }
    normal(1, 2, 3); // 输出 [1, 2, 3]

用作构造函数

  • 箭头函数

    不能用作构造函数,无法使用 new 操作符调用,因为它们没有内部的 [[Construct]] 方法,也没有 prototype 属性。

    1
    2
    const Arrow = () => {};
    // new Arrow(); // TypeError: Arrow is not a constructor
  • 普通函数

    可以用作构造函数,通过 new 调用后,会创建一个新对象,并将其绑定到函数内部的 this 上。

    1
    2
    3
    4
    5
    function Person(name) {
    this.name = name;
    }
    const p = new Person("Alice");
    console.log(p.name); // "Alice"

prototype属性

  • 箭头函数

    不存在 prototype 属性,因为它们不能作为构造函数。

  • 普通函数

    拥有 prototype 属性,当函数被用作构造函数时,该属性会成为新创建对象的原型。

4、扩展运算符的作用及使用场景

ES6引入的语法,…主要是把可迭代对象(数组、字符串、Set、Map等)或者对象展开为单独的元素或者属性。

数组合并、数组复制

1
2
3
4
5
6
7
8
9
10
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 合并数组
const merged = [...arr1, ...arr2];
console.log(merged); // [1, 2, 3, 4, 5, 6]

// 复制数组(浅拷贝)
const copy = [...arr1];
console.log(copy); // [1, 2, 3]

浅拷贝的含义

浅拷贝仅复制数组的第一层,引用类型元素与原数组共享内存。

修改基本类型(不影响原数组),修改引用类型的内容(影响原数组)

替换整个元素(改变引用)不会影响原数组。

函数参数传递

参数存储在数组中时,可以使用扩展运算符将数组转换为一个参数列表。

1
2
3
4
5
6
function sum(a, b, c) {
return a + b + c;
}

const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 输出 6

和rest运算符的区别

rest 运算符(使用三个点 ...)用于将多个数据“收集”成一个数组或对象,主要应用在函数参数和解构赋值中。它与扩展运算符(spread operator)共享相同的语法,但语义相反:

1
2
3
4
5
6
function sum(...numbers) {
// `numbers` 是一个数组,包含了所有传入的参数
return numbers.reduce((acc, curr) => acc + curr, 0);
}

console.log(sum(1, 2, 3, 4)); // 输出 10

对象合并、对象复制

将多个对象的属性合并到一个新对象中

1
2
3
4
5
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };

const mergedObj = { ...obj1, ...obj2 };
console.log(mergedObj); // { a: 1, b: 2, c: 3, d: 4 }

利用扩展运算符进行浅拷贝,快速复制一个对象

1
2
3
4
5
6
const original = { name: "Alice", age: 25,c:[1,2,3]};
const clone = { ...original };
clone.name = 'an'
clone.c.push(4)
console.log(clone); // { name: "an", age: 25 }
console.log(original); // { name: "Alice", age: 25 }

解构赋值

用于提取剩余的数组元素或对象属性。

1
2
3
4
5
6
7
8
9
// 数组解构
const [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [2, 3, 4]

// 对象解构
const { a, ...others } = { a: 10, b: 20, c: 30 };
console.log(a); // 10
console.log(others); // { b: 20, c: 30 }

5、什么是解构

ES6提供的一种模式,用于从数组或对象提取数据,是一种语法糖。

数组解构

按顺序、默认值、跳过元素、嵌套解构

1
2
3
4
5
const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
1
2
3
4
5
6
7
const arr = [1, 2];
const [x, y, z = 3] = arr; // z 默认值为 3,因为 arr 第三项不存在
console.log(x, y, z); // 1 2 3

const nested = [1, [2, 3]];
const [m, [n, o]] = nested; // 要按照嵌套解构来,否则o是undefined
console.log(m, n, o); // 1 2 3

对象解构

以属性名为匹配条件!换顺序也可以提取!

1
2
3
4
const person = { name: "Alice", age: 25 };
const { name, age } = person;
console.log(name); // "Alice"
console.log(age); // 25

也可以使用别名,默认值

1
2
3
4
const person = { name: "Bob" };
const { name: xxx, age: a = 30 } = person;
console.log(xxx); // "Bob"
console.log(a); // 30 (因为 person 没有 age 属性,所以使用默认值 30)

如果是深度嵌套如何提取

1
2
3
4
5
6
7
8
const school = {
classes: {
stu: {
name: 'Bob',
age: 24,
}
}
}
1
2
3
const { classes: { stu: { name } }} = school

console.log(name) // 'Bob'

应用场景

函数参数使用解构提取数组或者对象的值

1
2
3
4
function display({ name, age }) {
console.log(`Name: ${name}, Age: ${age}`);
}
display({ name: "Charlie", age: 28 });

快速交换变量值(如果是引用类型,交换地址)

1
2
3
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1

处理返回多个值的函数:

1
2
3
4
function getCoordinates() {
return [10, 20];
}
const [x, y] = getCoordinates();

6、ES6中模板字符串语法

使用反引号来定义模版字面量:

1
const str = `Hello, world!`;

支持多行字符串,无需特殊的转义字符。

在模板字符串中,空格、缩进、换行都会被保留(所以可以写HTML代码)

1
2
3
4
const multiLine = `这是第一行
这是第二行
这是第三行`;
console.log(multiLine); //就真的换行了!

通过${}进行字符串插值,可以嵌入任意表达式,在运行时计算并转为字符串:

1
2
3
4
const name = "Alice";
const age = 25;
const greeting = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(greeting);