1. 实现 instanceof 运算符

instanceof 运算符用于检测构造函数的 prototype 属性是否呈现在某个实例对象的原型链上,运算符左侧是实例对象,右侧是构造函数。

const iInstanceof = function (left, right) {    let proto = Object.getPrototypeOf(left);     while (true) {        if (proto === null) return false;        if (proto === right.prototype) return true;        proto = Object.getPrototypeOf(proto);    }};

这是常见的实现,咱们也能够用 isPrototypeOf 实现

const iInstanceof = function (left, right) {    return right.prototype.isPrototypeOf(left)};

2. 实现 new 操作符

new 执行过程如下:

  1. 创立一个新对象;
  2. 新对象的[[prototype]]个性指向构造函数的prototype属性;
  3. 构造函数外部的this指向新对象;
  4. 执行构造函数;
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创立的新对象;
const iNew = function (fn, ...rest) {    let instance = Object.create(fn.prototype);    let res = fn.apply(instance, rest);    return res !== null && (typeof res === 'object' || typeof res === 'function') ? res : instance;};

3. 实现 Object.assign 办法

浅拷贝办法,只会拷贝源对象本身的且可枚举的属性(包含以 Symbol 为 key 的属性)到指标对象

const iAssign = function (target, ...source) {    if (target === null || target === undefined) {        throw new TypeError('Cannot convert undefined or null to object');    }    let res = Object(target);    for (let i = 0; i < source.length; i++) {        let src = source[i];        let keys = [...Object.keys(src), ...Object.getOwnPropertySymbols(src)];        for (const k of keys) {            if (src.propertyIsEnumerable(k)) {                res[k] = src[k];            }        }    }    return res;};// 放弃 assign 的数据属性统一Object.defineProperty(Object, 'iAssign', {    value: iAssign,    configurable: true,    enumerable: false,    writable: true});

4. bind 办法

扭转函数内 this 的值并且传参,返回一个函数

