JS基础
什么是事件委托
- 概念:子元素委托事件给父元素去处理;
- 好处:节约内存+方便管理+防内存泄漏;
- 节约内存:事件监听器的数量少;
- 方便管理:无需考虑子元素动态增减时事件监听器的动态增减问题;
- 防内存泄露:子元素被删除时无需删除其对应的事件监听器;
- PS:追问=哪些情形会造成内存泄露?
ES6有哪些新特性?
变量层面:
- let:var弊病=变量提升+重复声明+作用域穿透;
- const:通常将不希望修改地址的对象声明为常量;
数据结构层面
- 数组:解构+展开;
- 对象:解构+展开+简写;
- 字符串:模板字符串;
- Map:方便增删改产的键值对存储器;
- Set: 有序且不重复的元素集;
函数层面
- 箭头函数:this从父级作用域继承;
- PS:this的可能情形;
类层面
- class系列语法:class(构造函数+原型属性) + constructor(构造函数) + static静态成员 + super调用父类方法;super(name)调用父类构造器; super.xxx()调用父类实例方法;
什么是深拷贝/浅拷贝
- 浅拷贝:引用数据类型赋值给别人(包括调用函数时实参给形参赋值)= 地址被拷贝 = 修改任何副本都会影响其它副本;
- 浅拷贝示例
const obj2 = obj或fn(obj) - 深拷贝:将对象中的所有key-value递归拷贝给另外一个对象,递归到每一个叶子结点(即value是基本数据类型或函数),深拷贝的副本修改相互不影响;
什么是值传递/引用传递
- 值传递:基本数据类型赋值给别人,包括调用函数时作为入参传递;
- 引用传递:引用数据类型赋值给别人,包括调用函数时作为入参传递;
- 值传递示例
let num2 = num或fn(num) - 引用传递示例
let obj2 = obj或fn(obj)
谈谈对事件传播/派发机制的理解
- 解决事件发生时具体由谁消费的问题;
- 具体解决方案可从三方面入手,即:指定传播方向 + 按需设置一级到多级监听器 + 设置传播断点;
- 指定传播方向:dom.addEventListener(type,handler,useCapture)最后一个入参true即使用捕获,否则使用冒泡,默认冒泡;
- 设置断点:e.stopPropagation()
内存泄露的可能情形有哪些?
- 用完的事件监听器:DOM元素删除时忘记删除其事件监听器;
- 用完的事件监听器:离开页面时没有释放各种事件监听器;(div.onclick=null,div.removeEventListener...)
- 用完的定时器:定时器用完没有clear;
- 用完的全局变量:用完没释放的全局变量(obj=null)
- 用完的闭包:用完未释放的闭包(innerFn = null)
- 总结:【用毕未释放】的【事件监听器】+【定时器】+【全局变量】+【闭包】;
谈谈对闭包的理解
- 广义闭包:凡返回引用数据的函数,在其返回值被引用期间,其执行空间无法被释放,客观上形成一个临时存在的封闭作用域,即函数闭包;
- 狭义闭包:即返回函数的函数,外层函数通常作为内层函数的状态缓存区;
- 函数式编程:闭包是函数式编程的基础,可以派生出诸多玩法(这里可以随便举几个案例)
- 释放闭包:闭包会导致算法的时间和空间开销增大,用完后一定要释放,否则会形成内存泄露;
- 释放方式:
let ret = enclosure();用完后ret=null,无人引用闭包的返回值时闭包就能释放了;
深拷贝 VS 浅拷贝
- 浅拷贝:复制地址+相互影响;
const objCopy = obj - 深拷贝:递归拷贝对象的所有key-value + 直到叶子结点 + 互不影响;
const objCopy = deepCopy(obj)
值传递 VS 引用传递
- 值传递:基本数据类型(number,string,boolean,null,undefined) + 赋值给别人 + 值拷贝 + 互不影响;
const numCopy = num;fn(num)基本数据类型的实参传递给形参走的是值传递/值拷贝; - 引用传递:引用数据类型 + 赋值给别人(包括实参给形参赋值)+ 地址的浅拷贝 + 相互影响
const objCopy = objfn(obj)
前端有哪些数据存储方式
- cookie: 4K级别 + 键值存储 + 主要用于前后端共享用户信息;
- localStorage/sessionStorage: 50M级别 + 键值存储 + 前端独享 + 缓存业务数据(以减少网络请求次数);
@面向对象
this的所有可能情形
- 普通函数的this就是调用者;(显式主语+无主语时为window+DOM事件监听器的主语恒为事件源e.currentTarget)
- 箭头函数的this从父级作用域继承;
- 普通函数可以通过 fn.call/apply/bind 绑定this
- 构造器里的this为正在构建的实例
- 实例方法中的this为当前实例
- 静态方法中的this为当前类
typeof 与 instanceof 区别
- typeof主要用于判断基本数据类型,对于引用数据类型只能识别为funciton或object
- instanceof判断某个对象是否是某类的实例或后代实例
- zhangsan instanceof Person或(Animal,Object)结果都是true
JS如何实现继承
- 以父类实例作为子类原型
new操作符具体干了什么
const zhangsan = new Person('张三',18)
- 提前构造一个默认的返回值p
- 将构造方法Person中的this绑定为这个p对象
- 最终将这个p对象返回
Map与Object的区别
- 本质上都是key-value管理数据
- Map是ES6新增的数据结构,主要就是在Object的基础上封装除了几个便捷API,以做到和其它语言接轨(很多其它语言中都有Map这种数据结构)
- 这些便捷API包括
map.set(key,value),let value = map.get(key),map.delete(key),map.size等 - 一个简单的MyMap封装
class MyMap {
constructor(){
this.dataObj = {}
this.size = 0
}
set(key,value){
if(!this.dataObj.hasOwnProperty(key)){
this.size ++
}
this.dataObj[key] = value
}
get(key){
return this.dataObj[key]
}
delete(key){
delete this.dataObj[key]
this.size --
}
}
const mm = new MyMap()
mm.set("name","OOP小菜鸡")
console.log(mm.get("name"));
console.log(mm);
@异步
Promise与asycn,await的区别
- await是promise.then的语法糖
//ajaxApi(url,parmas)的返回值是promise对象
//阻塞等待promise对象resolve数据出来
const data = await ajaxApi(url,parmas)
- await关键字所在的函数必须声明为异步函数
async function fn(){//内含await}
async ()=>{//内含await}
const fn = async ()=>{//内含await}
- promise.catch由await所在的代码块进行try-catch
async fn(){
try{
// ajaxApi(url,parmas)的返回值是promise对象
// 如果promise毁约(reject),则错误信息由try-catch的catch获得
const data = await ajaxApi(url,parmas)
}catch(err){
//此处的err即promise所reject出的错误信息
hanleErr(err)
}
}