2-1值类型与引用类型有什么区别?
哪些类型是值类型?哪些类型是引用类型?
- 值类型:String、Number、Boolean、Symbol
- 引用类型:JSON、Array、null
演示值类型与引用类型的堆栈模型
2-2手写深拷贝
如何实现深拷贝
- JSON.parse(JSON.stringify(obj))
不能存放函数、时间对象、正则... - 递归
没有考虑循环引用。 - 演示代码
<script>
let obj1 = {
name:"张三",
age:"18",
address: {
city:"北京",
},
hobby: ["台球","篮球"]
}
const obj2 = deepClone(obj1);
console.log(obj1);
obj2.age = 20;
obj2.address.city = "上海"
console.log(obj2);
function deepClone(obj) {
if(typeof obj !=="object" || obj == null){
return obj
}
let res = obj instanceof Array ? [] : {};
for(key in obj){
if(obj.hasOwnProperty(key)){
res[key] = deepClone(obj[key]);
}
}
return res
}
</script>
2-3何时使用== 何时使用===
==隐式转换
100 == '100'
0 == ''
0 == false
false == ''
null == undefined
只有 obj == null 使用双等
if(obj === null || obj === undefined) {
}
演示代码
只有在判断一个对象是否等于null的时候用==,除此之外情况我们都是使用===的。
<script>
console.log(100 == '100');
console.log(0 == '');
console.log(0 == fasle);
console.log('' == false);
console.log(null == undefined);
let obj = { age: 18 };
if (obj == null) {
}
if (obj == null || obj ===undefined) {
}
</script>
2-4哪些是truly变量?哪些是flasely变量?
什么是truly变量?什么是falsely变量
- truly变量:!!val === true
- falsely变量:!!val === false
除了falsely变量,都是truly变量
- 数字0
- NaN
- 空字符串
- null
- undefined
2-5说说原型和原型链是怎么回事?
复习class基础语法
- class
- construct
- extends
- super
- instanceof
什么是原型
什么是原型链
手写instanceof
出题目的
- 考察原型链的理解
知识点
instanceof
- instanceof 运算符用于检测构造函数的prototype 属性是否出现在某个实例对象的原型链上
- 可以判断继承关系,只要是在同一条原型链上,就可以返回true
参考答案
<script>
class Person {
constructor(name) {
this.name = name;
}
}
class Student extends Person {
constructor(name,age) {
super(name);
this.age = age;
}
}
const zhangsan = new Student('张三',18);
console.log(myInstanceof(zhangsan,Student));
console.log(myInstanceof(zhangsan,Person));
function myInstanceof(obj1,obj2) {
let obj1Proto = obj1.__proto__;
while (true){
if (obj1Proto === null) {
return false
}
if (obj1Proto === obj2.prototype) {
return true;
}
obj1Proto = obj1Proto.__proto__
}
}
</script>
this不同场景,如何取值?
this 易混场景
- 普通函数下的this
- call apply bind的this
- 定时器中的this
- 箭头函数中的this
测试题
<button id="btn">test</button>
<script>
// 普通函数中的this
// 非严格模式 this -> window
// 严格模式下 this -> undefined
// 'use strict'//开启严格模式
// function a() {
// console.log(this);
// }
// a();
// call apply bind 中的this
// 情况1:this -> window
// a.call()
// a.call(undefined)
// a.call(null)
// 情况2:传什么,this就是什么
// function a() {
// console.log(this);
// }
// a.call();
// a.apply({x : 101});
// a.bind({x : 101})()
// 定时器中的this
// 情况1:定时器+function this->window
// 情况2:定时器+箭头函数 this->上层作用域的this
// 情况1
// setTimeout(function () {
// console.log(this);
// },100);
// function fn() {
// setTimeout(function () {
// console.log(this);
// },100)
// }
// fn.call({ x : 101})
// const a={
// fn() {
// setTimeout(function () {
// console.log((this));
// })
// }
// }
// a.fn()
// 情况2
// class Obj{
// fn() {
// setTimeout(() => {
// console.log((this));
// },100)
// }
// }
// const o = new Obj();
// o.fn()
// function fn() {
// setTimeout(() => {
// console.log(this);
// },100)
// }
// fn.call({x : 101})
// 箭头函数中的 this
// 情况1:有function作用域的,this是上层作用域的this
// 情况2:没有function作用域的,this是window
// class Obj{
// say = () => {
// console.log(this);
// }
// }
// const obj1 = new Obj();
// obj1.say()
// const obj2 = {
// say: () => {
// console.log(this);
// }
// }
// obj2.say()
// const oBtn = document.getElementById('btn');
// oBtn.onclick = function () {
// console.log(this);
// }
// oBtn.onclick = () => {
// console.log(this);
// }
</script>
手写bind函数
知识点
- Function.prototype.myBind
- Array.prototype.slice.call()
- array.shift()
演示代码
<script>
function fn(a,b,c) {
console.log(this);
console.log(a,b,c);
return "this is return"
}
// const cb = fn.bind({x : 100},1,2,3);
// console.log(cb());
//手写bind
Function.prototype.myBind = function () {
const fn = this;
// [{ x : 100 } ,1,2,3]
//arguments是类数组
// const _this = arguments[0];
const arg = Array.prototype.slice.call(arguments);
const _this = arg.shift();
return function () {
return fn.apply(_this,arg);
}
console.log(this);
}
const cb = fn.myBind({ x : 100 },1,2,3)
console.log(cb());
</script>
谈谈闭包和闭包使用场景
什么是闭包
概念:闭包是作用域的一种特殊应用
作用域
全局作用域、局部作用域
自由变量
不在自己作用域里的变量,就是自由变量 自由变量的值,在函数定义的地方向上层作用域查找。与函数调用位置无关
触发闭包的情况
- 函数当做返回值被返回
- 函数当做参数被传递
- 自执行匿名函数
闭包的应用
- 隐藏变量
- 解决 for i 的问题
演示代码
<script>
// const a = 100 //全局变量
// function fn() {
// console.log(a); //自由变量
// }
// 情况1 :函数当做返回值被返回
// function fn() {
// const a = 1;
// return function () {
// console.log(a);
// }
// }
// const a = 5;
// const cb = fn()
// cb()
// 情况2 :函数当做参数传递
// function fn(cb) {
// const a = 100;
// cb()
// }
// const a = 500;
// fn(function () {
// console.log(a);
// })
// 情况3 :自执行匿名函数
// (function (index) {
// console.log(index);
// })(10);
</script>
<button>0</button>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<script>
// const aBtn = document.querySelectorAll('button');
// for(var i=0;i<aBtn.length;i++) {
// (function (index) {
// aBtn[i].onclick = function () {
// console.log(index);
// }
// })(i)
// }
//隐藏变量
function fn() {
const data = {};
return {
set:function (key,val) {
data[key] = val
},
get:function (val) {
return data[val]
}
}
}
const json = fn();
json.set('age',18);
console.log(json.get('age'));
</script>
请描述event loop的机制
JavaScript是如何执行的?
- 自上而下,从左到右一行一行执行
- 如果有一行报错,后面的代码不执行
- 先执行同步代码,再执行异步代码(setTimeout、ajax)
Event loop 过程
- 同步代码,一行一行放在Call Stack 中执行
- 遇到异步,会先"记录"下,等待执行时机(setTimeout、Ajax) 。时机到了,将之前"记录"的代码放入Callback Queue
- 当 Call Stack为空 (同步代码执行完),Event Loop 开始工作
- Event Loop 轮询查找Callback Queue 中是否有可执行的代码。如果有,将代码移动到Call Stack 中执行
- Event Loop 如果没有找到可以执行代码,则会继续轮询查找
<script>
console.log('strat');
setTimeout (() => {
console.log('setTimeout');
},2000)
console.log('end');
</script>
什么是宏任务?什么是微任务?二者有什么区别?
宏任务有哪些?微任务有哪些?
请描述宏任务、微任务和DOM渲染的关系
什么是微任务?什么是宏任务?
微任务:Promise、async、await
宏任务:setTimeout、setInterval、Ajax、DOM事件
二者的区别
先执行微任务,后执行宏任务
宏任务、微任务、DOM渲染的关系
- Call Stack 清空,触发Event Loop
- 执行微任务
- DOM渲染
- 执行宏任务
完整Event Loop过程
手写Promise加载图片
出题目的
- 考察Image对象
- 考察Promise
- 考察async await
知识点
image 对象
- new Image() —— 声明一个Image对象
- onload —— 当图片加载成功时执行
- onerror —— 当图片加载失败时执行
- .src —— 设置图片路径
Promise
- 作用:解决回调地狱问题
- 语法:
new Promise().then().catch()
async await
- 作用:Promise 的语法糖,可以增加代码可读性 (用同步的思维写代码)
- 语法:
(async function () {
try {
const oImg = await loadImg("https://robohash.org.1");
document.body.appendChild(oImg1);
const oImg2 = await loadImg("https://robohash.org/2");
document.body.appendChild(oImg2)
} catch (msg) {
console.log(msg);
}
})()
Promise 阅读代码题
- 考察Promise、async、await 的基础
- 考察对Event Loop、宏任务、微任务的理解
知识点
- JS执行顺序:自上而下、先同步再异步、先微任务后宏任务
- new promise() -> Promise.resolve()
- then 和 catch 内部没有 throw new Error 相当于 resolve
- async function 相当于返回 Promise.resolve()
- await 后面的代码都是异步的
- 初始化Promise时,函数内部代码会被立即执行
for...in、for..of有什么区别
出题目的
- 考察JavaScript基础
参考答案
是否可枚举:Object.getOwnPropertyDescriptors(obj) -> enumerable:true
是否可迭代:arrSymbol.iterator -> next()
拓展问题
for await ...of 有什么作用?
演示代码
<script>
// const arr = [10,20,30];
// for(let key in arr){
// console.log(key);
// }
// for(let key of arr){
// console.log(key);
// }
const obj = {
name:'abc',
age:18,
}
for (let key in obj) {
console.log(key);
}
const set = new Set([10,20,30]);
for (let val of set) {
console.log(val);
}
const map = new Map([
["a",10],
["b",10],
["c",10]
])
for (let val of map) {
console.log(val);
}
</script>
for await..of有什么作用?
出题目的
- 考察对异步的掌握情况
参考答案
for await .. of 用于遍历一组Promise
知识点
for await .. of
Promise.all
<script>
function getPromise(num) {
return new Promise((resolve,reject) => {
setTimeout (() => {
resolve(num);
},1000)
})
}
const p1 = getPromise(10)
const p2 = getPromise(20)
const p3 = getPromise(30)
const list = [p1,p2,p3];
(async function () {
const data = [10,20,30];
for (let val of data) {
const res = await getPromise(val)
console.log(res);
}
})()
// (async function() {
// for await (let res of list) {
// console.log(res);
// }
// })()
// Promise.all(list).then((res) => {
// console.log(res);
// })
</script>
var、let和const有什么区别?
出题目的
- 考察JS基础
- 暂时死区
参考答案
var、let和const的区别
1.块级作用域
2. 变量提升
3. 重名
手写flat方法拍平数组
出题目的
- 考察Array相关API熟悉程度
知识点
Array.concat -Array.concat(1,2,3,[4,5])
判断是不是数组
- xxx instanceof Array
递归
参考答案
<script>
function falt(arr) {
const hasDeep = arr.some(item => item instanceof Array)
if(!hasDeep) {
return arr
}
const res = Array.prototype.concat.apply([],arr)
return flat(res)
}
function flat(arr) {
while(arr.some((item) => item instanceof Array)) {
arr = Array.prototype.concat.apply([],arr)
}
return arr
}
</script>
数组去重
出题目的
- 考察Set的使用
参考答案
<script>
const arr = [10,20,20,20,30,50,50,60];
console.log(unique(arr));
function unique (arr) {
const set = new Set(arr);
return [...set];
}
// function unique(arr) {
// const res = [];
// arr.foreach((item) => {
// if(res.indexOf(item) === -1) {
// res.push(item)
// }
// })
// return res
// }
</script>
求数组中的最大值?
出题目的
- 考察数组API
知识点
- Array.sort
- Array.reduce
- Math.max
参考答案
<script>
let arr = [100,5,20,3,200,6]
console.log(getMax(arr));
// function getMax(arr) {
// // return Math.max.apply(null,arr);
// return Math.max(...arr)
// }
// function getMax(arr) {
// arr.sort((n1,n2) => {
// return n2 - n1
// })
// return arr[0]
// }
function getMax(arr) {
return arr.reduce((n1,n2) => {
return n1 > n2 ? n1:n2
})
}
</script>
防抖和节流有什么区别? 手写防抖与节流
出题目的
- 考察防抖与节流的掌握程度
知识点
防抖与节流
参考答案
<style>
div {
margin-top: 20px;
width: 200px;
height: 200px;
background-color: #ccc;
}
</style>
<h1>手写防抖与节流</h1>
<input type="text">
<div draggable="true"></div>
<body>
<script>
// 防抖
// const oInp = document.querySelector("input");
// oInp.addEventListener(
// "keyup",
// debounce(function () {
// console.log(oInp.value + "取后台请求");
// })
// )
// function debounce(fn) {
// let timer = null;
// return function() {
// if(timer) clearTimeout(timer);
// timer = setTimeout(() => {
// fn.call(this,arguments);
// timer = null;
// },1000);
// }
// }
//节流
const oDiv = document.querySelector('div');
oDiv.addEventListener(
'drag',
throttle(function (e) {
console.log(e.clientX);
})
);
function throttle(fn) {
let timer = null
return function () {
if (timer) return;
timer = setTimeout(() => {
fn.apply(this,arguments);
timer = null
},100)
}
}
</script>