const iBind = function (thisArg, ...args) {    const originFunc = this;    const boundFunc = function (...args1) {        // 解决 bind 之后对返回函数 new 的问题        if (new.target) {            if (originFunc.prototype) {                boundFunc.prototype = originFunc.prototype;            }            const res = originFunc.apply(this, args.concat(args1));            return res !== null && (typeof res === 'object' || typeof res === 'function') ? res : this;        } else {            return originFunc.apply(thisArg, args.concat(args1));        }    };    // 解决length 和 name 属性问题    const desc = Object.getOwnPropertyDescriptors(originFunc);    Object.defineProperties(boundFunc, {        length: Object.assign(desc.length, {            value: desc.length < args.length ? 0 : (desc.length - args.length)        }),        name: Object.assign(desc.name, {            value: `bound ${desc.name.value}`        })    });    return boundFunc;};// 放弃 bind 的数据属性统一Object.defineProperty(Function.prototype, 'iBind', {    value: iBind,    enumerable: false,    configurable: true,    writable: true});

5. call 办法

用指定的 this 值和参数来调用函数

const iCall = function (thisArg, ...args) {    thisArg = (thisArg === undefined || thisArg === null) ? window : Object(thisArg);    let fn = Symbol('fn');    thisArg[fn] = this;    let res =  thisArg[fn](...args);    delete thisArg[fn];    return res;};// 放弃 call 的数据属性统一Object.defineProperty(Function.prototype, 'iCall', {    value: iCall,    configurable: true,    enumerable: false,    writable: true});

6. 函数柯里化

将一个多参数函数转化为多个嵌套的单参数函数。

const curry = function (targetFn) {    return function fn (...rest) {        if (targetFn.length === rest.length) {            return targetFn.apply(null, rest);        }  else {            return fn.bind(null, ...rest);        }    };};// 用法function add (a, b, c, d) {    return a + b + c + d;}console.log('柯里化:', curry(add)(1)(2)(3)(4)); // 柯里化: 10

7. 函数防抖 debounce 办法

const debounce = function (func, wait = 0, options = {    leading: true,    context: null}) {    let timer;    let res;    const _debounce = function (...args) {        options.context || (options.context = this);        if (timer) {           clearTimeout(timer);        }        if (options.leading && !timer) {            timer = setTimeout(() => {                timer = null;            }, wait);            res = func.apply(options.context, args);        } else {            timer = setTimeout(() => {               res = func.apply(options.context, args);               timer = null;           }, wait);        }        return res;    };    _debounce.cancel = function () {        clearTimeout(timer);        timer = null;    };    return _debounce;};

leading 示意进入时是否立刻执行,如果在wait 工夫内触发事件,则会将上一个定时器革除,并从新再设置一个 wait 工夫的定时器。

8. 函数节流 throttle 办法

const throttle = function (func, wait = 0, options = {    leading: true,    trailing: false,    context: null}) {    let timer;    let res;    let previous = 0;    const _throttle = function (...args) {        options.context || (options.context = this);        let now = Date.now();        if (!previous && !options.leading) previous = now;        if (now - previous >= wait) {            if (timer) {                clearTimeout(timer);                timer = null;            }            res = func.apply(options.context, args);            previous = now;        } else if (!timer && options.trailing) {            timer = setTimeout(() => {                res = func.apply(options.context, args);                previous = 0;                timer = null;           }, wait);        }        return res;    };    _throttle.cancel = function () {        previous = 0;        clearTimeout(timer);        timer = null;    };    return _throttle;};

函数节流就像水龙头滴水一样,距离 wait 工夫就会触发一次,这里相比函数防抖新增了 trailing 选项,示意是否在最初额定触发一次。

9. 事件公布订阅(EventBus 事件总线)

class EventBus {    constructor () {        Object.defineProperty(this, 'handles', {            value: {}        });    }    on (eventName, listener) {        if (typeof listener !== 'function') {            console.error('请传入正确的回调函数');            return;        }        if (!this.handles[eventName]) {            this.handles[eventName] = [];        }        this.handles[eventName].push(listener);    }    emit (eventName, ...args) {        let listeners = this.handles[eventName];        if (!listeners) {            console.warn(`${eventName}事件不存在`);            return;        }        for (const listener of listeners) {            listener(...args);        }    }    off (eventName, listener) {        if (!listener) {            delete this.handles[eventName];            return;        }        let listeners = this.handles[eventName];        if (listeners && listeners.length) {            let index = listeners.findIndex(item => item === listener);            listeners.splice(index, 1);        }    }    once (eventName, listener) {        if (typeof listener !== 'function') {            console.error('请传入正确的回调函数');            return;        }        const onceListener = (...args) => {            listener(...args);            this.off(eventName, listener);        };        this.on(eventName, onceListener);    }}

自定义事件的时候用到,留神一些边界的查看

10. 深拷贝

const deepClone = function (source) {    if (source === null || typeof source !== 'object') {        return source;    }    let res = Array.isArray(source) ? [] : {};    for (const key in source) {        if (source.hasOwnProperty(key)) {            res[key] = deepClone(source[key]);        }    }    return res;};

这个是深拷贝的很根底版本,其中存在一些问题,比方循环援用,比方递归爆栈,前面我会专门写一篇文章来展开讨论。

11. 实现 ES6 的Class

用构造函数模仿,class 只能用 new 创立,不能够间接调用,另外留神一下属性的描述符

const checkNew = function (instance, con) {    if (!(instance instanceof con)) {        throw new TypeError(`Class constructor ${con.name} cannot be invoked without 'new'`);    }};const defineProperties = function (target, obj) {    for (const key in obj) {        Object.defineProperty(target, key, {            configurable: true,            enumerable: false,            value: obj[key],            writable: true        });    }};const createClass = function (con, proto, staticAttr) {    proto && defineProperties(con.prototype, proto);    staticAttr && defineProperties(con, staticAttr);    return con;};// 用法function Person (name) {    checkNew(this, Person);    this.name = name;}var PersonClass = createClass(Person, {    getName: function () {        return this.name;    }}, {    getAge: function () {}});

12. 实现 ES6 的继承

ES6 外部应用寄生组合式继承,首先用 Object.create 继承原型,并传递第二个参数以将父类构造函数指向本身,同时设置数据属性描述符。

而后用 Object.setPrototypeOf 继承动态属性和静态方法。

const inherit = function (subType, superType) {     // 对 superType 进行类型判断    if (typeof superType !== "function" && superType !== null) {        throw new TypeError("Super expression must either be null or a function");    }    subType.prototype = Object.create(superType && superType.prototype, {        constructor: {            configurable: true,            enumerable: false,            value: subType,            writable: true        }    });    // 继承静态方法    superType && Object.setPrototypeOf(subType, superType);};// 用法function superType (name) {    this.name = name;}superType.staticFn = function () {    console.log('staticFn');}superType.prototype.getName = function () {    console.log('name: ' + this.name);}function subType (name, age) {    superType.call(this, name);    this.age = age;}inherit(subType, superType);// 必须在继承之后再往 subType 中增加原型办法,否则会被笼罩掉subType.prototype.getAge = function () {    console.log('age: ' + this.age);}let subTypeInstance = new subType('Twittytop', 29);subType.staticFn();subTypeInstance.getName();subTypeInstance.getAge();

13. 图片懒加载

// 获取窗口高度function getWindowHeight () {    return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;}function getTop (e) {    let t = e.offsetTop;    while (e = e.offsetParent) {        t += e.offsetTop;    }    return t;}const delta = 30;let count = 0;function lazyLoad (imgs) {    const winH = getWindowHeight();    const s = document.documentElement.scrollTop || document.body.scrollTop;    for (let i = 0, l = imgs.length; i < l; i++) {        if (winH + s + delta > getTop(imgs[i]) && getTop(imgs[i]) + imgs[i].offsetHeight + delta > s) {            if (!imgs[i].src) {                imgs[i].src = imgs[i].getAttribute('data-src');                count++;            }            if (count === l) {                window.removeEventListener('scroll', handler);                window.removeEventListener('load', handler);            }        }    }    }const imgs = document.querySelectorAll('img');const handler = function () {    lazyLoad(imgs);};window.addEventListener('scroll', handler);window.addEventListener('load', handler);

当然你也能够用 getBoundingClientRect 办法:

// 获取窗口高度function getWindowHeight () {    return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;}const delta = 30;let count = 0;function lazyLoad (imgs) {    const winH = getWindowHeight();    for (let i = 0, l = imgs.length; i < l; i++) {        const rect = imgs[i].getBoundingClientRect();        if (winH + delta > rect.top && rect.bottom > -delta) {            if (!imgs[i].src) {                imgs[i].src = imgs[i].getAttribute('data-src');                count++;            }            if (count === l) {                window.removeEventListener('scroll', handler);                window.removeEventListener('load', handler);            }        }    }    }const imgs = document.querySelectorAll('img');const handler = function () {    lazyLoad(imgs);};window.addEventListener('scroll', handler);window.addEventListener('load', handler);

当然你也能够用 IntersectionObserver 办法:

function lazyLoad (imgs) {    let options = {        rootMargin: '30px'    };    let count = 0;    let observer = new IntersectionObserver(entries => {        entries.forEach(entry => {            if (entry.intersectionRatio > 0) {                entry.target.src = entry.target.getAttribute('data-src');                count++;                observer.unobserve(entry.target);                if (count === imgs.length) {                    window.removeEventListener('load', handler);                }            }        });    }, options);    for (let i = 0; i < imgs.length; i++) {        observer.observe(imgs[i]);    }}const imgs = document.querySelectorAll('img');const handler = function () {    lazyLoad(imgs);};window.addEventListener('load', handler);

14. 实现Object.is 办法

Object.is() 和 === 的区别是 Object.is(0, -0) 返回 false, Object.is(NaN, NaN) 返回 true。

const iIs = function (x, y) {    if (x === y) {        return x !== 0 || 1 / x === 1 / y;    } else {        return x !== x && y !== y;    }}// 放弃 is 的数据属性统一Object.defineProperty(Function.prototype, 'iIs', {    value: iIs,    configurable: true,    enumerable: false,    writable: true});

15. 工夫切片

把长工作切割成多个小工作,应用场景是避免一个工作执行工夫过长而阻塞线程

function ts (gen) {    if (typeof gen === 'function') gen = gen();    if (!gen || typeof gen.next !== 'function') return;    (function next() {        const start = performance.now();        let res = null;        do {            res = gen.next();        } while(!res.done && performance.now() - start < 25)        if (res.done) return;        setTimeout(next);    })();}// 用法ts(function* () {    const start = performance.now();    while (performance.now() - start < 1000) {        yield;    }    console.log('done!');});

16. CO (协程)实现

function co (gen) {    return new Promise(function (resolve, reject) {        if (typeof gen === 'function') gen = gen();        if (!gen || typeof gen.next !== 'function') return resolve(gen);        onFulfilled();                function onFulfilled (res) {            let ret;            try {                ret = gen.next(res);            } catch (e) {                return reject(e);            }            next(ret);        }                function onRejected (err) {            let ret;            try {                ret = gen.throw(err);            } catch (e) {                return reject(e);            }            next(ret);        }                function next (ret) {            if (ret.done) return resolve(ret.value);            let val = Promise.resolve(ret.value);            return val.then(onFulfilled, onRejected);        }    });}// 用法co(function* () {    let res1 = yield Promise.resolve(1);    console.log(res1);    let res2 = yield Promise.resolve(2);    console.log(res2);    let res3 = yield Promise.resolve(3);    console.log(res3);    return res1 + res2 + res3;}).then(value => {    console.log('add: ' + value);}, function (err) {    console.error(err.stack);});

co 承受一个生成器函数,当遇到 yield 时就暂停执行,交出控制权,当其余程序执行结束后,将后果返回并从中断的中央继续执行,如此往返,始终到所有的工作都执行结束,最初返回一个 Promise 并将生成器函数的返回值作为 resolve 值。

咱们将 * 换成 async,将 yield 换成 await 时,就和咱们常常用的 async/await 是一样的,所以说 async/await 是生成器函数的语法糖。

17. 单例模式

const getSingleton = function (fn) {    let instance;    return function () {        return instance || (instance = new (fn.bind(this, ...arguments)));    };};// 用法function Person (name) {    this.name = name;}let singleton = getSingleton(Person);let instance1 = new singleton('Twittop1');let instance2 = new singleton('Twittop2');console.log(instance1 === instance2); // true

当然你也能够用 ES6 的 Proxy 实现:

const getSingleton = function (fn) {    let instance;    const handler = {        construct (target, argumentsList) {           return instance || (instance = Reflect.construct(target, argumentsList));         }    }    return new Proxy(fn, handler);};// 用法function Person (name) {    this.name = name;}let singleton = getSingleton(Person);let instance1 = new singleton('Twittop1');let instance2 = new singleton('Twittop2');console.log(instance1 === instance2); // true

18. Promise

function isFunction (obj) {    return typeof obj === 'function';}function isObject (obj) {    return !!(obj && typeof obj === 'object');}function isPromise (obj) {    return obj instanceof Promise;}function isThenable (obj) {    return (isFunction(obj) || isObject(obj)) && 'then' in obj;}function transition (promise, state, result) {    // 一旦变成非 pending 状态,就不可逆    if (promise.state !== 'pending') return;    promise.state = state;    promise.result = result;    setTimeout(() => promise.callbacks.forEach(callback => handleCallback(callback, state, result)));}function resolvePromise (promise, result, resolve, reject) {    if (promise === result) {        return reject(new TypeError('Chaining cycle detected for promise'));    }     if (isPromise(result)) {        return result.then(resolve, reject);    }     if (isThenable(result)) {      try {        let then = result.then;        if (isFunction(then)) {          return new Promise(then.bind(result)).then(resolve, reject);        }      } catch (error) {        return reject(error);      }    }    resolve(result);}function handleCallback (callback, state, result) {    let { onFulfilled, onRejected, resolve, reject } = callback;    try {        if (state === 'fulfilled') {            isFunction(onFulfilled) ? resolve(onFulfilled(result)) : resolve(result);        } else if (state === 'rejected') {            isFunction(onRejected) ? resolve(onRejected(result)) : reject(result);        }    } catch (e) {        reject(e);    }}class Promise {    constructor (executor) {        this.state = 'pending';        this.result = undefined;        this.callbacks = [];        let onFulfilled = value => transition(this, 'fulfilled', value);        let onRejected = reason => transition(this, 'rejected', reason);        // 保障 resolve 或 reject 只有一次调用        let flag = false;        let resolve = value => {            if (flag) return;            flag = true;            resolvePromise(this, value, onFulfilled, onRejected);        };        let reject = reason => {            if (flag) return;            flag = true;            onRejected(reason);        };        try {           executor(resolve, reject);         } catch (e) {            reject(e);        }    }    then (onFulfilled, onRejected) {        return new Promise((resolve, reject) => {            let callback = { onFulfilled, onRejected, resolve, reject };            if (this.state === 'pending') {                this.callbacks.push(callback);            } else {                setTimeout(() => {                    handleCallback(callback, this.state, this.result);                });            }        });    }    catch (onRejected) {        this.then(undefined, onRejected);    }    // 无论胜利还是失败都会执行,个别都会传递前一个 promise 的状态,只有在 onFinally 抛出谬误(显示抛出或 reject)的时候才会返回一个 rejected 的 promise    finally (onFinally) {        return this.then(            val => Promise.resolve(onFinally()).then(() => val),            rea => Promise.resolve(onFinally()).then(() => { throw rea; })        );    }    static resolve (value) {        if (isPromise(value)) return value;        return new Promise ((resolve, reject) => resolve(value));    }    static reject (reason) {        return new Promise ((resolve, reject) => reject(reason));    }    // 当所有 promise 都返回 fulfilled 的时候,它才会返回一个 fulfilled 的 promise,外面蕴含了对应后果的数组,否则只有一个 promise 返回 rejected,它就会返回一个 rejected 的 promise,其中蕴含第一个 rejected 的 promise 抛出的错误信息    static all (iterable) {        return new Promise ((resolve, reject) => {            let count = 0;            let arr = [];            for (let i = 0, l = iterable.length; i < l; i ++) {                iterable[i].then(val => {                    count++;                    arr[i] = val;                    if (count === l) {                        reresolve(arr);                    }                }, reject);            }        });    }    // 只有有一个 promise 返回 fulfilled 或 rejected,它就会返回一个 fulfilled 或 rejected 的 promise    static race (iterable) {        return new Promise ((resolve, reject) => {            for (const p of iterable) {                p.then(resolve, reject);            }        });    }    // 当所有 promise 都 fulfilled 或 rejected 后,返回一个蕴含对应后果的数组    static allSettled (iterable) {        return new Promise ((resolve, reject) => {            let count = 0;            let arr = [];            function handle (state, index, result) {                arr[index] = {                    status: state,                    [state === 'fulfilled' ? 'value' : 'reason']: result                };                count++;                if (count === iterable.length) {                    resolve(arr);                }            }            for (let i = 0, l = iterable.length; i < l; i ++) {                iterable[i].then(val => handle ('fulfilled', i, val), rea => handle ('rejected', i, rea));            }        });    }    // 只有有一个 promise 胜利,就会返回一个胜利的 promise,否则返回一个 AggregateError 类型实例的失败 promise    static any (iterable) {        return new Promise ((resolve, reject) => {            let count = 0;            let arr = [];            for (let i = 0, l = iterable.length; i < l; i ++) {                iterable[i].then(resolve, rea => {                    count++;                    arr[i] = rea;                    if (count === l) {                        reject(new AggregateError(arr));                    }                });            }        });    }}

Promise 有三种状态 pending、fulfilled 和 rejected,pending 是最后的状态,一旦落定为 fulfilled 或 rejected 状态,就不可逆。且一旦执行 resolve 或 reject,前面的 resolve 或 reject 就不会失效。then 传入的回调函数有可能提早执行,所以需放到 callbacks 数组中,等状态变更的时候再取出执行。

参考资料

https://juejin.cn/post/684490...
https://github.com/berwin/tim...
CO 模块
100 行代码实现 Promises/A+ 标准