前端面试常见知识点
js
数据类型和计算
- 值类型和引用类型
- 常见值类型
string、num、undefined、boolean、symbol - 常见引用类型
- 值类型和引用类型存储区别
值可以直接存储在栈中,如下
而引用类型存储如下:
- 堆和栈:(这篇文章介绍得很详细了)
栈内存相当于一个水桶类型,入栈顺序先进后出
栈内存存储基本数据类型,堆内存存储引用数据类型
栈内存中自动分配空间和释放,但堆内存中变量不同,具体看下面垃圾回收机制;
- 浏览器的垃圾回收机制
- 引用计数(ie8及之前)
跟踪变量被引用次数,引用一次+1,释放-1,当引用次数为0时即可回收
缺点:
循环引用时会出问题 - 标记清除(目前主流浏览器使用)
当变量进入执行环境时,这个变量标记为“进入环境”;而当变量离开环境时,则将其标记为“离开环境”,垃圾回收器销毁并回收那些被标记为“离开环境”的值所占用的内存空间
缺点: - v8垃圾回收策略
采用分代回收策略,将内存分为新生代和老生代
新生代空间中的对象为存活时间较短的对象,大多数的对象被分配在这里,这个区域很小但是垃圾回特别频繁
老生代空间中的对象为存活时间长或常驻内存对象,大多数从新生代晋升的对象会被移动到这里
- 实现深拷贝
- 循环+递归
- JSON.stringify与JSON.parse结合
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4],
b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);
-
函数库lodash
该函数库有提供 _.cloneDeep 用来做 Deep Copy -
借用JQ的extend
let a=[0,1,[2,3],4],
b=$.extend(true,[],a);
a[0]=1;
a[2][0]=1;
console.log(a,b);
- 防抖节流
防抖是指 一定时间,连续触发,只在最后一次触发
let deBounce = (fn, delay) => {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(()=> {
fn(...args);
}, delay)
}
}
节流是指 时间持续触发时,在一定间隔时间内触发
let throttle = (fn, delay) => {
let flag = true;
return function (...args) {
if (!flag) return;
flag = false;
setTimeout(() => {
fn(...args);
flag = true;
}, delay)
}
}
- 类型计算
- 包含字符串的计算(需注意)
let a= '5' - '3';
let c = '' + 1;
let d= false - true;
let e = null + 1;
let f= [] - [];
let g= [] + [];
console.log(a,b,c,d,e,f,g)
-
类型转成字符串
String(value);
‘’ + value;
value.toString() -
类型转成数字
Number();
‘’ - value; -
== 和 === 使用
除了判断null时使用= = ,其他情况都用 === -
falsely变量和truely变量(一个变量双重否定后是true或false来判定)(误区:if判断,truely变量即进入if,falsely变量则进入else)
-
&& 和 ||
&&返回falsely变量
||返回truely变量
- 数组、字符串一些常用函数:看这里
- 数组去重、扁平化的一些方法
js原型和原型链相关知识(重要)
- 类型判断
- 平常还有哪些其他判断数据类型的方法呢
- typeof: typeof 1
可以判断所有的值类型;可判断函数;可判断是否是引用类型(但不能具体分辨那种类型) - instanceof(原理,判断前者的_proto_原型链上是否存在后者的prototype原型对象)
可判断一个变量是否是Array但是需注意:不能判断简单的数据类型。 还有isArray可以判断是否是数组
[] instanceof Object 结果也是true,所有的引用类型使用instanceof Object都是true - (value).constructor.name,多重继承时会出错,在有原型时出错
- 推荐:object.prototype.toString.call() 返回格式为[Object, 真实类型];(可以检测所有类型,但不能检测自定义的具体类型)
- 推荐:Array.isArray()
- jquery.type
- 原型和原型链(是我看到最清晰的解释了)
闭包(重要)
- es6语法新特性(具体的文章多处地方都提到了,我就不展开说了)
- let、const、var区别
- 模板字符串
- promise
- 增加箭头函数
- Map、Set等新的数据类型
- 解构赋值
- 函数默认参数
- 作用域问题
- 变量提升
var声明的变量作用域为函数作用域,会将变量定义提升至函数作用域顶部,函数声明也会有提升,且函数提升优先级高于变量提升, - 块级作用域
在指定的作用域外无效,位于一对花括号之内,使用let和const声明变量
使用var声明的变量,在其函数体及其嵌套函数体中都可以访问 - 暂时性死区
如下图,从块级作用域顶部到let语句之间的部分称为暂时性死区,如果没有let,则不会形成暂时性死区
大家能准确描述出let、const、var的区别,则就对2中的问题完全掌握
- 自由变量
自由变量的查找,是在函数定义的地方,向上级作用域查找,而不是在调用的地方
- 闭包指访问了其它作用域中的变量的函数
- 闭包的应用(可以类比到vue的data是个函数)
使属性不能直接访问,只能通过get方法获取
- this的应用场景 (this取什么值,是在函数调用的地方确定,而不是在定义的地方确定)(与闭包相反)
- 普通函数调用(window)
- 使用call、bind、apply等(传入什么,this就是什么)
- 作为对象方法调用(对象本身)
- 作为构造函数被调用(调用的实例)
- 箭头函数(没有自己的this,上级作用域的this值)
-
箭头函数与普通函数区别
-
call、bind、apply区别
-
如何判断函数是构造函数还是普通函数
当以构造函数调用时,new.target 等于构造函数本身
异步(重要 )
- promise的链式调用及一些注意点
promise.all和promise.race - require和import区别
- require/exports 是运行时动态加载,import/export 是静态编译,所以import无法在函数内部使用,所以一般都在顶部
- require/exports 输出的是一个值的拷贝,import/export 模块输出的是值的引用
- import会有提升
- 异步使用场景
- async/await
async会返回一个promise对象;
await相当于promise的.then方法,若promise失败,则await不会触发,需要借助于try…catch捕获;
async原理:是一种语法糖,是generator函数和promise共同实现的,因为async最后会返回promise,所以最后肯定后return 一个promise,promise中会递归调用generator函数(由next.done来判断是否结束)
- 执行顺序:对于await后面的内容,都是异步执行(即类似前面提到的event loop中的setTimeout)
- event loop(DOM事件也使用回调,也是基于event loop)
* 浏览器的event loop机制
这里先给大家看个模拟的运行机制吧
再配合下图讲出即可
* node 的event loop 机制
整体来说,两者区别为:
浏览器和 Node 环境下,微任务队列的执行时机不同
Node 端,微任务在事件循环的各个阶段之间执行
浏览器端,微任务在事件循环的 宏任务 执行完之后执行
- 宏任务和微任务区别
宏任务:setTimeout、Ajax、等,发生在DOM渲染之后
微任务:Promise、async/await等,发生在DOM渲染之前 - 宏任务、微任务触发时机解释
- 设计模式
- 设计原则
创建 - 工厂模式:应用场景,创建成功、失败、警告等不同颜色、icon的弹框实例
- 建造函数:应用场景(常见于初始化),编辑器初始化、vue初始化,首先创建一个简单的vue实例,然后把生命周期、事件、数据、模板渲染这些混入其中
- 单例模式:应用场景,vue-router
提高代码复用率 - 享元模式,把公共部分提取出来,共同使用
- 模板方法模式,先实现一个最基础的版本,再针对不同需求在基础版上实现
提高代码可扩展性 - 适配器模式-接口
- 装饰者模式-方法内容