手撕JavaScript代码
- 实现 new 关键字
- 实现 instanceof 关键字
- 实现 call() && apply()
- 实现 bind()
- 实现 map() && reduce()
- 实现 throttle() && debounce()
- 用 setTimeout() 实现 setInterval()
- 实现 Promise.retry()
- 实现 Promise.all()
- 实现 Promise.race()
- 实现 懒惰计算(Lazy)
- 实现 数据类型判断
- 实现 对象扁平化
- 实现 扁平转对象
- 实现 数组扁平化
- 实现 对象深拷贝
- 实现 函数柯里化
- 实现 数组 转 树结构
- 实现 XML转JSON对象
- 实现 EventEmitter
- 实现 JSONP
- 二叉树的遍历
- 快速排序
- 排列组合公式
实现 new 关键字
const New = function(fn, ...args) {
// 创建空对象
let obj = {};
// 空对象的隐式原型连接构造函数的显式原型
obj.__proto__ = fn.prototype;
// 构造函数的this指向空对象并执行
let result = fn.apply(obj, ...args);
// 结果不是对象就返回空对象
return result instanceof Object ? result : obj;
}
实现 instanceof 关键字
function Instanceof(obj, Fn) {
// 构造函数必须是函数类型
if (typeof Fn !== 'function') throw new TypeError('不是函数');
// 实例必须是对象或函数
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) return false;
// Fn的prototype原型
let proto = Fn.prototype;
while (obj.__proto__) {
if (obj.__proto__ === proto) return true;
// 原型链查找
obj = obj.__proto__;
}
return false;
}
实现 call() && apply()
Function.prototype.call = function(context, ...args) {
// 传入对象为空则默认为window
context = context || window;
// obj2上保存obj1函数,可以用Symbol创建唯一属性
context.fn = this;
// 在obj2中执行obj1函数,obj1中的this指向obj2
const result = context.fn(...args);
Reflect.deleteProperty(context, 'fn');
// 返回结果
return result;
}
Function.prototype.apply = function(context, args=[]) {
// 传入对象为空则默认为window
context = context || window;
// obj2上保存obj1函数,可以用Symbol创建唯一属性
context.fn = this;
// 在obj2中执行obj1函数,obj1中的this指向obj2
const result = context.fn(...args);
Reflect.deleteProperty(context, 'fn');
// 返回结果
return result;
}
实现 bind()
Function.prototype.bind = function(context, ...arg1) {
// this是obj1,context是obj2
const self = this;
// 闭包一个函数
const res = function(...arg2) {
// self是obj1,this是res的调用者,context是obj2,res.prototype是obj1的原型
// 当new res()时,res的调用者是隐式原型为obj1原型的对象
// 如果调用者的隐式原型与obj1的显示原型一样,说明是res做构造函数使用,obj1的this指向调用者
// 否则将obj1的this指向obj2
return self.apply(this.__proto__ === res.prototype ? this: context, [...arg1, ...arg2]);
}
// this是obj1,obj1与res的原型连接
if (this.prototype) res.prototype = this.prototype;
return res;
}
实现 map() && reduce()
Array.prototype.map = function(fn) {
let arr = []
for (let i = 0; i < this.length; i++) {
arr.push(fn(this[i], i, this));
}
return arr;
}
Array.prototype.reduce = function(fn, initVal) {
let res = initVal ? initVal : this[0]
for (let i = initVal ? 0 : 1; i < this.length; i++) {
res = fn(res, this[i], i, this);
}
return res;
}
实现 throttle() && debounce()
function throttle(fn, delay, ...args) {
let canRun = true;
// 闭包
return function() {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, args);
canRun = true;
}, delay);
};
}
function debounce(fn, delay, ...args) {
let timer = null;
// 闭包
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
用 setTimeout() 实现 setInterval()
// 储存定时器ID
const timer = {};
function setInterval(fn, delay, ...args) {
timer.id = setTimeout(() => {
clearTimeout(timer);
fn.apply(this, args);
setInterval(fn, delay);
}, delay);
}
// 关闭定时器
clearTimeout(timer.id);
实现 Promise.retry()
Promise.prototype.retry = function(fn, times, delay) {
return new Promise((resolve, reject) => {
const action = () => {
// 执行异步操作
fn()
.then(res => resolve(res))
.catch(err => {
if (times <= 1) reject(err);
else {
times--;
setTimeout(() => action(), delay);
}
})
};
action(resolve, reject);
})
}
实现 Promise.all()
Promise.prototype.all = function(promises) {
return new Promise((resolve, reject) => {
const len = promises.length;
const p = new Array(len);
let count = 0;
for (let i = 0; i < len; i++) {
// 转成Promise对象
Promise.resolve(promises[i])
.then(res => {
p[i] = res;
count++;
if (count === len) resolve(p);
})
.catch(err => reject(err))
}
})
}
实现 Promise.race()
Promise.prototype.race = function(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
// 转成Promise对象
Promise.resolve(promises[i])
.then(res => resolve(res))
.catch(err => reject(err))
}
})
}
实现 懒惰计算(Lazy)
function Lazy(num) {
this.fns = [];
this.res = num;
this.f = null;
// setTimeout(() => this.next(), 0);
}
Lazy.prototype = {
add(num) {
const fn = () => {
this.res += num;
this.f?.(this.res);
this.next();
};
this.fns.push(fn);
return this;
},
tap(f) {
this.f = f;
const fn = () => this.next();
this.fns.push(fn);
return this;
},
delay(time) {
const fn = () => {
setTimeout(() => this.next(), time);
};
this.fns.push(fn);
return this;
},
next() {
const fn = this.fns.shift();
fn?.();
},
};
const lazy = (num) => new Lazy(num);
const lazyCalc = lazy(2).add(2).delay(1000).add(3).tap(console.log);
lazyCalc.next();
实现 数据类型判断
function getType(obj) {
const map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Function]': 'function',
};
if (obj instanceof Element) return 'element';
return map[Object.prototype.toString.call(obj)];
}
实现 对象扁平化
function flatten(obj) {
let result = {}
const process = (key, value) => {
// 基础数据类型: String, Number, Boolean
if (Object(value) !== value) {
if (value !== null && value !== undefined) result[key] = value;
}
// 引用数据类型: Array
else if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
process(`${key}[${i}]`, value[i]);
}
if (value.length === 0) result[key] = [];
}
// 引用数据类型: Object
else {
const keys = Object.keys(value);
keys.forEach(item => process(key ? `${key}.${item}` : `${item}`, value[item]));
if (keys.length === 0 && key) result[key] = {};
}
}
process('', obj);
return result;
}
实现 扁平转对象
function process(obj) {
const keys = Object.keys(obj);
const res = {};
for (const key of keys) {
const chain = key.split(".");
let temp = res;
for (let i = 0; i < chain.length; i++) {
const k = chain[i];
temp[k] = temp[k] ?? (i === chain.length - 1 ? obj[key] : {});
temp = temp[k];
}
}
return res;
}
实现 数组扁平化
function flatten(arr) {
let res = []
arr.forEach(item => {
if (Array.isArray(item)) res = res.concat(flatten(item));
else res.push(item);
})
return res;
}
实现 对象深拷贝
①遇到函数时,可以调用toString()
方法获取字符串表示,并且eval()
还原来实现深拷贝。
function deepClone(obj) {
let copy = Array.isArray(obj) ? [] : {};
if (typeof obj === "object") {
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (obj[key] && typeof obj[key] === "object") {
copy[key] = deepClone(obj[key]);
} else {
copy[key] = obj[key];
}
}
}
}
return copy;
}
实现 函数柯里化
函数参数不确定的柯里化
function add() {
return [...arguments].reduce((r, v) => r + v);
}
function curry(fn) {
let args = [];
return function Fun() {
args = [...args, ...arguments];
if (arguments.length) return Fun;
else return fn.apply(this, args);
}
}
函数参数确定的柯里化
function add(arg1, arg2, arg3) {
return [...arguments].reduce((r, v) => r + v)
}
function curry(fn) {
let args = [];
return function Fun(){
args = [...args, ...arguments];
if(arg.length < fn.length) return Fun;
else return fn.apply(this, args);
}
}
实现 数组 转 树结构
function fun(data) {
let res = [];
let map = {};
data.forEach(item => map[item.id] = item);
data.forEach(item => {
let parent = map[item.parentId];
if (parent) (parent.children = parent.children || []).push(item);
else res.push(item);
})
return res;
}
实现 XML转JSON对象
function xml2json(str) {
let res;
let stack = [];
let text = "";
for (let i = 0; i < str.length; i++) {
if (str[i] === "<") {
i++;
let tagName = "";
while (i < str.length && str[i] !== ">") {
tagName += str[i];
i++;
}
if (tagName[0] === "/") {
res = stack.pop();
if (!res.children.length) res.children = text;
if (stack.length > 1) stack[stack.length - 1].children.push(res);
} else {
stack.push({ tag: tagName, children: [] });
}
text = "";
continue;
}
text += str[i];
}
return res;
}
实现 EventEmitter
function EventEmitter() {
this.events = {};
// 订阅
this.on = (event, fn) => (this.events[event] = this.events[event] || []).push(fn);
// 发布
this.emit = (event, ...args) => {
const fns = this.events[event];
fns.forEach(fn => fn.apply(this, args));
};
// 取消订阅
this.off = (event, fn) => {
const fns = this.events[event];
this.events[event] = fns && fns.filter(f => f !== fn);
};
// 触发一次
this.once = (event, fn) => {
const wrapFun = (...args) => {
fn.apply(this, args);
this.off(event, wrapFun);
};
this.on(event, wrapFun);
};
}
实现 JSONP
<script type="text/javascript">
let url = "http://xxx/xxx?callback=fun";
let script = document.createElement('script');
script.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(script);
var fun = function(data){
alert(data);
};
</script>
二叉树的遍历
前序
function preorderTraversal(root) {
const res = [];
const stack = [];
while (root || stack.length) {
while (root) {
res.push(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop();
root = root.right;
}
return res;
};
中序
function inorderTraversal(root) {
const res = [];
const stack = [];
while (root || stack.length) {
while (root) {
stack.push(root)
root = root.left;
}
root = stack.pop();
res.push(root.val);
root = root.right;
}
return res;
};
后序
function postorderTraversal(root) {
const res = [];
const stack = [];
while (root || stack.length) {
while (root) {
stack.push(root);
res.unshift(root.val);
root = root.right;
}
root = stack.pop();
root = root.left;
}
return res;
};
层序
function levelOrder(root) {
const ans = [];
if (!root) {
return ans;
}
let q = [root];
while (q.length) {
const newQ = [];
let layer = [];
for (const node of q) {
layer.push(node.val);
if (node.left) newQ.push(node.left);
if (node.right) newQ.push(node.right);
}
ans.push(layer);
q = newQ;
}
return ans;
};
快速排序
function qSort(arr, l, r) {
if (l >= r) return;
let i = l, j = r;
while (i < j) {
while (i < j && arr[j] >= arr[l]) j--;
while (i < j && arr[i] <= arr[l]) i++;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
[arr[i], arr[l]] = [arr[l], arr[i]];
qSort(arr, l, i - 1);
qSort(arr, i + 1, r);
};
排列组合公式
C(n, m) 从n
个元素中取出m
个的组合方案。
function C(n, m) {
if (m > n) return 0;
let numerator = 1;
let denominator = 1;
while (m > 0) {
numerator *= n--;
denominator *= m--;
}
return numerator / denominator;
}
A(n, m) 从n
个元素中选出m
个的排列方案。
function A(n, m) {
if (m > n) return 0;
let ans = 1;
while (m-- > 0) {
ans *= n--;
}
return ans;
}