1.var、let、const的区别
1.var 可以变量提升,const、let不存在变量提升。
(1)var 代码
console.log(a); // undefiend
var a = 100;
(2)let、const 代码
console.log(b); // Cannot access 'b' before initialization
const/let b = 100;
2.var 不存在暂时性死区,const、let存在暂时性死区,只有等声明的那一行代码出现,才可以获取该变量的值。例子同上。
3.var 不存在块级作用域,const、let存在块级作用域。
(1)
{
var a = 100;
}
console.log(a); // 100
(2)
{
let/const b = 100;
}
console.log(b); // b is not defined
4.var 可以重复生命,const、let不允许重复声明。
(1)
var a = 100;
var a = 10;
console.log(a); // 10
(2)
const/let b = 100;
const/let b = 1000;
console.log(b); // Identifier 'b' has already been declared
5.var 、let可以修改变量的值,const不能改变。
(1)
var/let a = 100;
a = 10;
console.log(a); // 10
(2)
const b = 100;
b = 1000;
console.log(b); // Uncaught TypeError: Assignment to constant variable
2. ES6中新增的Set、Map两种数据结构怎么理解?
1.共同点:集合、字典都可以存储不重复的值。
2.不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储。
3.Set只有值,元素不可重复且自动排序,Map是以键值对的方式存储。
4.例子
(4.1)
const objSetList =
[
{
id : 1,
name: "skam"
},
{
id : 2,
name: "skam2"
}
];
const objMapList =
[
['key', 'value'],
['任意类型', { id: 1, name: "skam" }]
];
const SetList = new Set(objSetList);
const MapList = new Map(objMapList);
console.log(SetList); // [[entries]] 下面的第0项 -> value: { id : 1, name : "skam" },
// 第1项 -> vlaue: { id : 2, name : "skam2" }
console.log(MapList); // [[entries]] 下面的第0项 -> { key : "key", value: "value" },
// 第1项 -> { key: "任意类型", value : { id : 1, name : "skam" } }
3.怎么理解ES6中 Promise的?使用场景有哪些?
1.是一种异步编程方式,用来解决回调地狱,优点(链式操作减低了编码难度、代码可读性明显增强)
2.三种状态(peeding、fulfilld、rejected)和种实例方法(then、catch、finally)
const Promise = function () {
new Promise((resolve, reject) => {
axios.get(url,...)
}).then(res => { // 当请求成功后,会把返回的数据当作参数(res), 然后打印输出。
console.log(res);
}).catch(err = > {
console.log(err); // 当请求失败后,会把请求失败的原因当作参数(err), 然后打印输出。
}).finally(() => {
console.log("不管状态如何,我都会执行") // 不管请求是否成功,都会打印这句话
})
}
Promise();
3.Promise.all() -> const p = Promise.all([p1, p2, p3]);
接受一个参数(数组),只有数组中所有都resolve都通过了, p.then()才会执行,只要有一个reject, 改错误就会执行单个错误请求的
reject, 不会触发Promise.all()的catch方法。
4.Promise.race() -> 同上
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变,率先改变的 Promise 实例的返回值则传递给p的回调函数。
5.使用场景?
(5.1)图片的加载。
(5.2)图片的请求超时
4.怎么理解ES6中 Generator的?使用场景有哪些?
1.是一种异步编程解决方案,执行Generator函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
2.特征:
(2.1)function关键字与函数名之间有一个星号
(2.2)函数体内部使用yield表达式,定义不同的内部状态
(2.3)函数执行返回一个generater对象,函数体里面任何代码不会执行。
(2.4)每次执行next(),遇到yield就会终止,再次next直到遇到return为止,把return后语句返回,如果没有return,value为 undefnied。
(2.5)将异步任务同步化
3.使用
(3.1)
function* generater(b){
let a = 100;
console.log(a * 1);
yield console.log(a * 1);
yield console.log(a * 1);
return console.log(a * 4);
}
const y = generater()
console.log(y); // generater对象
console.log(y.next()); // 100 200 done: false
console.log(y.next()); // 300 done: false
console.log(y.next()); // 400 done: false
console.log(y.next()) // done: true 当done为true时,相当于改函数完整执行一次,后面多次next返回相同结果。
(3.2)
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v); // 1 2 3 4 5
}
4.使用场景
(4.1)Generator是异步解决的一种方案,最大特点则是将异步操作同步化表达出来
function* loadUI() {
showLoadingScreen();
yield loadUIDataAsynchronously();
hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()
// 卸载UI
loader.next() // 这列加载UI和卸载UI就相当于同步代码, 其实内部是异步请求的。
(4.2)利用Generator函数,在对象上实现Iterator接口
function* iterEntries(obj) {
let keys = Object.keys(obj);
for (let i=0; i < keys.length; i++) {
let key = keys[i];
yield [key, obj[key]];
}
}
let myObj = { foo: 3, bar: 7 };
for (let [key, value] of iterEntries(myObj)) {
console.log(key, value); // foo 3 // bar 7
}
5. symbol 有什么用处?应用场景?
1.新的原始数据类型Symbol,表示独一无二的值。
2.细节
let obj = {
[Symbol("name")]: "一斤代码",
age: 18,
title: "Engineer",
}
//(2.1) 使用symbol作为对象属性名不被Object.key等方式访问
//(2.2) 使用JSON.stringify()将对象转换成JSON字符串,Symbol属性会被排除在外
//(2.3) 获取Symbol方式定义的对象属性
Object.getOwnPropertySymbols(obj);//[Symbol(name)]
Reflect.ownKeys(obj);// ['age', 'title', Symbol(name)]
3.应用场景
使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突,那么就使用
Symbol创建一个独一无二的对象名。
6.箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?
1.箭头函数与普通函数的区别
(1)语法更加简洁、清晰
(2)箭头函数不会创建自己的this
(2.1)官方解释 箭头函数不会创建自己的this,所以它没有自己的this,它只会从自己的作用域链的上一层继承this。
(2.2)箭头函数中this的指向在它被定义的时候就已经确定了,之后永远不会改变
(3)箭头函数继承而来的this指向永远不变
(4).call()/.apply()/.bind()无法改变箭头函数中this的指向
(5)箭头函数没有自己的arguments
(6)箭头函数没有原型prototype
(7)箭头函数不能用作Generator函数,不能使用yeild关键字
2.箭头函数能当构造函数吗?
构造函数的new都做了些什么?
(1)JS内部首先会先生成一个对象
(2)再把函数中的this指向该对象
(3)然后执行构造函数中的语句
(4)最终返回该对象实例
因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会随在哪里调用、被谁调用而改变,所 以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用new调用时会报错!
7.common.js和es6中模块引入的区别?
1.CommonJS模块输出的是一个值的拷贝 <--> ES6模块输出的是值的引用
2.CommonJS模块是运行时加载 <--> ES6模块是编译时输出接口
3.CommonJs是单个值导出 <--> ES6 Module可以导出多个
4.CommonJs是动态语法可以写在判断里 <--> ES6 Module静态语法只能写在顶层
5.CommonJs的this是当前模块 <--> ES6 Module的this是undefined
6.CommonJS 模块的require()是同步加载模块 <--> ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段
8.谈谈 Object.defineProperty 与 Proxy 的区别
1.Object.defineProperty(obj, prop, descriptor)
(1.1) 不能监听数组的变化 Vue2.x 中解决数组监听的方法是将能够改变原数组的方法进行重写实现(比如:push、 pop、shift、 unshift、splice、sort、reverse)
(1.2) 必须遍历对象的每个属性 --> 通过 Object.keys() 来实现
(1.3) 必须深层遍历嵌套的对象 --> 通过递归深层遍历嵌套对象,然后通过 Object.keys() 来实现对每个属性的劫持
2.Proxy
(2.1) Proxy 针对的整个对象,Object.defineProperty 针对单个属性,这就解决了 需要对对象进行深度递归(支持嵌套的复杂对象 劫持)实现对每个属性劫持的问题
(2.2) Proxy 解决了 Object.defineProperty 无法劫持数组的问题
(2.3) 比 Object.defineProperty 有更多的拦截方法,对比一些新的浏览器,可能会对 Proxy 针正对性的优化,有助于性能提升
9. object.assign和扩展运算法是深拷贝还是浅拷贝,两者区别是什么?
1. 都是浅拷贝。
2. 区别
(2.1)Object.assign()方法接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。它 会修改了一个对象,因此会触发 ES6 setter。
(2.2)扩展操作符(…)使用它时,数组或对象中的每一个值都会被拷贝到一个新的数组或对象中。它不复制继承的属性或类的属性,但是 它会复制ES6的 symbols 属性
10. ES6有哪些新特性?
1.解决原有语法上的一些不足
比如let 和 const 的块级作用域
2.对原有语法进行增强
比如解构赋值、展开运算符(...)、参数默认值、模板字符串(``)
3.全新的对象、全新的方法、全新的功能
比如promise、proxy、object的assign、is
4.全新的数据类型和数据结构
比如symbol、set、map
11. 说说对 ES6 中rest参数的理解
1.什么是rest参数 --> function fn(...rest) {} // ...rest 就是rest参数
2.举例
function fn(...rest) {
console.log(rest) // [1, 2, 3, 4]
console.log(...rest) // 1 2 3 4
console.log(rest.length) // 4
}
fn(1, 2, 3, 4)
3.rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
function fn2(...rest, a) {} // 会报错