1.介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
Set
成员唯一、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
可以遍历,方法有:add、delete、has
WeakSet
成员都是对象
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
不能遍历,方法有add、delete、has
Map
本质上是键值对的集合,类似集合
可以遍历,方法很多可以跟各种数据格式转换
WeakMap
只接受对象作为键名(null除外),不接受其他类型的值作为键名
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有get、set、has、delete
2.setTimeout、Promise、Async/Await 的区别
主要是考察这三者在事件循环中的区别,事件循环中分为宏任务队列和微任务队列。
其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行;
promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;
async函数表示函数里面可能会有异步方法,await后面跟一个表达式,
async方法执行时,遇到await会立即执行表达式,
然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。
练习 下面代码输出顺序
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
答案 想一想为什么?
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
3.JS 异步解决方案的发展历程以及优缺点。
Promise
优点:解决了同步的问题
缺点:无法取消 Promise ,错误需要通过回调函数来捕获
Async/await
优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
4.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
执行结果是:1243
promise构造函数是同步执行的,then方法是异步执行的
5.Vue 的响应式原理
Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
1 Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
2 Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,
还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
3 Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
6.练习题
6.1
如下:{1:222, 2:123, 5:888},请把数据处理为如下结构:
[222, 123, null, null, 888, null, null, null, null, null, null, null]。
let obj = {1:222, 2:123, 5:888};
//创建一个长度为12的空数组 循环吧对应的值得到obj的值 否则为空
Array.from({ length: 12 }).map((item,index)=>{
return obj[index+1] || null
})
6.2
var a = 10;
(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)
})()
=========================================================
在立即执行函数中,var a = 20; 语句定义了一个局部变量 a,由于js的变量声明提升机制,局部变量a的声明会被提升至立即执行函数的函数体最上方,
且由于这样的提升并不包括赋值,因此第一条打印语句会打印undefined,最后一条语句会打印20。
由于变量声明提升,a = 5; 这条语句执行时,局部的变量a已经声明,因此它产生的效果是对局部的变量a赋值,
此时window.a 依旧是最开始赋值的10,
6.3下面代码中 a 在什么情况下会打印 1?
var a = ?;
if(a == 1 && a == 2 && a == 3){
console.log(1);
}
6.3答案
var a = {
a: 1,
toString() {
return ++this.a;
}
}
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
===================================
let a = [1,2,3];
a.toString = a.shift;
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
6.4简单改造下面的代码,使之分别打印 10 和 20。
var b = 10;
(function b(){
b = 20;
console.log(b);
})();
6.4答案
打印10
var b = 10;
(function b(b) {
window.b = 20;
console.log(b)
})(b)
打印20
var b = 10;
(function b() {
var b = 20;
console.log(b)
})()
==========
var b = 10;
(function b(b) {
b = 20;
console.log(b)
})(b)
6.5改造下面的代码,使之输出0 - 9,写出你能想到的所有解法
for (var i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
========================================================================================
//方法1
for (var i = 0; i< 10; i++){
setTimeout((i) => {
console.log(i);
}, 1000,i)
}
//方法2
for (let i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
6.6全局作用域中,用 const 和 let 声明的变量不在 window 上,那到底在哪里?如何去获取?
用 let 和 const 声明的全局变量并没有在全局对象中,只是一个块级作用域(Script)中
怎么获取?在定义变量的块级作用域中就能获取啊,既然不属于顶层对象,那就不加 window(global)呗。
let aa = 1;
const bb = 2;
console.log(aa); // 1
console.log(bb); // 2
6.7['1', '2', '3'].map(parseInt) what & why ?
而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。
parseInt(string, radix)
parseInt('1', 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
parseInt('2', 1) //基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN
parseInt('3', 2) //基数为2(2进制)表示的数中,最大值小于3,所以无法解析,返回NaN
练习 ['10','10','10','10','10'].map(parseInt);
// 10 nan 2 3 4