代码仓库👇
call,apply,bind
Function.prototype.myCall1 = function(context) {
// 判断当前调用call的是否是函数,不是的话就抛出错误
if (typeof this !== 'function') {
throw new TypeError('Error')
}
// 如果没有传或传的值为空 context 指向 window
context = context || window
// 创造一个独一无二的字符串
let fn = Symbol(context)
// 给context添加一个方法,该方法保存着调用call的函数
context.fn = this
// 处理参数 去除第一个参数context,其它参数传入fn函数,返回一个新的参数数组
let arg = [...arguments].slice(1)
// 执行fn函数,也就是执行调用call的函数
const result = context.fn(...arg)
// 删除该属性
delete context.fn
// 返回函数执行的结果
return result
}
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
let fn = Symbol(context)
context.fn = this
// apply和call的区别是apply的第二个参数是数组
let arg = [...arguments].slice(1)
const result = context.fn(arg)
delete context.fn
return result
}
Function.prototype.bind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error');
}
var aArgs = [...arguments].slice(1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// 因为返回了一个函数,我们可以 new F(),所以需要检测new
// 如果当前函数的this指向的是构造函数中的this 则判定为new方式调用
// 此时我们需要忽略传入的this也就是context
// 因为 bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来
return fToBind.apply(this instanceof fBound
? this
: context,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 维护原型关系
if (this.prototype) {
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
Promise家族
// Promise.all
// all的原理,只能使用Promise,不能使用promise.resolve
Promise.all = function(values){
return new Promise((resolve,reject)=>{
let results = []; // 结果数组
let i = 0;
let processData = (value,index)=>{
results[index] = value;
// 当成功的个数 和 当前的参数个数相等就把结果抛出去
if(++i === values.length){
resolve(results);
}
}
for(let i = 0 ; i< values.length;i++){
let current = values[i]; // 拿到数组中每一项
// 判断是不是一个promise
if((typeof current === 'object' && current !==null)|| typeof current == 'function'){
// 如果是promise
if(typeof current.then == 'function'){
// 就调用这个promise的then方法,把结果和索引对应上
// 如果任何一个失败了返回的proimise就是一个失败的promise
current.then(y=>{
processData(y,i);
},reject)
}else{
processData(current,i);
}
}else{
processData(current,i);
}
}
});
}
// 可使用Promise和Promise.resolve和Promise.then方法来写
function promiseAll(promises) {
return new Promise(function(resolve, reject) {
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}
var resolvedCounter = 0;
var promiseNum = promises.length;
var resolvedValues = new Array(promiseNum);
for (let i = 0; i < promiseNum; i++) {
// 使用Promise.resolve判断当前promise的状态是resolve还是reject
Promise.resolve(promises[i]).then(function(value) {
resolvedCounter++
resolvedValues[i] = value
if (resolvedCounter == promiseNum) {
return resolve(resolvedValues)
}
}, function(reason) {
return reject(reason)
})
}
})
}
// promise.race
function promiseRace(promises) {
if (!Array.isArray(promises)) {
throw new Error("promises must be an array")
}
return new Promise(function (resolve, reject) {
promises.forEach(p =>
Promise.resolve(p).then(data => {
resolve(data)
}, err => {
reject(err)
})
)
})
}
// promise.catch
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
// Promise.allSettled
const formatSettledResult = (success, value) =>
success
? { status: "fulfilled", value }
: { status: "rejected", reason: value };
Promise.allSettled = function(iterators) {
const promises = Array.from(iterators);
const num = promises.length;
const settledList = new Array(num);
let settledNum = 0;
return new Promise(resolve => {
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(value => {
settledList[index] = formatSettledResult(true, value);
if (++settledNum === num) {
resolve(settledList);
}
})
.catch(error => {
settledList[index] = formatSettledResult(false, error);
if (++settledNum === num) {
resolve(settledList);
}
});
});
});
};
JSON.stringify
// 不带`replacer`和`space`参数的简化版本
// 不考虑循环引用
if (!window.JSON) {
window.JSON = {
parse: function(jsonStr) {
return eval('(' + jsonStr + ')');
},
stringify: function(jsonObj) {
var result = '',
curVal;
if (jsonObj === null) {
return String(jsonObj);
}
switch (typeof jsonObj) {
case 'number':
case 'boolean':
return String(jsonObj);
case 'string':
return '"' + jsonObj + '"';
case 'undefined':
case 'function':
return undefined;
}
switch (Object.prototype.toString.call(jsonObj)) {
case '[object Array]':
result += '[';
for (var i = 0, len = jsonObj.length; i < len; i++) {
curVal = JSON.stringify(jsonObj[i]);
result += (curVal === undefined ? null : curVal) + ",";
}
if (result !== '[') {
result = result.slice(0, -1);
}
result += ']';
return result;
case '[object Date]':
return '"' + (jsonObj.toJSON ? jsonObj.toJSON() : jsonObj.toString()) + '"';
case '[object RegExp]':
return "{}";
case '[object Object]':
result += '{';
for (i in jsonObj) {
if (jsonObj.hasOwnProperty(i)) {
curVal = JSON.stringify(jsonObj[i]);
if (curVal !== undefined) {
result += '"' + i + '":' + curVal + ',';
}
}
}
if (result !== '{') {
result = result.slice(0, -1);
}
result += '}';
return result;
case '[object String]':
return '"' + jsonObj.toString() + '"';
case '[object Number]':
case '[object Boolean]':
return jsonObj.toString();
}
}
};
}
New操作符
function New(func) {
// 创建一个全新的对象
var res = {};
// 链接到原型
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
// 执行函数,指定this指向创建的对象res
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
// 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error)
// 那么new表达式中的函数调用将返回该对象引用
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);
InstanceOf
// instanceof`可以正确的判断对象的类型,
// 因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
function myInstanceof(left, right) {
if (typeof left !== 'object' || left === null) return false // 基础类型一律为 false
let proto = Object.getPrototypeOf(left) // 获取对象的原型
while(true) {
if (proto === null) return false
if (proto === right.prototype) return true
proto = Object.getPrototypeOf(proto)
}
}
简单深拷贝
function clone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
};
函数柯里化
function curry(fn, args) {
// 获取传进来的函数的参数个数
var length = fn.length;
// 获取初始柯里化的参数数组
var args = args || [];
return function(){
// 柯里化的参数参数合并
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this,fn,newArgs);
}else{
return fn.apply(this,newArgs);
}
}
}
function multiFn(a, b, c) {
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(5)(4);
multi(2,5,4);
multi(2)(5,4);
multi(2,5)(4);
// 使用ES6方式
const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4)
curryTest(1,2)(4)(3)
curryTest(1,2)(3,4)
节流函数
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。
// 当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
防抖函数
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
// 缓存一个定时器id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
寄生组合式继承
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
// 不必为了指定子类型的原型而调用父类型的构造函数,我们所需要的无非就是父类型原型的一个副本而已。
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
EventEmitter
class EventEmitter {
constructor() {
this.queue = {} //可触发多次的事件
this.onceQueue = {} //只能触发一次的事件
}
on(event, fn) { //监听事件,可以触发多次
if (!this.queue[event]) this.queue[event] = []
this.queue[event].push(fn)
}
once(event, fn) { //监听事件,只能触发一次
if (!this.onceQueue[event]) {
this.onceQueue[event] = {
fns: [],
hasFired: false
}
}
this.onceQueue[event].fns.push(fn)
}
fire() { // 触发指定的事件
// 取得事件名称
const event = [].shift.call(arguments),
// 取得该事件里所有的回调函数(可以触发多次的事件)
// 取得该事件里所有的回调函数(只能触发一次的事件)
fns = this.queue[event],
onceFns = this.onceQueue[event]
if (fns && fns.length != 0) {
let i = 0,fn
while (fn = fns[i++]) {
fn.apply(this, arguments)
}
}
if (onceFns && !onceFns.hasFired) {
let i = 0,fn
while (fn = onceFns.fns[i++]) {
fn.apply(this, arguments)
}
this.onceQueue[event].hasFired = true
}
}
off(event, fn = null) { //可移除特定事件里的某个回调函数或者所有回调函数
const fns = this.queue[event]
if (!fns || fns.length == 0) return
if (fn) { //移除该事件特定的回调
this.queue[event] = fns.filter(item => {
return item !== fn
})
} else { //移除该事件所有的回调
this.queue[event] = []
}
}
}
大数相加
function add(a ,b){
//取两个数字的最大长度
let maxLength = Math.max(a.length, b.length);
//用0去补齐长度
a = a.padStart(maxLength , 0);//"0009007199254740991"
b = b.padStart(maxLength , 0);//"1234567899999999999"
//定义加法过程中需要用到的变量
let t = 0;
let f = 0; //"进位"
let sum = "";
for(let i=maxLength-1 ; i>=0 ; i--){
t = parseInt(a[i]) + parseInt(b[i]) + f;
f = Math.floor(t/10);
sum = t%10 + sum;
}
if(f == 1){
sum = "1" + sum;
}
return sum;
}
控制并发请求数量
// 原理:使用一个队列维护所有的请求,然后使用async/await或者promise对请求进行控制
// 当前面的请求完成就从队列中出队下一个请求
class LimitResquest {
constructor(limit) {
this.limit = limit
this.currentSum = 0
this.requests = []
}
request (reqFn) {
if (!reqFn || !(reqFn instanceof Function)) {
console.error('当前请求不是一个Function', reqFn)
return
}
this.requests.push(reqFn)
if (this.currentSum < this.limit) {
this.run()
}
}
async run() {
try {
++this.currentSum
const fn = this.requests.shift()
await fn()
} catch(err) {
console.log('Error', err)
} finally {
--this.currentSum
if (this.requests.length > 0) {
this.run()
}
}
}
}
let a = () => new Promise((resolve) => {
setTimeout(() => {resolve(1)}, 1000)
}).then((data) => console.log(data))
let b = () => new Promise((resolve) => {
setTimeout(() => {resolve(2)}, 1000)
}).then((data) => console.log(data))
let c = () => new Promise((resolve) => {
setTimeout(() => {resolve(3)}, 1000)
}).then((data) => console.log(data))
let d = () => new Promise((resolve) => {
setTimeout(() => {resolve(4)}, 1000)
}).then((data) => console.log(data))
// 可以调整限制数查看控制效果
let limitResquest = new LimitResquest(2)
limitResquest.request(a)
limitResquest.request(b)
limitResquest.request(c)
limitResquest.request(d)
limitResquest.request(a)
limitResquest.request(b)
limitResquest.request(c)
limitResquest.request(d)
遍历嵌套对象
const obj = {
a:{
b:{
c:666
}
}
}
var str = 'a.b.c';
const getData = ()=>{
var newArr = str.split('.').reduce((o,s)=>{ return o[s]},obj)
return newArr
}
console.log( getData());
// 第二种实现方式
const obj = {
a:{
b:{
c:666
}
}
}
var str = 'a.b.c';
const getData=(obj,str)=>{
str.split('.').forEach(element =>{
obj= obj[element]
})
return obj;
}
console.log(getData(obj,str));