手写XXX

258 阅读2分钟

1. 柯里化

function curry(func) {

  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };

}

用例:

function sum(a, b, c) {
  return a + b + c;
}

let curriedSum = curry(sum);

alert( curriedSum(1, 2, 3) ); // 6,仍然可以被正常调用
alert( curriedSum(1)(2,3) ); // 6,对第一个参数的柯里化
alert( curriedSum(1)(2)(3) ); // 6,全柯里化

2. Function.prototype.call

Function.prototype.call = function (obj, ...args) {
	let res;
    if(!obj) {
        res = this(...args);
    } else {
        obj.fn = this;
        res = obj.fn(...args);
        delete obj.fn;
    }
    return res;
}

用例:

const person = {
    name: 'haha',
    selfTalk(age) {
        console.log(this.name, age);
    }
} 

const animal = {
    name: 'lion',
}

person.selfTalk.call(animal, 20)
console.log(animal);

3. promisify

function promisify(f) {
  return function (...args) { // 返回一个包装函数(wrapper-function)
    return new Promise((resolve, reject) => {
      function callback(err, result) { // 我们对 f 的自定义的回调
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      }

      args.push(callback); // 将我们的自定义的回调附加到 f 参数(arguments)的末尾

      f.call(this, ...args); // 调用原始的函数
    });
  };
};

用例:

let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);

4.删除数组元素

Array.prototype.remove = function(dx) {
  if(isNaN(dx) || dx > this.length){
    return false;
  }
 
  for(var i = 0, n = 0; i < this.length; i++) {
    if(this[i] != this[dx]) {
      this[n++] = this[i];
    }
  }
  this.length -= 1;
};

用例:

var colors = ["red", "blue", "grey"];
colors.remove(1);
console.log(colors); // ["red", "grey"]

5.EventHub

class EventHub {

    eventCenter = {}

    on(eventName, callback) {
        if (typeof callback !== 'function') {
            console.log('回调必须为函数');
            return
        }

        if (eventName in this.eventCenter) {
            this.eventCenter[eventName].eventList.push(callback);
        } else {
            this.eventCenter[eventName] = {
                name: eventName,
                eventList: [callback]
            };
        }
    }

    off(eventName, callback) {
        if (eventName in this.eventCenter) {
            this.eventCenter[eventName].eventList.forEach((fn, index) => {
                if(fn === callback) this.eventCenter[eventName].eventList.splice(index, 1)
            })
        } else {
            console.log('解绑的事件不存在')
        }
    }

    emit(eventName, ...args) {
        if (eventName in this.eventCenter) {
            this.eventCenter[eventName].eventList.forEach(fn => {
                fn(...args);
            })
        } else {
            console.log('触发的事件不存在')
        }
    }
}

用例:

const e = new EventHub();

function fn1() {
    console.log('clicked');
}

e.on('click', fn1)
e.on('click', function (p1, p2) {
    console.log('clicked2' + p1 + p2);
    
})
e.off('click', fn1);


e.emit('click', 'hello', 'world');

6.Promise

function resolve(result) {
    if (this.state !== Promise2.PENDING) return;
    this.state = Promise2.FULLFILLED;
    nextTick(() => {
        this.stack.forEach(({ success, fail, promise2 }) => {
            if (typeof success !== 'function') {
                resolve.call(promise2, result);
                return
            }
            try {
                const res = success(result);
                resolveWith(promise2, res);
            } catch (err) {
                reject.call(promise2, err);
            }
        })
    })
}

function reject(reason) {
    if (this.state !== Promise2.PENDING) return;
    this.state = Promise2.REJECTED;
    nextTick(() => {
        this.stack.forEach(({ success, fail, promise2 }) => {
            if (typeof fail !== 'function') {
                resolve.call(promise2, reason);
                return
            }
            try {
                console.log(2);
                const res = fail(reason);
                console.log(3);
                resolveWith(promise2, res);
            } catch (err) {
                reject.call(promise2, err);
            }
        })
    })
}

function resolveWith(promise, x) {
    if (promise === x) {
        reject.call(promise, new TypeError(''));
    } else if (x instanceof Promise2) {

        x.then((result) => {
            resolve.call(promise, result)
        }, (reason) => {
            reject.call(promise, reason)
        });
    } else if (typeof x === 'object') {
        let then
        try {
            // 因为then可能是一个 get 属性,所以有可能报错
            then = x.then;
            if (typeof then === 'function') {
                try {
                    then.call(x, (result) => {
                        resolveWith(promise, result)
                    }, (reason) => {
                        reject.call(promise, reason)
                    });
                } catch (err) {
                    reject.call(promise, err)
                }
            } else {
                resolve.call(promise, x);
            }
        } catch (err) {
            reject.call(promise, err);
        }
    } else {
        resolve.call(promise, x);
    }
}

export class Promise2 {
    static PENDING = 'PENDING'
    static FULLFILLED = 'FULLFILLED'
    static REJECTED = 'REJECTED'

    constructor(fun) {
        if (typeof fun !== 'function') {
            throw new Error('Promise参数必须是一个函数');
        }
        fun(resolve.bind(this), reject.bind(this));
    }

    private stack = []

    private state = Promise2.PENDING

    then(success?, fail?) {
        const promise2 = new Promise2(() => { });
        this.stack.push({
            success,
            fail,
            promise2
        })

        return promise2;
    }
}

const nextTick = function () {
    var callbacks = [];
    function resolveCallbacks() {
        const copys = callbacks.slice(0);
        copys.forEach(cb => {
            cb();
        });
        callbacks.length = 0;
    }
    if (typeof process !== undefined && process.nextTick) {
        return process.nextTick;
    } else {
        var counter = 1;
        var textNode = document.createTextNode(String(counter));
        var observer = new MutationObserver(resolveCallbacks);

        observer.observe(textNode, {
            characterData: true
        });
        return function (fun) {
            callbacks.push(fun);
            counter = (counter + 1) % 2;
            textNode.data = String(counter);
        }
    }
}()

7. JSONP

 const jsonp = function (url, data) {
      return new Promise((resolve, reject) => {
        // 初始化url
        let dataString = url.indexOf('?') === -1 ? '?' : '&'
        let callbackName = `jsonpCB_${Date.now()}`
        url += `${dataString}callback=${callbackName}`
        if (data) {
         // 有请求参数,依次添加到url
          for (let k in data) {
            url += `&${k}=${data[k]}`
          }
        }
        let jsNode = document.createElement('script')
        jsNode.src = url
        // 触发callback,触发后删除js标签和绑定在window上的callback
        window[callbackName] = result => {
          delete window[callbackName]
          document.body.removeChild(jsNode)
          if (result) {
            resolve(result)
          } else {
            reject('没有返回数据')
          }
        }
        // js加载异常的情况
        jsNode.addEventListener('error', () => {
          delete window[callbackName]
          document.body.removeChild(jsNode)
          reject('JavaScript资源加载失败')
        }, false)
        // 添加js节点到document上时,开始请求
        document.body.appendChild(jsNode)
      })
    }
    jsonp('http://192.168.0.103:8081/jsonp', {a: 1, b: 'heiheihei'})
      .then(result => { console.log(result) })
      .catch(err => { console.error(err) })