# new 关键字
new 关键字实现分为三步:
1、根据构造函数的原型创建新的对象
2、执行构造函数,绑定 this 指向为新对象,传入参数
3、返回执行后的结果
function myNew(constructor,...args){ | |
// 创建对象,原型为构造函数的原型对象 | |
const obj = Object.create(constructor.prototype); | |
// 执行构造函数,传入创建的对象和参数 | |
const res = constructor.apply(obj,args); // 注意 apply 方法是函数对象的方法,普通对象不可用 | |
return (res && typeof res === 'object')?res:obj; | |
} | |
function A (){ | |
this.name='sdjf' | |
} | |
const ab = myNew(A) | |
console.log(ab); |
# 手写 class
function Example(name) { | |
if(!new.target) throw new TypeError("该函数应该使用new来调用"); | |
this.name = name | |
} | |
Object.definePropertie(Example.prototype,"init",{ | |
enumerable:false, | |
value: function () { | |
'use strict'; | |
if(new.target) throw new Error("init函数不能使用new来调用"); | |
let fn = function () { | |
console.log(this.name); | |
} | |
fn.call(this); | |
} | |
}) |
# 闭包的简单使用
// 闭包隐藏数据,只提供 API 操作 | |
function createCache() { | |
const cache = {}; | |
return { | |
get(key){ | |
return cache[key] | |
}, | |
set(key,val){ | |
cache[key] = val; | |
} | |
} | |
} |
# 判断数据类型
function myTypeOf(obj) { | |
// 使用 toString | |
const typeMap = { | |
"[object Object]": "Object", | |
"[object Array]": "Array", | |
"[object String]": "String", | |
"[object Number]": "Number", | |
"[object Boolean]": "Boolean", | |
"[object Function]": "Function", | |
"[object Null]": "Null", // 注意这里是 "Null" 而不是 "null" | |
"[object Undefined]": "Undefined", // 注意这里是 "Undefined" 而不是 "undefined" | |
"[object Symbol]": "Symbol", | |
"[object BigInt]": "BigInt" | |
}; | |
return typeMap[Object.prototype.toString.call(obj)]; | |
// 或者根据类型进行判断 | |
switch (typeof obj) { | |
case "object": | |
if(Array.isArray(obj)) return "Array" | |
if(obj === "null") return "null"; | |
return "Object"; | |
case "number": | |
return "Number"; | |
case "string": | |
return "String"; | |
case "undefined": | |
return "undefined"; | |
case "bigint": | |
return "BigInt"; | |
case "boolean": | |
return "Boolean"; | |
case "function": | |
return "Function"; | |
case "symbol": | |
return "Symbol"; | |
default: | |
return "unknown" | |
} | |
} |
# 数组去重
function unique(arr) { | |
return new Array(...new Set(arr)) | |
} |
# 数组扁平化 (扁平一层)
function arrFlat(arr) { | |
if(arr.flat){ | |
return arr.flat(); //Array.flat 方法默认扁平一层 | |
} | |
// 兼容 es5 版本: | |
const res = []; | |
arr.forEach(item=>{ | |
if(Array.isArray(item)) res.push(...item); | |
else res.push(item) | |
}) | |
return res; | |
} |
# 手写对象的 [Symbol.iterator] 迭代器协议(普通对象不内置迭代器)
// 内置迭代器的异质对象有:Array,String,Set,Map | |
Symbol.myIterator = Symbol('myIterator'); | |
Object.prototype[Symbol.myIterator] = function () { | |
const iterKeys = Object.keys(this); | |
let iterIndex = 0; | |
return { | |
next: ()=> { | |
if (iterIndex < iterKeys.length) { | |
return { | |
value: this[iterKeys[iterIndex++]], | |
done: false | |
}; | |
} else { | |
return { | |
value: undefined, | |
done: true | |
}; | |
} | |
} | |
} | |
} |
# 手写 for of 循环(for of 便利前提是对象内置迭代器)
function myForOf(obj,fn) { | |
if(!obj[Symbol.iterator]) throw new TypeError('obj不存在迭代器') | |
const myIterator = obj[Symbol.iterator](); | |
let cur = myIterator.next(); | |
while (!cur.done) { | |
fn(cur.value); | |
cur = myIterator.next(); | |
} | |
} |
# 手写 for in 循环(for in 的前提是对象属性可被枚举,Object.keys 也是)
function myForIn(obj,fn) { | |
const keys = Object.keys(obj); | |
keys.forEach(item=>{ | |
fn({[item]:obj[item]}); | |
}) | |
} |
# 浅拷贝,返回新的对象,丢失原型链
function shallowClone(objLike) { | |
if(typeof objLike !== 'object') return objLike; | |
const res = Array.isArray(objLike)?[]:{}; | |
for(let item in objLike){ | |
if(objLike.hasOwnProperty(item)){ | |
res[item] = objLike[item]; | |
} | |
} | |
return res; | |
} |
# 手写深拷贝
// 是否是对象类型 | |
const isObject = (obj) => typeof obj === 'object' || typeof obj === 'function' && obj !== null; | |
function deepClone(target,map = new WeakMap()) { | |
if(structuredClone) return structuredClone(target); | |
// 兼容 structuredClone, 因为要进行递归处理,所以需要增加默认参数 | |
if(map.get(target)) return target; | |
let constructor = target.constructor; // 获取到 constructor | |
// 如果是日期或者正则,就新创建一个实例 | |
if(/^(RegExp|Date)$/i.test(constructor.name)){ | |
return new constructor(target); | |
} | |
if(isObject(target)){ | |
map.set(target,true); | |
const cloneTarget = Array.isArray(target) ? []:{}; | |
for(let prop in target){ | |
if(target.hasOwnProperty(prop)){ | |
cloneTarget[prop] = deepClone(target[prop],map); | |
} | |
} | |
return cloneTarget; | |
}else { | |
return target; | |
} | |
} | |
// > 注意:1、原型链不能形成闭环(报错),2、__proto__的值只能是对象或者 null,3、一个对象只能又一个 [[Prorotype]],4、__proto__是内部 [[Prototype]] 的 getter/setter。 |
# 手写全局事件总线
class EventEmitter{ | |
#cache = {}; | |
constructor(){} | |
on(name,fn){ | |
if(this.#cache[name]){ | |
this.#cache[name].push(fn); | |
}else{ | |
this.#cache[name] = [fn] | |
} | |
} | |
off(name,fn){ | |
if(!this.#cache[name]) return; | |
this.#cache[name] = this.#cache[name].filter(item=>{ | |
return item !== fn; | |
}) | |
} | |
emit(name){ | |
this.#cache[name].forEach(item=>{ | |
item(); | |
}) | |
} | |
} | |
// 复习 CustomEvent、Event 和 EventTarget(dispatchEvent 和 addEventListener) | |
const sayHiEvent = new CustomEvent('sayHi',{ | |
detail:{ | |
name:"sayHi事件" | |
} | |
}) | |
const eventTarget = new EventTarget(); | |
eventTarget.addEventListener('sayHi',(e)=>{ | |
console.log(e.detail,'e.detail'); | |
}) | |
// eventTarget.dispatchEvent(sayHiEvent) | |
// 深度思考:为什么 eventTarget 使用 dispatchEvent 时,要传入 Event 事件,而不是 type 类型? | |
// 因为一个 DOM 同一事件不能重复,并且同一事件类型一般要创建很多个事件(用于不同的 DOM,因此不能复用同一事件),此时用类型很难对其标识 |
# 遍历输出构造类
function consoClass(classFn) { | |
let a = Object.getPrototypeOf(classFn); | |
while (a !== null) { | |
console.log(a); | |
a = Object.getPrototypeOf(a); | |
} | |
} | |
// HTMLElement--Element--Node--EventTarget--Object--null | |
consoClass(HTMLElement) |
# 图片懒加载
const imgList = [...document.querySelectorAll("image")]; | |
const imgLength = imgList.length; | |
const imgLazyLoad = (function () { | |
let count = 0; | |
return function () { | |
let deleteIndexList = []; | |
// 遍历图片列表 | |
imgList.forEach((item,index)=>{ | |
let rect = item.getBoundingClientRect(); | |
// 如果 rect.top 小于 window.innerHeight,代表以及进入视野之内 | |
if(rect.top < 0){ | |
item.src = item.dataset.src; | |
deleteIndexList.push(index); | |
} | |
}) | |
imgList = imgList.filter((item,index)=>!deleteIndexList.includes(index)); | |
} | |
})() | |
document.addEventListener("scroll",imgLazyLoad); |
# 函数防抖(多次执行,重新计时)
function debounce(fn,time) { | |
let timer = null; | |
return function () { | |
const bindFn = fn.bind(this,...arguments) | |
clearTimeout(timer); | |
setTimeout(() => { | |
bindFn(); | |
}, time); | |
} | |
} |
# 函数节流(固定时间只执行一次)
function th(fn,time) { | |
let timer = null; | |
return function () { | |
if(timer) return; | |
const bindFn = fn.bind(this,...arguments) | |
timer = setTimeout(() => { | |
bindFn(); | |
clearTimeout(timer); | |
}, time); | |
} | |
} |
# 函数柯里化
function curry(fn) { | |
// 将 fn 函数进行柯里化 | |
return function curried(...args) { | |
if(args.length >= fn.length){ | |
return fn.apply(this,args); | |
}else{ | |
return function (...args2) { | |
return curried.apply(this,args.concat(args2)) | |
} | |
} | |
} | |
} |
# 函数偏函数化
function partial(fn,...args) { | |
return (...arg) => { | |
return fn(...args,...arg); | |
} | |
} |
# 使用 jsonp 进行跨域 get 请求
const jsonp = ({ url, params, callbackName }) => { | |
const generateUrl = () => { | |
let dataSrc = '' | |
for (let key in params) { | |
if (params.hasOwnProperty(key)) { | |
dataSrc += `${key}=${params[key]}&` | |
} | |
} | |
dataSrc += `callback=${callbackName}` | |
return `${url}?${dataSrc}` | |
} | |
return new Promise((resolve, reject) => { | |
const scriptEle = document.createElement('script') | |
scriptEle.src = generateUrl() | |
document.body.appendChild(scriptEle) | |
window[callbackName] = data => { | |
resolve(data) | |
document.removeChild(scriptEle) | |
} | |
}) | |
} |
# AJAX 封装 get 请求 JSON 数据
function getJSON(url) { | |
return new Promise((resolve,reject)=>{ | |
const xhr = new XMLHttpRequest(); | |
xhr.open("GET",url,false); | |
xhr.setRequestHeader("Accept","application/json"); | |
xhr.onreadystatechange = function () { | |
if(xhr.status !== 4) return; | |
if(xhr.status === 200 || xhr.status === 304) { | |
resolve(xhr.responseText); | |
}else { | |
reject(new Error(xhr.responseText)); | |
} | |
} | |
xhr.send(); | |
}) | |
} |
# 实现 forEach
Array.prototype.myForEach = function (callback,thisArg) { | |
if(this === null) throw new TypeError("this is null or not defined"); | |
// 如果回调不是函数 | |
if(typeof callback !== 'function') throw new TypeError(callback + "is not a function"); | |
const superThis = Object(this); // 将 this 进行兼容,确保不是 null 或 undefined | |
const len = superThis.length >>> 0; // 确保该函数的参数是正整数或 0 | |
let k = 0; | |
while (k < len) { // 当 k 小于 0 进行遍历 | |
if(k in superThis){ | |
callback.call(thisArg,superThis[k],k,superThis); | |
} | |
k++; | |
} | |
} |
# 实现 map
Array.prototype.myMap = function (callback,thisArg) { | |
if(this.null) throw new TypeError("this is null or defined"); | |
if(typeof callback !== "function") throw new TypeError(callback.name + "is not a function"); | |
const thisObj = Object(this); // 保证 thisObj 不是 null 或 undefined,使用 Object 可以传入数组,使用 Array 需要列出来 | |
const len = thisObj.length >>> 0; // 无符号右移 0,内部全部流程:valueOf、toString、Number,然后无符号左移(去小数) | |
let k =0;res = [] | |
while (k < len) { | |
if(k in thisObj){ | |
res[k]=callback.call(thisArg,thisObj[k],k,thisObj); | |
} | |
k++; | |
} | |
return res; | |
} |
# 实现 filter
Array.prototype.myFilter = function (callback,thisArg) { | |
if(this == null) return new TypeError("this is null or undefined") | |
if(typeof callback !== "function") return new TypeError(callback.name + "is not a function") | |
// 处理 filter | |
const thisObj = Object(this); | |
const len = thisObj.length >>> 0; | |
let res = [],k = 0; | |
while (k < len) { | |
if(k in thisObj){ | |
if(callback.call(thisArg,thisObj[k],k,thisObj)){ | |
res.push(thisObj[k]); | |
} | |
} | |
k++; | |
} | |
return res; | |
} |
# 实现 some
Array.prototype.mySome = function (callback,thisArg) { | |
if(this == null) return new TypeError("this is null or undefined"); | |
if(typeof callback !== "function") return new TypeError(callback.name + "is not a function") | |
const thisObj = Object(this); | |
let len = thisObj.length,k = 0; | |
while (k < len) { | |
if(k in thisObj){ | |
if(callback.call(thisArg,thisObj[k],k,thisObj)){ | |
return true; | |
} | |
} | |
k++; | |
} | |
return false; | |
} |
# 实现 reduce
Array.prototype.myReduce = function (callback,initialValue) { | |
if(this == null) return new TypeError("this is null or undefined"); | |
if(typeof callback !== "function") return TypeError(callback.name + "is not a function"); | |
const thisObj = Object(this); // 防止 null 或者 undefined | |
const len = thisObj.length; | |
let res,k=0; | |
while (k < len) { | |
if(k in thisObj){ | |
// 进行执行 | |
initialValue = callback(initialValue,thisObj[k]); | |
} | |
k++; | |
} | |
return initialValue; | |
} |
# 手写 call、apply 和 bind
Function.prototype.myCall = function (thisContext) { | |
// 将 this(函数),绑定 this, 传入参数 | |
thisContext = thisContext || window; //this 默认为 window | |
const fn = Symbol(thisContext); | |
thisContext[fn] = this; // 将 this 函数挂载到 thisContext 环境中 | |
let args = [...arguments].slice(1); | |
thisContext[fn](...args); // 用对象 [属性] 的方式使用函数(绑定 this) | |
delete thisContext[fn]; // 最后删除属性 | |
} | |
// 小结:使用 call 绑定 this 的关键就在于:将 this(也就是函数)挂载到 thisContext(需要绑定的 this 上),使用对象 [属性] 的方式进行执行,从而自动绑定 this | |
Function.prototype.myApply = function (thisContext) { | |
const fn = Symbol("myApply"); | |
// 将 fn 绑定到黄经中 | |
thisContext[fn] = this; | |
// 执行 | |
const args = [...arguments].slice(1); | |
thisContext[fn](args); | |
delete thisContext[fn]; // 删除属性之后,垃圾回收机制自动回收 fn | |
} | |
Function.prototype.myBind = function (thisContext) { | |
// 函数绑定 | |
// 返会一个函数,绑定 this 指向,到固定的函数上 | |
const thisFn = this; | |
const arg = [...arguments].slice(1); // 保存传入的参数 | |
return function () { | |
const newArg = [...arguments]; | |
return thisFn.apply(thisContext,[...arg,...newArg]) | |
} | |
} |
# instanceof
function myInstanceOf(left,right) { | |
// 判断 left 是不是 right 的子类,遍历 left 的原型链,看是否有 right | |
if(typeof left !== "object") throw new TypeError("Left-hand side of myInstanceOf is not an object"); | |
if(typeof right !== "object") throw new TypeError("Right-hand side of myInstanceOf is not an object"); | |
while (Object.getPrototypeOf(left)) { | |
left = Object.getPrototypeOf(left); | |
if(left === Object.getPrototypeOf(right)){ | |
return true; | |
} | |
} | |
return false; | |
} |
# 实现 Object.create
// 参数一:proto 二:属性对象 | |
Object.myCreate = function (proto,propertyObj) { | |
if(typeof proto !== "object" && typeof proto !== "function"){ | |
throw new TypeError("object prototype may not be an object or null") | |
} | |
if(propertyObj === null){ | |
new TypeError("Cannot convert undefined or null to object"); | |
} | |
function F() {} | |
F.prototype = proto; | |
const obj = new F(); | |
if(propertyObj !== undefined){ | |
Object.defineProperties(obj,propertyObj) | |
} | |
// 模拟 Object.create (null) | |
if(proto === null){ | |
obj.__proto__ = null; | |
} | |
return obj; | |
} |
# 实现 Object.assign
Object.myAssign = function (target,...source) { | |
if(target == null){ | |
throw new TypeError("Cannot convert undefined or null to object") | |
} | |
// 确保是一个对象类型 | |
let resObj = Object(target); | |
source.forEach(item=>{ | |
if(item != null){ | |
// 如果 key 属性是在 | |
for(let key in item){ | |
// 如果 key 是原型上的方法 | |
if(item.hasOwnProperty(key)){ | |
resObj[key] = item[key]; | |
} | |
} | |
} | |
}) | |
return resObj; | |
} |
# Promise 实现
const PENDING = Symbol.for('pending'); | |
const FULFILLED = Symbol.for('fulfilled'); | |
const REJECTED = Symbol.for('rejected'); | |
const resolvemyPromise = (myPromise2, x, resolve, reject) => { | |
if (myPromise2 === x) { | |
return reject( | |
new TypeError("Chaining cycle detected for myPromise #<myPromise>") | |
); | |
} | |
let called; | |
if ((typeof x === "object" && x != null) || typeof x === "function") { | |
try { | |
let then = x.then; | |
if (typeof then === "function") { | |
then.call( | |
x, | |
(y) => { | |
if (called) return; | |
called = true; | |
resolvemyPromise(myPromise2, y, resolve, reject); | |
}, | |
(r) => { | |
if (called) return; | |
called = true; | |
reject(r); | |
} | |
); | |
} else { | |
resolve(x); | |
} | |
} catch (e) { | |
if (called) return; | |
called = true; | |
reject(e); | |
} | |
} else { | |
resolve(x); | |
} | |
}; | |
class myPromise { | |
constructor(executor) { | |
this.status = PENDING; // 状态 | |
this.value = undefined; // 值 | |
this.reason = undefined; // 原因 | |
this.onResolvedCallbacks = []; // 成功回调数组 | |
this.onRejectedCallbacks = []; // 失败回调数组 | |
let resolve = (value) => { | |
if (value instanceof myPromise) { | |
return value.then(resolve, reject); | |
} | |
if (this.status === PENDING) { | |
this.status = FULFILLED; | |
this.value = value; | |
this.onResolvedCallbacks.forEach((fn) => fn()); | |
} | |
}; | |
let reject = (reason) => { | |
if (this.status === PENDING) { | |
this.status = REJECTED; | |
this.reason = reason; | |
this.onRejectedCallbacks.forEach((fn) => fn()); | |
} | |
}; | |
try { | |
executor(resolve, reject); | |
} catch (error) { | |
reject(error); | |
} | |
} | |
then(onFulfilled, onRejected) { | |
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v; | |
onRejected = | |
typeof onRejected === "function" | |
? onRejected | |
: (err) => { | |
throw err; | |
}; | |
return new myPromise((resolve, reject) => { | |
if (this.status === FULFILLED) { | |
setTimeout(() => { | |
try { | |
let x = onFulfilled(this.value); | |
resolvemyPromise(myPromise2, x, resolve, reject); | |
} catch (e) { | |
reject(e); | |
} | |
}, 0); | |
} | |
if (this.status === REJECTED) { | |
setTimeout(() => { | |
try { | |
let x = onRejected(this.reason); | |
resolvemyPromise(myPromise2, x, resolve, reject); | |
} catch (e) { | |
reject(e); | |
} | |
}, 0); | |
} | |
if (this.status === PENDING) { | |
this.onResolvedCallbacks.push(() => { | |
setTimeout(() => { | |
try { | |
let x = onFulfilled(this.value); | |
resolvemyPromise(myPromise2, x, resolve, reject); | |
} catch (e) { | |
reject(e); | |
} | |
}, 0); | |
}); | |
this.onRejectedCallbacks.push(() => { | |
setTimeout(() => { | |
try { | |
let x = onRejected(this.reason); | |
resolvemyPromise(myPromise2, x, resolve, reject); | |
} catch (e) { | |
reject(e); | |
} | |
}, 0); | |
}); | |
} | |
}); | |
} | |
catch(errCallback) { | |
return this.then(null, errCallback); | |
} | |
finally(callback) { | |
return this.then( | |
(value) => { | |
return myPromise.resolve(callback()).then(() => value); | |
}, | |
(reason) => { | |
return myPromise.resolve(callback()).then(() => { | |
throw reason; | |
}); | |
} | |
); | |
} | |
static resolve(data) { | |
return new myPromise((resolve, reject) => { | |
resolve(data); | |
}); | |
} | |
static reject(reason) { | |
return new myPromise((resolve, reject) => { | |
reject(reason); | |
}); | |
} | |
static all(values) { | |
if (!Array.isArray(values)) { | |
const type = typeof values; | |
return new TypeError(`TypeError: ${type} ${values} is not iterable`); | |
} | |
return new myPromise((resolve, reject) => { | |
let resultArr = []; | |
let orderIndex = 0; | |
const processResultByKey = (value, index) => { | |
resultArr[index] = value; | |
if (++orderIndex === values.length) { | |
resolve(resultArr); | |
} | |
}; | |
for (let i = 0; i < values.length; i++) { | |
let value = values[i]; | |
if (value && typeof value.then === "function") { | |
value.then((value) => { | |
processResultByKey(value, i); | |
}, reject); | |
} else { | |
processResultByKey(value, i); | |
} | |
} | |
}); | |
} | |
static race(myPromises) { | |
return new myPromise((resolve, reject) => { | |
for (let i = 0; i < myPromises.length; i++) { | |
let val = myPromises[i]; | |
if (val && typeof val.then === "function") { | |
val.then(resolve, reject); | |
} else { | |
resolve(val); | |
} | |
} | |
}); | |
} | |
} | |
myPromise.defer = myPromise.deferred = function () { | |
let dtd = {}; | |
dtd.myPromise = new myPromise((resolve, reject) => { | |
dtd.resolve = resolve; | |
dtd.reject = reject; | |
}); | |
return dtd; | |
}; | |
export default MyPromise; |
# vue3 封装 debounce
// 定义一个返回懒执行响应式数据的函数 | |
function useDebouncedRef(value,delay=200){ | |
let timeout; | |
return customRef((track,trigger)=>{ | |
return { | |
get(){ | |
track(); | |
return value; | |
}, | |
set(newValue){ | |
clearTimeout(timeout); | |
timeout = setTimeout(()=>{ | |
value = newValue | |
trigger(); | |
},delay) | |
} | |
} | |
}) | |
} |