1、深拷贝deepCopy
function deepCopy (obj) {
// 如果obj为空,直接return
if (!obj) return obj
// 如果obj不是对象类型,直接return
if (!obj instanceof Object) return obj
// 如果obj是Function类型,
if (obj instanceof Function) {
return function () {
return obj.apply(this, arguments)
}
}
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
const res = Array.isArray(obj) ? [] : {}
Object.keys(obj).forEach(key => {
if (obj[key] instanceof Object) {
res[key] = deepCopy(obj[key])
} else {
res[key] = obj[key]
}
})
return res
}
2、对象扁平化
function objectFlat (obj) {
let res = {}
function flat (key, value) {
if (Object(value) !== value) {
// 判断是基本数据类型还是引用数据类型
if (key) {
res[key] = value
}
} else if (Array.isArray(value)) {
for (let i = 0; i < value.length; i ++) {
flat(`${key}[${i}]`, value[i])
}
if (value.length === 0) {
res[key] = []
}
} else {
let objArr = Object.keys(value)
objArr.forEach(item => {
flat(key ? `${key}.${item}` : `${item}`, value[item])
})
if (objArr.length === 0 && key) {
res[key] = {}
}
}
}
flat('', obj)
return res
}
3、数组扁平化
// arr = [1,[2,3], [3, [5,6]]]
方法一:
function arrFlat (arr) {
let res = []
arr.forEach(item => {
if (Array.isArray(item)) {
res.push(...arrFlat(item))
} else {
res.push(item)
}
})
return res
}
方法二:
function arrFlat (arr) {
return arr.reduce((res, item) => {
res.concat(Array.isArray(item) ? arrFlat(item) : item, [])
})
}
4、手写Promise
// pending fulfilled reject
// constructor resolve reject then catch
// Promise是一个类
class MyPromise {
// 定义一个构造函数
constructor (func) {
this.status = 'pending'
this.value = null
this.reason = null
// 成功回调的空数组
this.resolvedTasks = []
// 失败回调的空数组
this.rejectedTasks = []
this._resolve = this._resolve.bind(this)
this._reject = this._reject.bind(this)
try {
func(this._resolve, this._reject)
} catch (error) {
this._reject(error)
}
}
_resolve (value) {
this.status = 'fulfilled'
this.value = value
// 循环回调数组,把数组前面的方法弹出来且直接调用
this.resolvedTasks.forEach(t => t(value))
}
_reject (reason) {
this.status = 'reject'
this.reason = reason
// 循环回调数组,把数组前面的方法弹出来且直接调用
this.rejectedTasks.forEach(t => t(reason))
}
// then方法接收两个参数:一个是成功时执行的回调函数,一个是失败时执行的回调函数
then (resolvedTasks, rejectedTasks) {
let promise2 = new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
resolvedTasks(this.value)
} else if (this.status === 'reject') {
rejectedTasks(this.reason)
} else {
this.resolvedTasks.push(resolvedTasks)
this.rejectedTasks.push(rejectedTasks)
}
})
return promise2
}
catch (rejectedTasks) {
return this.then(null, rejectedTasks)
}
}
module.exports = MyPromise
// 测试
new MyPromise((resolve) => {
setTimeout(() => {
resolve(1)
}, 500)
}).then((res) => {
return new MyPromise((resolve) => {
setTimeout(() => {
resolve(2)
}, 500)
})
}).then((res) => {
throw new Error('error')
}).catch((err) => {
console.log('err')
})
5、promise.all方法
// all方法接收一个数组,Promise返回值也是一个Promise对象,如果数组中所有值是成功的,那么then里面就是成功回调,如果有一个值是失败的,那么then里面就是失败的
Promise.all = function (promise) {
return new Promise((resolve, reject) => {
let index = 0; // 声明一个计数,每完成一个Promise就加1
let result = []
if (promise.length === 0) {
resolve(result)
} else {
function processValue (i, data) {
result[i] = data
// 如果计数器和数组长度相同,那说明所有元素都执行完了,可以输出了
if (++index === promise.length) {
resolve(result)
}
}
// 对传递的数组进行遍历
for (let i = 0; i < promise.length; i++) {
// Promise对象就执行then方法,如果是resolve就把值添加到数组中,如果是错误就执行reject返回
Promise.resolve(promise[i]).then((data) => {
processValue(i, data)
}, (err) => {
reject(err)
return
})
}
}
})
}
6、防抖
// 防抖就是延迟执行,就是等待n秒后再去执行。也就是持续触发时不执行,等不触发了一段时间之后再去执行。
function debounce (fn, delay) {
let timer = null
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args) // 使用apply立即执行
}, delay)
}
}
7、节流
// 节流就是间隔执行,就是n秒内只执行一次。持续触发时,将开关关闭,等时间到了,再将开关打开。
function throttle (fn, delay) {
let canRun = true
return function (...args) {
if (!canRun) return
canRun = false
setTimeout(() => {
fn.apply(this, args)
canRun = true
}, delay)
}
}
8、call
// call是立即执行
Function.prototype.call = function (context) {
const cxt = context || window;
// 将当前被调用的方法定义在cxt.func上,为了能以对象调用的形式绑定this
cxt.func = this;
// 获取实参,入参是(对象,参数列表),slice(1)是为了获取参数列表
const args = Array.from(arguments).slice(1);
// 以对象调用的形式调用func,此时this指向cxt,也就是传入的需要绑定的this指向
const res = arguments.length > 1 ? cxt.func(...args) : cxt.func();
// 删除该方法,不然会对传入对象造成污染(添加该方法)
delete cxt.func;
return res;
};
9、apply
// apply是立即执行
Function.prototype.apply = function (context) {
const cxt = context || window;
// 将当前被调用的方法定义在cxt.func上,为了能以对象调用的形式绑定this
cxt.func = this;
// 获取实参
const res = arguments[1] ? cxt.func(...arguments[1]) : cxt.func();
// 删除该方法,不然会对传入对象造成污染(添加该方法)
delete cxt.func;
return res;
};
10、bind
// bind是绑定一个对象指向当前函数,以便稍后调用。方法.bind(对象)
Function.prototype.bind = function (context) {
// 对context进行深拷贝,防止污染
const cxt = JSON.parse(JSON.stringify(context)) || window;
// 将当前被调用的方法定义在cxt.func上,为了能以对象调用的形式绑定this
cxt.func = this;
// 获取实参
const args = Array.from(arguments).slice(1);
// bind返回一个绑定函数,等待调用
const newFunc = function () {
// 这里需要注意一点的是需要对bind函数的实参和返回的绑定函数的实参进行参数合并,调用时传入!
const allArgs = args.concat(Array.from(arguments));
// 以对象调用的形式调用func,此时this指向cxt,也就是传入的需要绑定的this指向
// if (this instanceof newFunc) {
// cxt.func.apply(this, newFunc)
// } else {
// cxt.func.apply(cxt, allArgs)
// }
return allArgs.length > 0 ? cxt.func(...allArgs) : cxt.func();
};
return newFunc
};
11、new操作
(1) 创建一个新对象;
(2) 将构造函数中的this指向该对象
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象。
function new (obj, ...args) {
// 基于obj的原型创建一个新对象
const newObj = Object.create(obj.prototype)
// 添加新的属性到newObj上,并获取obj函数的结果
const res = obj.apply(newObj, args)
// 如果执行结果有返回值并且是一个对象,则返回执行的结果,否则,返回新创建的对象
return typeof res === 'object' ? res : newObj
}
12、二叉树层序遍历(广度遍历)
/**
* params {TreeNode} = root
* 先遍历根节点的相邻节点,再一次遍历相邻节点的子节点,用队列来存储当前
* 层的节点数,遍历当前层的节点,将当前层节点依次推入subRes[],
*再将其子节点依次推入,然后进入下一层遍历,直到遍历完整棵树。
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
// 非递归遍历
function levelOrder (root) {
if (!root) return []
let res = []
let queue = [root]
// 当前层的节点数目>0
while (queue.length > 0) {
let subRes = []
for (let i = 0; i < queue.length; i++){
let node = queue.shift() // 节点出列
subRes.push(node.value) // 将当前层的节点值加入subRes数组中
// 将下一层节点计入队列中
if (node.left) {
subRes.push(node.left)
}
if (node.right) {
subRes.push(node.right)
}
}
res.push(subRes)
}
return res
}
// 递归遍历
function levelOrder (root) {
if (!root) return []
let res = []
let stack = [tree] // 先将要遍历的树压入栈
let count = 0 // 用来记录执行到第一层
function bfs () {
let node = stack[count]
if (node) {
res.push(node.value)
if (node.left) res.push(node.left)
if (node.right) res.push(node.right)
count++
bfs()
}
}
return res
}
13、二叉树深度遍历
// 二叉树的结构
var root = {
value: '-',
left: {
value: '+',
left: {
value: 'a'
},
right: {
value: 'b'
}
},
right: {
value: '*',
left: {
value: 'd'
},
right: {
value: 'e'
}
}
}
// 前序遍历:根 - 左 - 右
// 非递归方法
// 利用栈:将遍历到的结点都依次存入栈中,拿结果时从栈中访问
function preDeepOrder (root) {
if (!root) return []
let res = []
let stack = [] // 初始化一个栈,将根节点压入栈中;
while (root || stack.length > 0) {
while (root) {
res.push(root.val)
stack.push(root)
root = root.left
}
if (stack.length > 0) {
root = stack.pop()
root = root.right
}
}
return res
}
// 递归的方法
// 先遍历根结点,将值存入数组,然后递归遍历:先左结点,将值存入数组,继续向下遍历;直到(二叉树为空)子树为空,则遍历结束;
然后再回溯遍历右结点,将值存入数组,这样递归循环,直到(二叉树为空)子树为空,则遍历结束。
function preDeepOrder (root) {
if (!root) return []
let res = []
function preOrder (root) {
if (root) {
res.push(root.val)
preOrder(root.left)
preOrder(root.right)
}
}
preOrder(root)
return res
}
// 中序遍历:左 - 根 - 右
// 非递归方法
function middleDeepOrder (root) {
if (!root) return []
let res = []
let stack = []
while (root || stack.length > 0) {
while (root) {
stack.push(root)
root = root.left
}
if (stack.length > 0) {
root = stack.pop()
res.push(root.val)
root = root.right
}
}
}
// 递归方法
function middleDeepOrder (root) {
if (!root) return []
let res = []
function middleOrder (root) {
if (root) {
middleOrder(root.left)
res.push(root.val)
middleOrder(root.right)
}
}
middleOrder(root)
return res
}
// 后序遍历:左 - 右 - 根
// 非递归方法
// 先把根结点和左树推入栈,然后取出左树,再推入右树,取出,最后取根结点。
function postDeepOrder (root) {
if (!root) return []
let node = root
let lastVisit = root
let res = []
let stack = []
while (node || stack.length > 0) {
while (node) {
stack.push(node)
node = node.left
}
node = stack[stack.length - 1]
if (!node.right || node.right === lastVisit) {
res.push(node.val)
stack.pop()
lastVisit = node
node = null
} else {
node = node.right
}
}
return res
}
// 递归方法
function postDeepOrder (root) {
if (!root) return []
let res = []
function postOrder (root) {
if (root) {
postOrder(root.left)
postOrder(root.right)
res.push(root.val)
}
}
postOrder(root)
return res
}
14、instanceof
function isInstanceOf (instance, obj) {
let proto = instance.__proto__
let prototype = obj.prototype
while (true) {
if (proto === null) return false
if (proto === prototype) return true
proto = proto.__proto__
}
}
15、ES5实现继承