AboutFE
AboutFE copied to clipboard
54、面试前准备的
1、 寄生组合继承
function Animal (name) {
this.name = name || 'Animal';
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
var Super = function(){};
Super.prototype = Animal.prototype; // 创建一个没有实例方法的类
Cat.prototype = new Super(); //将实例作为子类的原型
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
2、 深拷贝
function deepClone (origin, weakMap = new WeakMap()) { // 循环引用 WeakMap只能使用对象作为key值,是弱引用,当从WeakMap中移除时,会自动垃圾回收
if (origin == undefined || typeof origin != 'object') {
return origin
}
if (origin instanceof Date) {
return new Date(origin)
}
if (origin instanceof RegExp) {
return new RegExp(origin)
}
const val = weakMap.get(origin)
if (val) return val
const target = new origin.constructor()
weakMap.set(origin, target)
for (let k in origin) {
target[k] = deepClone(origin[k], weakMap)
}
return target
}
3、JS快速排序
let qsort = (list) => {
if (list.length === 0) {
return []
}
let [x, ...xs] = list
return [...qsort(xs.filter(u => u <= x)), x, ...qsort(xs.filter(u => u > x))]
}
4、flatter 数组
const deepFlatten = arr => arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []);
// deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5]
5、原生New
function newOperator(ctor) {
if (typeof ctor !== 'function') {
throw 'newOperator function the first param must be a function';
}
newOperator.target = ctor; // ES6 new.target 是指向构造函数
var newObj = Object.create(ctor.prototype);
var argsArr = [].slice.call(arguments, 1);
var ctorReturnResult = ctor.apply(newObj, argsArr); // 3. 生成的新对象会绑定到函数调用的 `this`。// 获取到ctor函数返回结果
var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
var isFunction = typeof ctorReturnResult === 'function';
if (isObject || isFunction) {
return ctorReturnResult;
}
return newObj;
}
6、原生call、apply、bind
Function.prototype.applyFour = function (context) {
var context = context || window;
var args = arguments[1]; //获取传入的数组参数
var fn = Symbol();
context[fn] = this //假想context对象预先不存在名为fn的属性
if (args == void 0) { //没有传入参数直接执行
return context[fn]();
}
var fnStr = 'context[fn]('
for (var i = 0; i < args.length; i++) {
//得到"context.fn(arg1,arg2,arg3...)"这个字符串在,最后用eval执行
fnStr += i == args.length - 1 ? args[i] : args[i] + ','
}
fnStr += ')';
var returnValue = eval(fnStr); //还是eval强大
delete context[fn]; //执行完毕之后删除这个属性
return returnValue
}
// call return this.applyFour(([].shift.applyFour(arguments), arguments)
Function.prototype.bind = Function.prototype.bind || function (context) {
if (typeof this !== 'function') {
throw new TypeError(this + ' must be a function');
}
var me = this;
var args = Array.prototype.slice.call(arguments, 1);
// bind返回的函数如果作为构造函数,搭配new关键字出现的话,我们的绑定this就需要“被忽略”。
var F = function () {};
F.prototype = this.prototype;
var bound = function () {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return me.applyFour(this instanceof F ? this : context || this, finalArgs);
}
bound.prototype = new F();
return bound;
}
7、promise
//用来解析回调函数的返回值x,x可能是普通值也可能是个promise对象
function resolvePromise(bridgePromise, x, resolve, reject) {
//如果x是一个promise
if (x instanceof MyPromise) {
//如果这个promise是pending状态,就在它的then方法里继续执行resolvePromise解析它的结果,直到返回值不是一个pending状态的promise为止
if (x.status === PENDING) {
x.then(y => {
resolvePromise(bridgePromise, y, resolve, reject);
}, error => {
reject(error);
});
} else {
x.then(resolve, reject);
}
//如果x是一个普通值,就让bridgePromise的状态fulfilled,并把这个值传递下去
} else {
resolve(x);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const self = this;
let bridgePromise;
//防止使用者不传成功或失败回调函数,所以成功失败回调都给了默认回调函数
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected = typeof onRejected === "function" ? onRejected : error => {
throw error
};
if (self.status === FULFILLED) {
return bridgePromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
})
}
if (self.status === REJECTED) {
return bridgePromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(self.error);
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
if (self.status === PENDING) {
return bridgePromise = new MyPromise((resolve, reject) => {
self.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
self.onRejectedCallbacks.push((error) => {
try {
let x = onRejected(error);
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
}
//catch方法其实是个语法糖,就是只传onRejected不传onFulfilled的then方法
MyPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
}
MyPromise.all = function (promises) {
return new MyPromise(function (resolve, reject) {
let result = [];
let count = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
result[i] = data;
if (++count == promises.length) {
resolve(result);
}
}, function (error) {
reject(error);
});
}
});
}
MyPromise.race = function (promises) {
return new MyPromise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
resolve(data);
}, function (error) {
reject(error);
});
}
});
}
MyPromise.resolve = function (value) {
return new MyPromise(resolve => {
resolve(value);
});
}
MyPromise.reject = function (error) {
return new MyPromise((resolve, reject) => {
reject(error);
});
}
MyPromise.promisify = function (fn) {
return function () {
var args = Array.from(arguments);
return new MyPromise(function (resolve, reject) {
fn.apply(null, args.concat(function (err) {
err ? reject(err) : resolve(arguments[1])
}));
})
}
}
8、async / await
function _async(fn) {
return (...args) => Promise.resolve(fn(...args));
}
function _await() {
let result = data.next();
while (true) {
console.log('waiting...', result);
if (result.done) return result.value;
result = data.next();
}
}
9、 观察者 发布订阅
var observer = {
//订阅
addSubscriber: function (callbackId) {
this.subscribers[this.subscribers.length] = callbackId;
},
//退订
removeSubscriber: function (callbackId) {
for (var i = 0; i < this.subscribers.length; i++) {
if (this.subscribers[i] === callbackId) {
delete(this.subscribers[i]);
}
}
},
//发布
publish: function (args) {
for (var i = 0; i < this.subscribers.length; i++) {
if (typeof this.subscribers[i] === 'function') {
this.subscribers[i](args);
}
}
},
// 将对象o具有观察者功能
make: function (o) {
for (var i in this) {
o[i] = this[i];
o.subscribers = [];
}
}
};
10、jsbridge
(function () {
var id = 0,
callbacks = {},
registerFuncs = {};
window.JSBridge = {
// 调用 Native
invoke: function (bridgeName, callback, data) {
// 判断环境,获取不同的 nativeBridge
var thisId = id++; // 获取唯一 id
callbacks[thisId] = callback; // 存储 Callback
nativeBridge.postMessage({
bridgeName: bridgeName,
data: data || {},
callbackId: thisId // 传到 Native 端
});
},
receiveMessage: function (msg) {
var bridgeName = msg.bridgeName,
data = msg.data || {},
callbackId = msg.callbackId, // Native 将 callbackId 原封不动传回
responstId = msg.responstId;
// 具体逻辑
// bridgeName 和 callbackId 不会同时存在
if (callbackId) {
if (callbacks[callbackId]) { // 找到相应句柄
callbacks[callbackId](msg.data); // 执行调用
}
} else if (bridgeName) {
if (registerFuncs[bridgeName]) { // 通过 bridgeName 找到句柄
var ret = {},
flag = false;
registerFuncs[bridgeName].forEach(function (callback) => {
callback(data, function (r) {
flag = true;
ret = Object.assign(ret, r);
});
});
if (flag) {
nativeBridge.postMessage({ // 回调 Native
responstId: responstId,
ret: ret
});
}
}
}
},
register: function (bridgeName, callback) {
if (!registerFuncs[bridgeName]) {
registerFuncs[bridgeName] = [];
}
registerFuncs[bridgeName].push(callback); // 存储回调
}
};
})();
11、useHook
import {
useState,
useEffect
} from 'react';
const useFetch = (url = '', options = null) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
let isMounted = true;
setLoading(true);
fetch(url, options)
.then(res => res.json())
.then(data => {
if (isMounted) {
setData(data);
setError(null);
}
})
.catch(error => {
if (isMounted) {
setError(error);
setData(null);
}
})
.finally(() => isMounted && setLoading(false));
return () => (isMounted = false);
}, [url, options]);
return { loading, error, data };
};
export default useFetch;
12、 防抖节流
const deb = (fn, delay, immediate) => {
let timer = null
return function () {
const context = this
timer && clearTimeout(timer)
if (immediate) {
!timer && fn.apply(context, arguments)
}
timer = setTimeout(() => {
fn.apply(context, arguments)
}, delay)
}
}
function useDebounce(fn, delay) {
const {
current
} = useRef({});
function f(...args) {
if (current.timer) {
clearTimeout(current.timer);
}
current.timer = setTimeout(fn.bind(undefined, ...args), delay);
}
return f;
}
const throttle = (fn, delay = 2000) => {
let timer = null
let startTime = new Date()
return function() {
const context = this
let currentTime = new Date()
clearTimeout(timer)
if (currentTime - startTime >= delay) {
fn.apply(context, arguments)
startTime = currentTime
} else {
//让方法在脱离事件后也能执行一次
timer = setTimeout(() => {
fn.apply(context, arguments)
}, delay)
}
}
}
export function useThrottle(fn, delay) {
const {
current
} = useRef({});
function f(...args) {
if (!current.timer) {
current.timer = setTimeout(() => {
delete current.timer;
}, delay);
fn(...args);
}
}
return f;
}
13、遍历DFS BFS
// 递归
var dfs_recursive = function (root, res = []) {
res.push(root)
for (let item of root.children) {
dfs_recursive(item, res)
}
return res
}
console.log('1------------->>', dfs_recursive(root))
// 非递归 深度优先遍历 - stack 先进后出【 push(item) - > pop】 或者[unshift(item) - > shift()]
var dfs_stack = function (root) {
const res = []
const stack = []
stack.push(root)
while (stack.length != 0) {
let item = stack.pop()
res.push(item)
for (let i = item.children.length - 1; i >= 0; i--) {
stack.push(item.children[i])
}
}
return res
}
console.log('2------------->>', dfs_stack(root))
// 广度优先遍历 - queue 先进先出[push(item) - > shift()] 或者[unshift(item) - > pop()]
var bfs_queue = function (root) {
const res = []
const queue = []
queue.push(root)
while (queue.length != 0) {
let currentLevelLength = queue.length
for (let i = 0; i < currentLevelLength; i++) {
let item = queue.shift()
res.push(item)
for (let j = 0; j < item.children.length; j++) {
queue.push(item.children[j])
}
}
}
return res
}
console.log('3------------->>', bfs_queue(root))
14、遍历树 先序中序后序
先序遍历的算法:
// 递归
var preOrder = function (node) {
if (node) {
console.log(node.value);
preOrder(node.left);
preOrder(node.right);
}
}
// 非递归
var preOrderUnRecur = function (root) {
var stack = [],
res = [];
if (root != null) {
stack.push(root);
}
while (arr.length != 0) {
var temp = stack.pop();
res.push(temp.val);
// 栈中元素都是自己和自己的左孩子都访问过了,而右孩子还没有访问到的节点, 而且先序遍历stack要后面塞left才能弹出left
if (temp.right != null) {
stack.push(temp.right);
}
if (temp.left != null) {
stack.push(temp.left);
}
}
return res;
}
中序遍历的算法:
// 递归
var inOrder = function (node) {
if (node) {
inOrder(node.left); //先遍历到最左边的节点,然后输出
console.log(node.value);
inOrder(node.right);
}
}
// 非递归 栈中保存的元素是节点自身和它的右子树都没有被访问到的节点地址
var inOrderUnRecur = function (root) {
va stack = [], res = [];
while (true) {
while (root != null) {
stack.push(root);
root = root.left;
}
//终止条件:最后树遍历完了自然就结束
if (stack.length == 0) {
break;
}
var temp = stack.pop();
res.push(temp.val);
root = temp.right;
}
return res;
}
后序遍历的算法:
// 递归
var postOrder = function (node) {
if (node) {
postOrder(node.left);
postOrder(node.right);
console.log(node.value);
}
}
// 非递归 栈中保存的元素是它的右子树和自身都没有被遍历到的节点,
与中序遍历不同的是先访问右子树, 在回来的时候再输出根节点的值。
var posOrderUnRecur = function (root) {
var stack = [],
res = [];
if (root !== null) {
stack.push(root);
}
while (stack.length !== 0) {
var temp = stack.pop();
if (temp !== null) {
res.push(temp.val);
}
if (temp.left != null) {
stack.push(temp.left);
}
if (temp.right != null) {
stack.push(temp.right);
}
}
return res.reverse();
}
层次遍历
let levelTrav = (root) => {
if (!root) return []
let queue = [root]
let res = []
while (queue.length > 0) {
let len = queue.length
let arr = []
while (len) {
let node = queue.shift()
arr.push(node.val)
if (node.left) queue.push(node.left)
if (node.right) queue.push(node.right)
len--
}
res.push(arr)
}
return res
}