题目1:typeof
基础类型 null,undefined,number,string,boolean,
function,
object
题目2:闭包
// 函数作为返回值
function create(){
let a = 100
return function(){
console.log(a)
}
}
len fn = create()
fn()
// 函数作为参数
function print(fn){
fn()
}
let a =100
function fn(){
console.log(a)
}
print(fn)
// 概念
函数或一个封闭的作用域代码执行时,其内部声明的函数或对象,通过传参,return等方式,直接或间接的暴露到该作用域外。
当函数或该封闭作用域的代码执行完后,暴露出去的函数或对象依旧持有对该作用域的引用,该作用域并未销毁
题目3:bind
// 实现
Function.prototype.bind = function(...args){
const oThis = args.shift()
const selfFn = this
const Fn = function(...args1){
let flag = this instance of Fn
if(flag){
return selfFn.apply(this,[...args,...args1])
}
return selfFn.apply(oThis,[...args,...args1])
}
Fn.prototype = Object.create(selfFn.prototype)
return Fn
}
题目4:document.createDocumentFragment()
const frag = document.createDocumentFragment()
frag.appendChild(dom)
dom.appendChild(frag)
题目4 jsonp封装
function jsonp(url, data={}) {
const p1 = new Promise((resolve, reject) => {
let fnName = "KKB_"+Math.random().toString().substr(2);
window[fnName] = (data) => {
// window[fnName] = null // 销毁?
resolve(data);
}
let script = document.createElement('script')
let query = parse(data)
script.src = `${url}?callback=${fnName}&${query}`
script.onerror = () => reject('加载失败')
script.onload =() => {
document.body.removeChild(script)
}
document.body.appendChild(script)
})
const p2 = new Promise(()=>{
setTimeout(()=>{
reject()
},50000)
})
return Promise.race([p1,p2])
}
题目5:ajax
function ajax(options){
const xhr = new XMLHttpRequest() // ie兼容
const {method,url,data} = options
if(method === 'GET'){
const url =`${url}?${parse(data)}`
xhr.open('GET', url, async = true)
}else if(method === 'post'){
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xhr.open('POST', url ,async)
xhr.send(qs.stringify(data))
}
xhr.onreadystatechange = ()=>{
if(xhr.readyState ===4){
if([200,206,304].includes(xhr.status)){
options.success(xhr.responseText)
}
}
}
}
题目5:cookie
// 缺点
1.存储大小4kb
2. http请求发送到服务端,增加请求数据量
3. 只能用document.cookie ,服务端Set-Cookie
// 属性
domain
path
Expires/Max-age
HttpOnly
// 跨子域
子域设置cookie Set-Cookie的domain为上级域名
题目6:window.onload和DOMContentLoaded
window.addEventListener('load',function(){
// 页面的全部资源加载完才执行,包括图片,视频等
})
document.addEventListener('DOMContentLoaded',function(){
// Dom渲染完即可执行,此时图片,视频还可能没加载完
})
题目7:性能优化
// 让加载更快
// 让渲染更快
# css放head,js放body最下面
// 避免先渲染一遍默认样式,再解析css又渲染一遍
// js放在head里面,会堵塞DOM的生成。使用就无法获取通过选择器获取DOM元素进行操作
// 首屏优化
# 尽早开始执行js, 用DOMContentLoaded触发
# 懒加载
# 节流,防抖
# 合并dom操作执行,
# dom查询缓存
# 服务端渲染ssr
题目8:防抖和节流
// 防抖:用户输入结束或暂停时,才触发事件
function debounce(fn,delay=500){
let timer = null;
return function(...args){
if(timer){
clearTimeout(timer)
}
timer=setTimeout(()=>{
fn.apply(this,args)
timer = null
},delay)
}
}
// 节流:无论拖拽速度多快,都会每隔100ms触发一次
function throttle(fn,delay=500){
let timer = null
return function(...args){
if(timer) return
timer = setTimeout(()=>{
fn.apply(this,args)
timer=null
},deley)
}
}
题目9: 数组扁平化
function flat(arr){
const isDeep=arr.some(item=>item instanceof Array)
if(!isDeep){
return arr
}
const res = Array.prototype.concat.apply([],arr)
return flat(res)
}
题目10:手写深度拷贝
function deepClone(obj={}){
if(typeof obj !== 'object' || obj == null){
return obj
}
let result
if(obj instanceof Array){
result = []
} else {
result = {}
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
result[key] = deepClone(obj[key])
}
}
return result
}
题目11: requestAnimationFrame
// 介绍
1. 要想动画帧流畅,更新频率要60帧/s,即16.67ms更新一次视图
2. RAF自动控制频率
3. 后台标签或隐藏iframe中,RAF会暂停
// demo
function animate(){
curWidth += 3
dom.css('width',curWidth)
if(curWidth < maxWidth){
window.requestAnimationFrame(animate) // 循环
}
}
animate()
题目12:手写isEqual
function isObject(obj){
return typeof obj === 'object' && obj!==null
}
function isEqual(obj1,obj2){
if(!isObject(obj1)&&!isObject(obj2)){
return obj1 === obj2
}
if(obj1 === obj2) return true
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if(obj1keys.length !== obj2keys.length) return false
for(let key in obj1){
const res = isEqual(obj1[key],obj2[key])
if(!res) return false
}
return true
}
题目13:手写promise
// https://zhuanlan.zhihu.com/p/144058361
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
var _this = this
this.state = PENDING; //状态
this.value = undefined; //成功结果
this.reason = undefined; //失败原因
this.onFulfilled = [];//成功的回调
this.onRejected = []; //失败的回调
function resolve(value) {
if(_this.state === PENDING){
_this.state = FULFILLED
_this.value = value
_this.onFulfilled.forEach(fn => fn(value))
}
}
function reject(reason) {
if(_this.state === PENDING){
_this.state = REJECTED
_this.reason = reason
_this.onRejected.forEach(fn => fn(reason))
}
}
try {
executor(resolve, reject);
}
catch (e) {
reject(e);
}
}
Promise.prototype.then = function (onFulfilled, onRejected){
//_this是promise1的实例对象
var _this = this;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
var promise2 = new Promise((resolve, reject) => {
if (_this.state === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(_this.value);
resolvePromise(promise2, x, resolve, reject);
}
catch (error) {
reject(error)
}
});
}
else if (_this.state === REJECTED) {
setTimeout(()=>{
try {
let x = onRejected(_this.reason);
resolvePromise(promise2, x ,resolve, reject);
}
catch (error) {
reject(error);
}
});
}
else if(_this.state === PENDING) {
_this.onFulfilled.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(_this.value);
resolvePromise(promise2, x, resolve, reject);
}
catch (error) {
reject(error);
}
});
});
_this.onRejected.push(() => {
setTimeout(() => {
try {
let x = onRejected(_this.reason);
resolvePromise(promise2, x ,resolve, reject);
}
catch (error) {
reject(error);
}
})
});
}
});
return promise2;
}
function resolvePromise(promise2, x, resolve, reject){
if (promise2 === x) {
reject(new TypeError('Chaining cycle'))
}
if (x && typeof x === 'object' || typeof x === 'function') {
let used;
try {
// 对于在then里头,return 一个新的promise或thenable那种
// 调用x.then拿到其返回值,传给resolvePromise
let then = x.then
if (typeof then === 'function') {
then.call(
x,
y => {
if (used) return;
used = true
resolvePromise(promise2, y, resolve, reject)
},
r =>{
if (used) return;
used = true;
reject(r);
}
);
}
else {
if (used) return;
used = true;
resolve(x);
}
}
catch(e) {
if (used) return;
used = true;
reject(e);
}
}
else {
resolve(x);
}
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加静态resolve方法
static resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
// 添加静态all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
})
}
})
}
// 添加静态race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
}
}
题目14: Promise
参考 Promise
// Promise.race
// Promise.all
function all(arr){
return new Promise((resolve,reject)=>{
let isComplete = false;
const resolveDataList = []
const onFullfilled = (data ,i )=>{
if(isComplete)return
resolveDataList[i] = data
if(resolveDataList.length===arr.length){
isComplete= true
resolve(resolveDataList)
}
}
const onRejected = reason =>{
if(isComplete)return
isComplete= true
reject(reason)
}
arr.forEach((promise,index)=>{
promise.then(
data => onFullfilled(data,index),
onRejected
)
})
})
}
// Promise.appSettled
if (!Promise.allSettled) {
Promise.allSettled = function (promises) {
return new Promise(resolve => {
const data = [], len = promises.length;
let count = len;
for (let i = 0; i < len; i += 1) {
const promise = promises[i];
promise.then(res => {
data[i] = { status: 'fulfilled', value: res };
}, error => {
data[i] = { status: 'rejected', reason: error };
}).finally(() => { // promise has been settled
if (!--count) {
resolve(data);
}
});
}
});
}
}
// Promise.fail
// Promise.any 有一个resolve或者所有都reject
function any(arr) {
return new Promise((resolve, reject) => {
let isComplete = false;
const rejectDataList = new Array(arr.length).fill(undefined);
const onFullfilled = data => {
if (isComplete) {
return;
}
isComplete = true;
resolve(data);
};
const onRejected = (reason, i) => {
if (isComplete) {
return;
}
rejectDataList[i] = reason;
if (rejectDataList.every(item => item !== undefined)) {
reject('AggregateError: All promises were rejected');
}
}
arr.forEach((promise, index) => {
promise.then(
onFullfilled,
reason => {onRejected(reason, index);}
);
});
});
}
题目15:柯里化
参考 柯里化
// 参数复用(闭包)
function curry(x){
return function(y){x+y}
}
// 提前确认
// 延迟执行
let fn = add(1)(2)(3) // 收集参数
fn() // 执行
function curry(fn){
let args = []
return function cb(...args1){
if(args1.length < 1){
return fn(...args)
}
args=[...args,...args1]
return cb
}
}
// apply实现
Function.prototype.apply = function(oThis, ...args){
oThis.FN = this
// apply
const res = oThis.FN(...args)
delete oThis.FN
return res
// bind
return (...args1)=>{
const res = oThis.FN([...args, ...args])
delete oThis.FN
return res
}
}
题目16:前端模块化
- CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案。
- AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅。
- CMD规范与AMD规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在Node.js中运行。不过,依赖SPM 打包,模块的加载逻辑偏重
- **ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案**
题目17:深拷贝循环应用如何处理
参考 深拷贝循环引用
// WeakMap
// Set
题目18:垃圾回收机制
参考 垃圾回收
// 内存泄漏
# 程序中己动态分配的[堆内存]由于某种原因程序未释放或无法释放引发的各种问题(变慢,崩溃,延迟大)
# 原因
全局变量,定时器,闭包,dom 清空时,还存在引用
// 垃圾回收机制
(自动)不停歇的寻找这些不再使用的变量,并且释放掉它所指向的内存
// 两种方式
# 标记清除
将垃圾回收过程分为 标记 和 清除 两个阶段。在标记阶段,从根对象(全局对象)出发遍历所有对象,将所有可达对象 做上标记。在清除阶段,同样会遍历所有对象,对没有标记的对象进行清除操作
当变量进入执行环境(声明变量)的时候,垃圾回收器将该变量进行了标记,当该变量离开环境的时候,将其再度标记,随之进行删除
# 引用计数
跟踪某一个值得引用次数,当引用次数为0时,变进行回收
// 两个概念
# 根对象
# 可达对象 (从对象出发,能够通过层层引用被访问到的对象)