We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
好的文章可参考如下。 各种源码实现,你想要的这里都有
当下不管是在面试过程中还是在日常开发过程中,一些底层的实现变得越来越有效率性,那么当理解如何实现时,能否直接写出一份实现呢?
为此,我特意写了一些自己的理解总结,便于后期的回顾或分享。😄
实现 Bind 函数的关键点在于两点。
Function.prototype.myBind = function(obj) { // 先判断是否为函数 if(typeof this !== 'function') { throw new Error('This is not function...') } const remainArgs = Array.prototype.slice.call(arguments, 1) // 获取剩余参数 const TempFun = function() {} // 构造一个空函数 const self = this // 缓存当前函数 if(self.prototype) { // 保存当前函数的原型 TempFun.prototype = self.prototype } const returnFun = function() { return self.apply(this instanceof TempFun ? this : obj, remainArgs) } returnFun.prototype = new TempFun() return returnFun }
实现 Call 函数和 Apply 函数,主要体现在两点。
Function.prototype.myCall = function(obj) { if(typeof this !== 'function') { throw new Error('This is not a function...') } const selfFun = this // 保存当前函数 const remainArgs = [...arguments].slice(1) // 获取剩余参数 const fn = Symbol('fn') // 避免对象函数名冲突 obj[fn] = selfFun const result = obj[fn](...remainArgs) // 保存最终的对象调用函数结果 delete obj[fn] // 删除相应的对象函数 return result }
实现 Reduce 函数,主要体现在两点。
// reduce方法实现 Array.prototype.myReduce = function(callbackFun) { let returnSum = undefined // 叠加器最终变量 let index = 0 // 数组遍历的索引 const self = this // 获取当前数组 if(arguments.length > 1) { returnSum = arguments[1] } else if(arguments.length === 1) { returnSum = self[index] index = 1 } while(index < self.length) { let hasVal = self.hasOwnProperty(index) if(hasVal) { let currentVal = self[index] returnSum = callbackFun(returnSum, currentVal, index) } index++ } return returnSum }
new 内部机制主要经历以下四个步骤:
因此要实现一个 new 方法,那么必须抓住两点。
// new实现 function myNew() { const obj = new Object() // 创建一个新的对象 const argsFun = Array.prototype.shift.call(arguments) // 获取构造函数 obj.__proto__ = argsFun.prototype // 将新对象的原型指针指向构造函数的原型对象 const remainArgs = Array.prototype.slice.call(arguments, 0) // 获取剩余参数 const result = argsFun.apply(obj, remainArgs) // 执行构造函数中的代码 return typeof result === 'object' && result !== null ? result : obj // 返回新的对象 }
需要注意的是,new 是属于关键字,不能被重写,因此只能使用函数来模拟实现 new 的功能。
async/await 是 generator 语法糖,基于 promise 进行编写的。由于 async/await 都是属于关键字,不能被重写,只能通过使用函数来模拟实现。
要实现 async/await 函数,必须遵循下列几点要求。
// async/await实现 function asyncFun(callbackFun) { return function() { const self = this const args = arguments return new Promise((resolve, reject) => { const gen = callbackFun.apply(self, args) function _next(value) { // 用于遍历迭代器 awaitFun(resolve, reject, gen, _next, _throw, 'next', value) } function _throw(err) { // 用于迭代器遍历时抛出异常 awaitFun(resolve, reject, gen, _next, _throw, 'throw', err) } _next() // 迭代器自执行 }) } } function await(res, rej, gen, nextFun, throwFun, funKey, args) { const nextRes = gen[funKey](args) if (nextRes.done) { res(nextRes.value) } else { Promise.resolve(nextRes.value).then(nextFun, throwFun) } }
在 JavaScript 为双向数据绑定的实现提供了两个 API,分别对应于 ES5 版本的Object.defineProperty和 ES6 版本Proxy。
Object.defineProperty
Proxy
Object.defineProperty版本
// Object.defineProperty实现双向数据绑定 const data = { text: '' } const inputEle = document.querySelector('input') const spanEle = document.querySelector('span') Object.defineProperty(data, 'text', { set(val) { spanEle.text = val } }) inputEle.addEventListener('input', function(e) { data.text = this.value })
Proxy版本
// Proxy实现双向数据绑定 const data = { text: '' } const inputEle = document.querySelector('input') const spanEle = document.querySelector('span') const handler = { set(target, key, value) { target[key] = value spanEle.text = value return value } } const proxyObj = new Proxy(data, handler) inputEle.addEventListener('input', function(e) { proxyObj.text = this.value })
实现 Object.create 方法,主要是利用寄生式继承。
简单理解,就是先构建一个空构造函数,接着将参数对象赋给构造函数的原型对象,并将原型对象中 constructor 指回空构造函数,最后返回空构造函数实例。
// Obejct.create方法实现 Object.prototype.myCreate = function(obj) { function Noop() {} Noop.prototype = obj Noop.constructor = Noop return new Noop() }
instanceof 的机制就是判断右边的构造函数的原型对象是否在左边对象的__proto__的原型链上。
要实现 instanceof,那么需要注意的是,利用遍历一直寻找左边对象的__proto__,若等于右边构造函数的原型对象,则返回 true,否则直到遍历为 null 为止。
// instanceof实现 function myInstanceof(left, right) { let leftPro = left.__proto__ let rightPro = right.prototype while(leftPro) { if(leftPro === rightPro) return true leftPro = leftPro.__proto__ } return false }
Object.getOwnPropertyNames 用于获取一个对象的所有属性名,不包括原型对象上的。
那么,要实现 Object.getOwnPropertyNames 方法,可结合使用 in 操作符和 Object.hasOwnProperty 实现。
// Object.getOwnPropertyNams实现 Object.myGetOwnPropertyNames = function(obj) { if(typeof obj !== 'object') throw new Error('This is not a object...') const resultArr = [] for(let allKey in obj) { if(Object.hasOwnProperty.call(obj, allKey)) { resultArr.push(allKey) } } return resultArr }
Promise 拥有三种状态,分别是pending、fullfilled、rejected。
而 Promise 的实现原理就是,通过队列形式控制每个成功回调以及失败回调的处理,内部使用私有变量保存当前处理状态以及处理所得到的值。接下来一一分析每个方法的实现要点。
静态resolve方法。
返回一个Promise实例,先判断当前状态是否为pending,若不是直接返回。
然后判断参数值是否为Promise实例,若是则必须调用then方法等待其调用完毕才能进行下一步。
最后需要对成功回调队列和失败回调队列中进行清空处理(即使用shift方法,一一执行其回调函数)。
上述操作必须在一个setTimeout中进行。
静态reject方法。
无需判断为Promise实例,直接对失败回调队列进行清空处理(即使用shift方法,一一执行其回调函数)。
then方法。
返回一个Promise实例,先创建两个处理函数(分别为成功处理函数和失败处理函数)。
成功处理函数中操作便是判断then方法的第一个参数是否为函数,若是则先执行其函数(其中内部值作为其函数的参数),接着再执行静态resolve方法清空成功回调队列操作。
失败处理函数中操作便是判断then方法的第二个参数是否为函数,若是则先执行其函数(其中内部值作为其函数的参数),接着再执行静态reject方法清空失败回调队列操作。
根据状态判断,当内部状态为pending时,则成功回调队列push进成功处理函数,失败回调队列push进失败处理函数,当内部状态为fullfilled时,则直接执行成功处理函数(其中内部值作为其函数的参数),当内部状态为rejected时,则直接执行失败回调处理函数(其中内部值作为其函数的参数)。
catch方法。
相当于直接执行this.then(undefined, rejectedFun)。
finally方法。
相当于直接执行this.then(val => MyPromise.resolve(callback()).then(() => val), val => MyPromise.reoslve(callback).then(() => { throw val }))
静态all方法。
返回一个Promise实例,创建一个空数组,遍历参数Promise数组,执行每一个Promise的then方法,然后将处理好的值push进空数组中,当判断数组长度与参数Promise数组长度一致时即可使用reoslve将处理好的数组返回。
静态race方法。
返回一个Promise实例,遍历参数Promise数组,执行每一个Promise的then方法,一旦有值返回即可直接resolve返回。
// Promise类实现 class MyPromise { constructor(handleFun) { handleFun(this._resolve, this._reject) this._status = 'pending' // 当前的promise状态 this._value = undefined // 当前promise处理的值 this._fullFilledQuene = [] // 当前promise接受处理队列 this._rejectedQuene = [] // 当前promise拒绝处理队列 } _resolve(val) { setTimeout(function() { if(this._status !== 'pending') return this._status = 'fullfilled' const runFullfilledQuene = (res) => { let callback while(callback = this._fullFilledQuene.shift()) { callback(res) } } const runRejectedQuene = (err) => { let callback while(callback = this._rejectedQuene.shift()) { callback(err) } } if(val instanceof MyPromise) { val.then(res => { this._value = res runFullfilledQuene(res) }, err => { runRejectedQuene(err) }) } else { this._value = val runFullfilledQuene(val) } }) } _reject(err) { setTimeout(function() { if(this._status !== 'pending') return this._status = 'rejected' this._value = err let callback while(callback = this._rejectedQuene.shift()) { callback(err) } }) } then(fullFilledFun, rejectedFun) { return new MyPromise((resolve, reject) => { // 封装一个promise回调成功的处理 const handleFullFilledFun = val => { if(typeof fullFilledFun !== 'function') { reject(val) } else { const value = fullFilledFun(val) if(value instanceof MyPromise) { value.then(res => { resolve(res) }, err => { reject(err) }) } } } // 封装一个promise回调失败的处理 const handleRejectedFun = err => { if(typeof rejectedFun !== 'function') { reject(err) } else { const value = rejectedFun(err) if(value instanceof MyPromise) { value.then(res => { resolve(res) }, err => { reject(err) }) } } } switch(this._status) { case 'pending': this._fullFilledQuene.push(handleFullFilledFun) this._rejectedQuene.push(handleRejectedFun) break case 'fullfilled': handleFullFilledFun(this._value) break case 'rejected': handleRejectedFun(this._value) break } }) } catch(errFun) { return this.then(undefined, errFun) } finally(callback) { return this.then( value => MyPromise.resolve(callback()).then(() => value), err => MyPromise.resolve(callback()).then(() => err) ) } static resolve(val) { if(val instanceof MyPromise) return val return new MyPromise(resolve => resolve(val)) } static reject(err) { return new MyPromise((resovle, reject) => reject(err)) } static all(list) { return new MyPromise(resolve => { let arrPromise = [] for(let [index, promiseItem] of list) { promiseItem.then(res => { arrPromise.push(res) if(arrPromise.length === list.length) resolve(arrPromise) }) } }) } static race(list) { return new MyPromise(resolve => { for(let [index, promiseItem] of list) { promiseItem.then(res => { resolve(res) }) } }) } }
防抖的原理是,通过闭包缓存定时器,当缓存的定时器不为空时,则使用clearTimeout进行清除,然后再重新赋值为一个setTimeout,频繁操作中直到最后一步操作才会真正起效,前面的操作都会被清除掉。
// 防抖(频繁操作最终只会执行一次) function debounce(fn, time) { let timeout = undefined return function() { let context = this let args = arguments if(!timeout) clearTimeout(timeout) timeout = setTimeout(() => { fn.apply(context, args) }, time) } }
节流的原理是,通过闭包缓存当前时间,当下一步重复操作的时间减去缓存时间大于参数时间时,那么就会直接执行函数,控制在规定的时间内执行函数。
// 节流(频繁操作只会每隔一段时间操作一次) function throttle(fn, time) { let tempTime = Date.now() return function() { let currentTime = Date.now() if(currentTime - tempTime > time) { fn.apply(this, arguments) tempTime = currentTime } } }
函数柯里化的原理,就是将多个参数的单一函数转化为单一参数的多个函数。
在实现函数柯里化中,通过使用递归形式来将多个单一参数转换为多个参数为止。
如:sum(a, b, c, d) 相当于 sum(a)(b)(c)(d),也相当于 sum(a, b)(c)(d)
// 函数柯里化 function curry(fn) { const judge = (...args) => args.length >= fn.length ? fn(...args) : (...arg) => judge(...args, ...arg) return judge }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
好的文章可参考如下。
各种源码实现,你想要的这里都有
当下不管是在面试过程中还是在日常开发过程中,一些底层的实现变得越来越有效率性,那么当理解如何实现时,能否直接写出一份实现呢?
为此,我特意写了一些自己的理解总结,便于后期的回顾或分享。😄
Bind 函数实现
实现 Bind 函数的关键点在于两点。
答案
Call 和 Apply 函数实现
实现 Call 函数和 Apply 函数,主要体现在两点。
答案
Reduce 函数实现
实现 Reduce 函数,主要体现在两点。
答案
New 实现
new 内部机制主要经历以下四个步骤:
因此要实现一个 new 方法,那么必须抓住两点。
答案
需要注意的是,new 是属于关键字,不能被重写,因此只能使用函数来模拟实现 new 的功能。
async/await 实现
async/await 是 generator 语法糖,基于 promise 进行编写的。由于 async/await 都是属于关键字,不能被重写,只能通过使用函数来模拟实现。
要实现 async/await 函数,必须遵循下列几点要求。
答案
双向数据绑定的实现
在 JavaScript 为双向数据绑定的实现提供了两个 API,分别对应于 ES5 版本的
Object.defineProperty
和 ES6 版本Proxy
。Object.defineProperty版本
答案
Proxy版本
答案
Object.create方法实现
实现 Object.create 方法,主要是利用寄生式继承。
简单理解,就是先构建一个空构造函数,接着将参数对象赋给构造函数的原型对象,并将原型对象中 constructor 指回空构造函数,最后返回空构造函数实例。
答案
instanceof 实现
instanceof 的机制就是判断右边的构造函数的原型对象是否在左边对象的__proto__的原型链上。
要实现 instanceof,那么需要注意的是,利用遍历一直寻找左边对象的__proto__,若等于右边构造函数的原型对象,则返回 true,否则直到遍历为 null 为止。
答案
Object.getOwnPropertyNames方法实现
Object.getOwnPropertyNames 用于获取一个对象的所有属性名,不包括原型对象上的。
那么,要实现 Object.getOwnPropertyNames 方法,可结合使用 in 操作符和 Object.hasOwnProperty 实现。
答案
Promise 实现
Promise 拥有三种状态,分别是pending、fullfilled、rejected。
而 Promise 的实现原理就是,通过队列形式控制每个成功回调以及失败回调的处理,内部使用私有变量保存当前处理状态以及处理所得到的值。接下来一一分析每个方法的实现要点。
静态resolve方法。
返回一个Promise实例,先判断当前状态是否为pending,若不是直接返回。
然后判断参数值是否为Promise实例,若是则必须调用then方法等待其调用完毕才能进行下一步。
最后需要对成功回调队列和失败回调队列中进行清空处理(即使用shift方法,一一执行其回调函数)。
上述操作必须在一个setTimeout中进行。
静态reject方法。
返回一个Promise实例,先判断当前状态是否为pending,若不是直接返回。
无需判断为Promise实例,直接对失败回调队列进行清空处理(即使用shift方法,一一执行其回调函数)。
上述操作必须在一个setTimeout中进行。
then方法。
返回一个Promise实例,先创建两个处理函数(分别为成功处理函数和失败处理函数)。
成功处理函数中操作便是判断then方法的第一个参数是否为函数,若是则先执行其函数(其中内部值作为其函数的参数),接着再执行静态resolve方法清空成功回调队列操作。
失败处理函数中操作便是判断then方法的第二个参数是否为函数,若是则先执行其函数(其中内部值作为其函数的参数),接着再执行静态reject方法清空失败回调队列操作。
根据状态判断,当内部状态为pending时,则成功回调队列push进成功处理函数,失败回调队列push进失败处理函数,当内部状态为fullfilled时,则直接执行成功处理函数(其中内部值作为其函数的参数),当内部状态为rejected时,则直接执行失败回调处理函数(其中内部值作为其函数的参数)。
catch方法。
相当于直接执行this.then(undefined, rejectedFun)。
finally方法。
相当于直接执行this.then(val => MyPromise.resolve(callback()).then(() => val), val => MyPromise.reoslve(callback).then(() => { throw val }))
静态all方法。
返回一个Promise实例,创建一个空数组,遍历参数Promise数组,执行每一个Promise的then方法,然后将处理好的值push进空数组中,当判断数组长度与参数Promise数组长度一致时即可使用reoslve将处理好的数组返回。
静态race方法。
返回一个Promise实例,遍历参数Promise数组,执行每一个Promise的then方法,一旦有值返回即可直接resolve返回。
答案
防抖/节流实现
防抖的原理是,通过闭包缓存定时器,当缓存的定时器不为空时,则使用clearTimeout进行清除,然后再重新赋值为一个setTimeout,频繁操作中直到最后一步操作才会真正起效,前面的操作都会被清除掉。
答案
节流的原理是,通过闭包缓存当前时间,当下一步重复操作的时间减去缓存时间大于参数时间时,那么就会直接执行函数,控制在规定的时间内执行函数。
答案
函数柯里化实现
函数柯里化的原理,就是将多个参数的单一函数转化为单一参数的多个函数。
在实现函数柯里化中,通过使用递归形式来将多个单一参数转换为多个参数为止。
如:sum(a, b, c, d) 相当于 sum(a)(b)(c)(d),也相当于 sum(a, b)(c)(d)
答案
The text was updated successfully, but these errors were encountered: