1、let、const、var的区别
块级作用域:let和const具有块级作用域,var不存在块级作用域。块级作用域解决了内层变量可能覆盖外层变量,以及用来计数的循环变量泄露为全局变量的问题。
变量提升:var存在变量提升,let和const不存在变量提升(只能在声明后使用,否则会报错)
给全局添加属性:浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但let和const不会。
重复声明:var可以重复声明变量,let和const不可以。
暂时性死区:在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。var声明变量,不存在暂时性死区。
初始值设置:var和let在声明变量时可以不用设置初始值,const在声明变量时必须设置初始值。
指针指向:let和const都是ES6新增语法,let创建的变量是可以更改指针指向的,而const声明的变量是不允许改变指针指向的。
2、const对象的属性可以修改吗
const保证的不是变量的值不能改变,而是变量指向的那个内存地址不能改动。对于基本数据类型的数据,其值就保存在变量指向的那个内存地址,因此等同于常量。
但对于引用类型的数据,变量指向数据的内存地址,保存的是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是不可变的,就完全不能控制了。
3、可以new一个箭头函数吗
箭头函数没有自己的prototype,也没有自己的this指向,不能使用arguments参数,所以不能new一个箭头函数。
new操作符的实现步骤:
a、创建一个对象
b、将构造函数的作用域赋给新对象(即将对象的__proto__属性指向构造函数的prototype属性)
c、指向构造函数中的内容,构造函数中的this指向该对象(即为这个对象添加属性的方法)
d、返回新对象
箭头函数无法执行第2、3步。
4、箭头函数与普通函数的区别
a、箭头函数比普通函数更简洁:如果不需要参数,一个空括号即可;如果只有一个参数,可以省略括号;如果有多个参数,用逗号分隔;如果函数体的返回值只有一句话,可以省略大括号;如果函数体不需要返回值,且只有一句话,在关键句前加一个void关键字即可。
let fn = () => void doesNotReturn();
b、箭头函数没有自己的this:箭头函数在自己作用域的上一层继承this,所以箭头函数中this的指向在它定义时已经确定了,后续不会改变。
c、 call()、apply()、bind()等方法不能改变箭头函数的this的指向
d、箭头函数不能作为构造函数使用
e、箭头函数没有自己的arguments
f、箭头函数没有prototype
g、箭头函数不能用作Generator函数,不能用yield关键字
5、扩展运算符的作用及使用场景
a、对象扩展运算符:用于取出参数对象中的所有可遍历属性,拷贝到当前对象中
扩展运算符对对象实例的拷贝属于浅拷贝。
let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }
如果用户自定义的属性,放在 扩展运算符后面,则后面的属性会覆盖前面的属性。
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}
b、数组扩展运算符
可以将数组转为用逗号分隔的参数序列,且每次只能展开一层数组。
console.log(...[1, 2, 3])
// 1 2 3
console.log(...[1, [2, 3, 4], 5])
// 1 [2, 3, 4] 5
将数组转换为参数序列:
function add(x, y) {
return x + y;
}
const numbers = [1, 2];
add(...numbers) // 3
复制数组:
const arr1 = [1, 2];
const arr2 = [...arr1]; // [1, 2]
合并数组:
const arr1 = ['two', 'three'];
const arr2 = ['one', ...arr1, 'four', 'five']; // ["one", "two", "three", "four", "five"]
与结构复制结合起来生成数组:
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
将字符串转换为数组:
[...'hello'] // [ "h", "e", "l", "l", "o" ]
使用Math函数获取数组中的特定值:
const numbers = [9, 4, 7, 1];
Math.min(...numbers); // 1
Math.max(...numbers); // 9
6、 Proxy可以实现什么功能
在vue3中,使用Proxy替换Object.defineProperty来实现数据响应式。
Proxy是ES6新增功能,可以用来自定义对象中的操作。
let p = new Proxy(target, handler)
target代表要添加代理的对象,handler用来自定义对象中的操作。
以下是一个简单的例子:
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver)
},
set(target, property, value, receiver) {
setBind(value, property)
return Reflect.set(target, property, value)
}
}
return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
p.a = 2 // 监听到属性a改变
p.a // 'a' = 2
7、如何提取高度嵌套的对象里的指定属性
const school = {
classes: {
stu: {
name: 'Bob',
age: 24,
}
}
}
方法一:逐层解构
const { classes } = school
const { stu } = classes
const { name } = stu
name // 'Bob'
方法二:嵌套解构
const { classes: { stu: { name } }} = school
console.log(name) // 'Bob'
在解构出来的变量名右侧,通过冒号+{目标属性名}这种形式,进一步解构它,一直解构到拿到目标数据为止。
8、对...参数的理解
扩展运算符被用在函数形参上时,可以把一个分离的参数序列整合成一个数组:
function mutiple(...args) {
let result = 1;
for (var val of args) {
result *= val;
}
return result;
}
mutiple(1, 2, 3, 4) // 24
传入 mutiple 的是四个分离的参数,但是如果在 mutiple 函数里尝试输出 args 的值,会发现它是一个数组。
function mutiple(...args) {
console.log(args)
}
mutiple(1, 2, 3, 4) // [1, 2, 3, 4]
除解构等外,还可以把函数的多个入参收敛进一个数组里。这一点经常用于获取函数的多余参数,或者像上面这样处理函数参数个数不确定的情况。
9、ES6中模板语法
模板语法使字符串的拼接变得更加简单:
var name = 'css'
var career = 'coder'
var hobby = ['coding', 'writing']
var finalString = `my name is ${name}, I work as a ${career} I love ${hobby[0]} and ${hobby[1]}`
模板字符串允许用${}的方式嵌入变量,使字符串的拼接更加简单、易读。模板字符串中,空格、缩进、换行都会被保留;且还可以在${}在进行一些简单的计算。
function add(a, b) {
const finalString = `${a} + ${b} = ${a+b}`
console.log(finalString)
}
add(1, 2) // 输出 '1 + 2 = 3'
10、ES6中的字符串处理方法
存在性判定:includes、startsWith、endsWith
includes:判断字符串与子串的包含关系
const son = 'haha'
const father = 'xixi haha hehe'
father.includes(son) // true
startsWith:判断字符串是否以某个字符开头
const father = 'xixi haha hehe'
father.startsWith('haha') // false
father.startsWith('xixi') // true
endsWith:判断字符串是否以某个字符结尾
const father = 'xixi haha hehe'
father.endsWith('hehe') // true
自动重复:可以使用repeat方法来使同一个字符串输出多次(被连续复制多次)
const sourceCode = 'repeat for 3 times;'
const repeated = sourceCode.repeat(3)
console.log(repeated) // repeat for 3 times;repeat for 3 times;repeat for 3 times;