一、let, var, const 区别
在 JavaScript 中,`let`,`var`和`const`都用于声明变量,但是它们有一些不同:
1. `var`是 ES5 的关键字,`let`和`const`是 ES6 引入的关键字。
2. `var` 变量声明是函数作用域,而 `let` 和 `const` 变量声明是块作用域。因此,可以使用 `let` 或 `const` 代替 `var` 来声明变量,有效地强制在块级作用域中声明变量。
3. `var` 可以重复声明同一个变量,而 `let` 和 `const` 不允许在同一个作用域内重复声明同一个变量。
4. `var` 变量可以被重新赋值,而 `const` 变量不能被重新赋值,但是它们都可以修改其值属性。
5. `let` 和 `const` 都有暂时性死区的概念,也就是在声明前使用变量会导致 `ReferenceError` 错误。
总之,`let` 和 `const` 是更加安全和可控的变量声明方式,推荐在代码中使用它们。`var` 还是存在的一些场景,但通常 `let` 和 `const` 是更好的选择。
二、es6解构赋值
ES6解构赋值是一种从数组或对象中提取值并赋值给变量的语法。可以通过解构赋值将数组或对象的值赋值给变量,也可以将多个变量赋值给数组或对象的属性。
数组解构赋值示例:
let arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(a); // 输出1
console.log(b); // 输出2
console.log(c); // 输出3
对象解构赋值示例:
let obj = {name: 'Tom', age: 18};
let {name, age} = obj;
console.log(name); // 输出'Tom'
console.log(age); // 输出18
可以在解构赋值中使用默认值:
let [a, b = 2] = [1];
console.log(a); // 输出1
console.log(b); // 输出2
也可以嵌套使用解构赋值:
let arr = [1, [2, 3], 4];
let [a, [b, c], d] = arr;
console.log(a); // 输出1
console.log(b); // 输出2
console.log(c); // 输出3
console.log(d); // 输出4
除了数组和对象的解构赋值,还可以使用剩余参数和展开运算符:
// 剩余参数
let arr = [1, 2, 3, 4];
let [a, b, ...rest] = arr;
console.log(a); // 输出1
console.log(b); // 输出2
console.log(rest); // 输出[3, 4]
// 展开运算符
let arr1 = [1, 2];
let arr2 = [3, 4];
let arr3 = [...arr1, ...arr2];
console.log(arr3); // 输出[1, 2, 3, 4]
let obj1 = {name: 'Tom'};
let obj2 = {age: 18};
let obj3 = {...obj1, ...obj2};
console.log(obj3); // 输出{name: 'Tom', age: 18}
三、箭头函数与普通函数区别
箭头函数与普通函数的区别主要有以下几点:
-
箭头函数没有自己的
this
,它的this
指向的是定义时所在的作用域中的this
,而不是运行时所在的作用域中的this
。因此,在箭头函数中不能通过this
来访问到它所在的函数的this
。 -
箭头函数不能用作构造函数,不能使用
new
关键字调用。因为箭头函数没有自己的this
,也没有prototype
属性。 -
箭头函数没有自己的
arguments
对象,也不能使用arguments
变量获取参数列表。箭头函数的参数只能通过显式命名参数或使用剩余参数来获取。 -
箭头函数不能使用
yield
关键字,因此不能用作生成器函数。 -
箭头函数的语法更加简洁,可以省略
{}
和return
关键字。当函数体只有一条语句时,可以直接将语句作为返回值。
例如,普通函数和箭头函数的写法对比如下:
普通函数:
function add(x, y) {
return x + y;
}
箭头函数:
let add = (x, y) => x + y;
可以看到,箭头函数的语法更加简洁,代码量更少。但是需要注意,箭头函数的使用场景和普通函数并不完全相同,需要根据具体情况进行选择。
四、class与class继承 promise使用及实现
- class与class继承
class是ES6中一个新增的关键字,用于定义类。它可以用来声明一个类,类中可以包含构造函数、属性和方法。示例代码如下:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I am ${this.age} years old.`);
}
}
const person = new Person('Tom', 18);
person.sayHello(); // 输出:Hello, my name is Tom, I am 18 years old.
class继承则是基于现有类创建新类的一种方式,新类会继承原有类的属性和方法。可以通过关键字extends
实现。示例代码如下:
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类的构造函数
this.grade = grade;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I am ${this.age} years old. I am in grade ${this.grade}.`);
}
}
const student = new Student('Alice', 16, 10);
student.sayHello(); // 输出:Hello, my name is Alice, I am 16 years old. I am in grade 10.
- promise使用及实现
promise是一种可用于异步编程的机制,它可以让我们更加方便地处理异步操作的结果。promise有三种状态:pending
(等待中)、fulfilled
(已成功)和rejected
(已失败)。promise支持链式调用,每个调用返回的都是一个新的promise对象。示例代码如下:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('data'); // 异步操作成功后将执行resolve,并将结果作为参数传入
// reject('error'); // 异步操作失败后将执行reject,并将错误信息作为参数传入
}, 1000);
});
}
fetchData()
.then(result => {
console.log(result); // 输出:data
return 'newData';
})
.then(result => {
console.log(result); // 输出:newData
})
.catch(error => {
console.log(error); // 输出:error
})
.finally(() => {
console.log('done'); // 输出:done
});
上面的代码中,fetchData
函数返回一个新的Promise对象,并在异步操作完成后调用resolve
或reject
函数。在调用fetchData
函数后,我们通过then
方法来指定当异步操作成功时执行的回调函数,并通过catch
方法来指定当异步操作失败时执行的回调函数。在最后我们可以通过finally
方法指定一个回调函数,在所有promise对象完成后执行。
实现一个简单的promise对象:
class MyPromise {
constructor(executor) {
this._status = 'pending'; // 初始状态为等待中
this._value = undefined; // 成功时的值
this._reason = undefined; // 失败时的原因
const resolve = (value) => {
if (this._status === 'pending') {
this._status = 'fulfilled';
this._value = value;
}
}
const reject = (reason) => {
if (this._status === 'pending') {
this._status = 'rejected';
this._reason = reason;
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
if (this._status === 'fulfilled') {
onFulfilled(this._value);
} else if (this._status === 'rejected') {
onRejected(this._reason);
}
}
catch(onRejected) {
if (this._status === 'rejected') {
onRejected(this._reason);
}
}
finally(onFinally) {
onFinally();
}
}
// 使用示例
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('data');
}, 1000);
});
promise.then(result => {
console.log(result);
}).finally(() => {
console.log('done');
});
五、async,await
async和await是JavaScript中的关键字,它们用于处理异步操作。
async用于定义一个异步函数,而await用于等待一个异步操作完成。当我们调用一个返回Promise对象的异步函数时,我们可以在它前面加上await关键字,JavaScript引擎会等待这个异步操作完成并返回结果,然后再继续执行后面的代码。
这样就可以避免使用回调函数或者Promise链等复杂的异步操作处理方式。async/await使得JavaScript代码的可读性和可维护性更好,并且可以避免回调函数地狱的情况。
六、generator函数 Es6中新的数据类型symbol
generator函数是ES6中引入的一种新型的函数类型。它通过生成器函数,可以生成一个迭代器,这个迭代器可以用来迭代一个可迭代对象。在generator函数中,使用yield语句可以暂停函数的执行,并将返回值传递给迭代器;使用next()方法可以继续执行函数,接着执行yield语句后面的代码。
Symbol是ES6中引入的一种新的基本数据类型,它是一种独一无二的标识符,用来表示一个对象的唯一属性名。Symbol值可以作为对象属性的键,并且不会被遍历到,因此它可以用来定义一些私有属性或者方法。在ES6中,通过Symbol()函数可以生成一个全局唯一的Symbol值。
七、Es6中Set、WeakSet、Map、WeakMap数据结构 es6模块规范
-
Set:Set是一种集合数据结构,可以用来存储一组唯一的值。在Set中,每个值都只能出现一次,重复的值会自动被过滤掉。
-
WeakSet:WeakSet也是一种集合数据结构,但是它只能存储对象类型的值,并且存储的对象是弱引用。
-
Map:Map是一种键值对数据结构,它可以用来存储任意类型的值。在Map中,可以使用任何类型的值作为键,而且它们是不重复的。
-
WeakMap:WeakMap也是一种键值对数据结构,但是它只能使用对象类型的值作为键,并且存储的对象是弱引用。
-
ES6模块规范:ES6模块规范定义了一套模块化开发的标准,可以让我们方便地将代码拆分成多个模块,以便于维护和复用。在ES6模块规范中,每个模块都是一个独立的文件,它们之间通过
import
和export
语句来实现模块之间的依赖关系。