模块化
模块化将复杂的程序拆分为一个个独立的模块,有利于重用和维护
参考
闭包以及使用场景
闭包:可以访问并维护其他函数内部变量的函数
使用场景:回调传参,setTimeout传参,ES6模块(类似,不确定实现原理,无法肯定),结果缓存
类和继承
ES5:
属性定义:构造函数
方法定义:原型链
继承:寄生组合继承
function inherit(father, child) {
Object.setPrototypeOf(child.prototype, father.prototype)
}
ES6:
class + extends
原型链终点
Function.__proto__ === Function.prototype
Object.prototype = {}
Eventloop
先取微任务队列,再取宏任务队列,每次操作都是这个步骤,至两个队列全部处理完成
注:process.nextTick
> promise.then
new 操作符
创建空对象,设置对象的原型为构造函数的原型,执行构造函数,返回对象
this指向
- 正常调用默认指向全局
- 前缀调用指向紧跟的前缀对象
call
,apply
,bind
指向传参new
指向返回实例- 箭头指向所在环境外层,且打破上述所有规则
节流和防抖
节流:规定间隔内只触发一次方法
防抖:连续触发只触发最后一次
垃圾回收
标记清除:可达性算法,第一次标记,第二次清除未标记的变量
引用计数:清除引用数为0的变量
Ajax防止浏览器缓存
Ajax.setRequestHeader("If-Modified-Since","0")
Ajax.setRequestHeader("Cache-Control","no-cache")
在URL后面加上一个数
"nowtime=" + new Date().getTime()
"fresh=" + Math.random()
事件流
事件流:
捕获阶段 => 目标阶段 => 冒泡阶段
addEventListener
接收3个参数,要处理的事件名,回调函数,是否在捕获阶段处理事件
DOM 0
级事件:onclick
监听,无法定义多个监听,后者覆盖前者
DOM 2
级事件:addEventListener
监听,可以定义多个监听,同一事件下的同一函数只能被监听一次
先冒泡后捕获:通过分别监听捕获和冒泡事件,捕获时展缓执行,直到冒泡事件执行后再执行捕获事件
事件委托
在事件触发目标的父节点上监听事件,通过事件冒泡来触发事件处理程序,从而统一管理同类事件
数组去重
indexOf
循环去重Set()
Object
属性
跨域
箭头函数
- 简化写法,绑定
this
- 没有自己的
this
,arguments
,super
或new.target
- 不能作为构造函数,用
new
会报错 - 没有
prototype
webpack用来干什么的
webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle
什么是按需加载
当用户触发了动作时才加载对应的功能。触发的动作,是要看具体的业务场景而言,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更改等。加载的文件,可以是JS、图片、CSS、HTML等。
说一下什么是virtual dom
用JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异 把所记录的差异应用到所构建的真正的DOM树上,视图就更新了。Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存
typeof,instanceof,isPrototypeOf 的区别
typeof
:检查参数的类型,值为JS
基本数据类型中的一个,null
为object
instanceof
:检查对象与类的关系,检查右参数的原型是否处于左参数的原型链中
isPrototypeOf
:检查对象与对象的关系,检查调用对象是否处于传参对象的原型链中
预加载和懒加载的区别
预加载:提前加载图片,用户查看时可以直接从缓存读取
懒加载:展缓加载图片,用户需要查看时再进行加载
两种行为相反,一个是优化用户体验,增加服务器压力。一个是优化页面性能,减轻服务器压力
mouseover和mouseenter的区别
mouseover
:鼠标移入目标元素会触发,在子元素以及目标元素之间移入或移出都会再次触发
mouseenter
:鼠标移入目标元素时触发,在子元素以及目标元素之间移入或移出不会再次触发
bind,apply,call的区别
都是用于修改函数内部this
的指向
bind
使用目标函数生成一个新函数
apply
立即执行目标函数,传参方式为数组
call
立即执行目标函数,传参方式为顺序传参
JS位置的区别
clientHeight
:可视区域高度,不包含边框和滚动条
offsetHeight
:可视区域高度,包含边框和滚动条
scrollHeight
:所有区域高度,包含因滚动隐藏部分
scrollTop
:滚动后被隐藏的高度
offsetTop
:当前元素距offsetParent顶部内边距的距离,offsetParent
为最近具有定位属性的元素,否则为body
。当为body
时,包含边框和外边距。
== 和 ===、以及Object.is的区别
==
:类型转换后比较值
===
: 比较类型、值,引用数据类型则比较地址,NAN
不等于自身
Object.is
:除了NAN
等于自身,+0 !== -0
以外,其他与===
行为一致
定时器和requestAnimationFrame的区别
setTimeout
:规定时间后触发回调,只触发一次
setInterval
:规定时间后触发回调,触发多次
requestAnimationFrame
:解决定时器触发动画时间不准确的问题,由浏览器内部根据显示器刷新频率自动调用回调并优化调用
由于setInterval
自身的缺陷,使用setTimeout
实现setInterval
的功能可以保证两次调用间隔时间和每次回调不会被取消
实现深拷贝函数
function deepCopy(obj) {
if (obj && typeof obj === 'object') {
let res = obj instanceof Array ? [] : {
}
for (let item of Object.keys(obj)) res[item] = deepCopy(obj[item])
return res
}
return obj
}
实现once函数
function once(fn, ...rest) {
let lock = 0
return function () {
if (!lock++) return fn(...rest)
}
}
实现属性监听
let obj = {
_name: 66
}
Object.defineProperty(obj, 'name', {
set(v) {
this._name = v
},
get() {
return this._name
}
})
let obj = new Proxy({
}, {
set(target, p, value) {
target[p] = value
},
get(target, p) {
return target[p]
}
})
实现私有变量
let obj = {
getName: (function () {
let name = 'Soraka'
return function () {
return name
}
})()
}
实现bind函数
Function.prototype.Bind = function (target) {
let that = this
return function (...rest) {
that.apply(target, rest)
}
}
实现全排列
function permutate(str) {
if (str.length === 1) return [str]
let res = []
for (let i = 0; i < str.length; i++) {
let list = permutate(str.substring(0, i) + str.substring(i + 1))
list.forEach(val => res.push(str[i] + val))
}
return res
}