原生实现

177 阅读4分钟

array.map

Array.prototype.MyMap = function(fn, context){
  var arr = Array.prototype.slice.call(this);//由于是ES5所以就不用...展开符了
  var mappedArr = [];
  for (var i = 0; i < arr.length; i++ ){
    mappedArr.push(fn.call(context, arr[i], i, this));
  }
  return mappedArr;
}

array.reduce

Array.prototype.myReduce = function(fn, initialValue) {
  var arr = Array.prototype.slice.call(this);
  var res, startIndex;
  res = initialValue ? initialValue : arr[0];
  startIndex = initialValue ? 0 : 1;
  for(var i = startIndex; i < arr.length; i++) {
    res = fn.call(null, res, arr[i], i, this);
  }
  return res;
}

call/apply

Function.prototype.myCall = function(context = window, ...args) {
  let func = this;
  let fn = Symbol("fn");
  context[fn] = func;

  let res = context[fn](...args);//重点代码,利用this指向,相当于context.caller(...args)

  delete context[fn];
  return res;
}

Object.create

function create(proto) {
    function F() {};
    F.prototype = proto;
    F.prototype.constructor = F;
    
    return new F();
}

bind

Function.prototype.bind = function(context, ...args) {
    let self = this;//谨记this表示调用bind的函数
    let fBound = function() {
        //this instanceof fBound为true表示构造函数的情况。如new func.bind(obj)
        return self.apply(this instanceof fBound ? this : context || window, args.concat(Array.prototype.slice.call(arguments)));
    }
    fBound.prototype = Object.create(this.prototype);//保证原函数的原型对象上的属性不丢失
    return fBound;
}

new

function myNew(fn, ...args) {
    let instance = Object.create(fn.prototype);
    let res = fn.apply(instance, args);
    return typeof res === 'object' ? res: instance;
}

instanceof

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

promise

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";


class Promise{
  constructor(excutorCallBack){
    this.status = 'pending';
    this.value = undefined;
    this.fulfillAry = [];
    this.rejectedAry = [];
    //=>执行Excutor
    let resolveFn = result => {
      if(this.status !== 'pending') return;
      let timer = setTimeout(() => {
        this.status = 'fulfilled';
        this.value = result;
        this.fulfillAry.forEach(item => item(this.value));
      }, 0);
    };
    let rejectFn = reason => {
      if(this.status !== 'pending')return;
      let timer = setTimeout(() => {
        this.status = 'rejected';
        this.value = reason;
        this.rejectedAry.forEach(item => item(this.value))
      })
    };
    try{
      excutorCallBack(resolveFn, rejectFn);
    } catch(err) {
      //=>有异常信息按照rejected状态处理
      rejectFn(err);
    }
  }
  then(fulfilledCallBack, rejectedCallBack) {
    typeof fulfilledCallBack !== 'function' ? fulfilledCallBack = result => result:null;
    typeof rejectedCallBack !== 'function' ? rejectedCallBack = reason => {
      throw new Error(reason instanceof Error? reason.message:reason);
    } : null

    return new Promise((resolve, reject) => {
      this.fulfillAry.push(() => {
        try {
          let x = fulfilledCallBack(this.value);
          x instanceof Promise ? x.then(resolve, reject ):resolve(x);
        }catch(err){
          reject(err)
        }
      });
      this.rejectedAry.push(() => {
        try {
          let x = this.rejectedCallBack(this.value);
          x instanceof Promise ? x.then(resolve, reject):resolve(x);
        }catch(err){
          reject(err)
        }
      })
    }) ;
  }
  catch(rejectedCallBack) {
    return this.then(null, rejectedCallBack);
  }
  static all(promiseAry = []) {
    let index = 0, 
        result = [];
    return new Promise((resolve, reject) => {
      for(let i = 0; i < promiseAry.length; i++){
        promiseAry[i].then(val => {
          index++;
          result[i] = val;
          if( index === promiseAry.length){
            resolve(result)
          }
        }, reject);
      }
    })
  }
 static race(promiseAry) {
  return new Promise((resolve, reject) => {
    if (promiseAry.length === 0) {
      return;
    }
    for (let i = 0; i < promiseAry.length; i++) {
      promiseAry[i].then(val => {
        resolve(val);
        return;
      }, reject);
    }     
  })
}
static resolve (value) {
    if (value instanceof Promise) return value
    return new Promise(resolve => resolve(value))
}
static reject (value) {
    return new Promise((resolve, reject) => reject(value))
}
}

module.exports = Promise;

new

function create(constructor, ...args) {
  const obj = Object.create(constructor.prototype)
  const res = constructor.apply(obj, args)
  return res instanceof Object ? res : obj
}

jsonStringify

function stringify(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();
            }
        }

json parse

class Lexer {
  constructor() {
    this.chunk = '';
  }
  lex(input) {
    this.chunk = input;
    this.tokens = [];
    while(!this.readEOF()) {
      let consumed = this.readWhiteSpace()
                  || this.readString()
                  || this.readNumber()
                  || this.readBoolean()
                  || this.readNull()
                  || this.readLiteral()
      this.chunk = this.chunk.slice(consumed);
    }
    return this.tokens;
  }
  pushToken(type, value) {
    this.tokens.push([type, value]);
  }
  readEOF() {
    if (!this.chunk) {
      this.pushToken('EOF', '');
      return true;
    };
  }
  readWhiteSpace() {
    return this.chunk.match(/^\s*/)[0].length;
  }
  readString() {
    let match = this.chunk.match(/^"(?:[^"\\\x00-\x1F\x7F\x80-\x9F]|\\["\\/bfnrt]|\\u[0-9a-fA-F]{4})*"/);
    if (match) {
      this.pushToken('String', match[0]);
      return match[0].length;
    }
  }
  readNumber() {
    let match = this.chunk.match(/^-?[1-9]*\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/);
    if (match) {
      this.pushToken('Number', match[0]);
      return match[0].length;
    }
  }
  readBoolean() {
    let match = this.chunk.match(/^(?:true|false)/);
    if (match) {
      this.pushToken('Boolean', match[0].toLowerCase());
      return match[0].length;
    }
  }
  readNull() {
    let match = this.chunk.match(/^null/);
    if (match) {
      this.pushToken('Null', match[0]);
      return match[0].length;
    }
  }
  readLiteral() {
    const value = this.chunk.charAt(0);
    this.pushToken(value, value);
    return value.length;
  }
}

class Parser {
  constructor() {
    this.tokens = [];
  }
  parse(input) {
    this.i = 0;
    this.tokens = new Lexer().lex(input);
    const value = this.getValue();
    if (!value) throw new TypeError('failed to parse JSON, expect value');
    return value;
  }
  format(input, indent = '  ') {
    const ast = this.parse(input);
    return this._format(ast, indent);
  }
  _format(node, indent) {
    switch(node.type) {
      case 'String':
        return JSON.stringify(JSON.parse(node.value));
      case 'Number':
      case 'True':
      case 'False':
      case 'Null':
        return node.value;
    }
    let [begin, end] = node.type === 'Object' ? ['{', '}'] : ['[', ']'];
    let output = begin;
    const innerMembers = node.value;
    if (!innerMembers.length) return output + end;
    output += '\n';
    innerMembers.forEach((item, index, arr) => {
      output += indent;
      if (node.type === 'Object') output += item.key.value + ': ';
      const value = node.type === 'Object' ?
          this._format(item.value, indent).replace(/\n/g, '\n' + indent) :
          this._format(item, indent).replace(/\n/g, '\n' + indent);
        output += value;
        if (index !== arr.length - 1) output += ',\n';
    });
    return output += '\n' + end;
  }
  getValue() {
    return this.readString() || this.readNumber() || this.readObject() || this.readArray() || this['true']() || this['false']() || this['null']();
  }
  readString() {
    const [tag, value] = this.tokens[this.i];
    if (tag === 'String') {
      this.i++;
      return { type: 'String', value };
    }
  }
  readNumber() {
    const [tag, value] = this.tokens[this.i];
    if (tag === 'Number') {
      this.i++;
      return { type: 'Number', value };
    }
  }
  readObject() {
    const [tag] = this.tokens[this.i];
    if (tag === '{') {
      this.i++;
      const members = this.readMembers() || [];
      const [tag] = this.tokens[this.i];
      if (tag !== '}') throw new TypeError('failed to parse object, expect "}"');
      this.i++;
      return { type: 'Object', value: members };
    }
  }
  readMembers() {
    let pair = this.readPair();
    if (!pair) return [];
    let members = [ pair ];
    while (true) {
      const [tag] = this.tokens[this.i];
      if (tag !== ',') break;
      this.i++;
      pair = this.readPair();
      members.push(pair);
    }
    return members;
  }
  readPair() {
    const string = this.readString();
    if (!string) throw new TypeError('failed to parse pair, expect string');
    const [tag] = this.tokens[this.i];
    if (tag !== ':') throw new TypeError('failed to parse pair, expect colon');
    this.i++;
    const value = this.getValue();
    if (!value) throw new TypeError('failed to parse pair, expect value');
    return { key: string, value };
  }
  readArray() {
    const [tag] = this.tokens[this.i];
    if (tag === '[') {
      this.i++;
      const elements = this.readElements() || [];
      const [tag] = this.tokens[this.i];
      if (tag !== ']') throw new TypeError('failed to parse object, expect "]"');
      this.i++;
      return { type: 'Array', value: elements };
    }
  }
  
  readElements() {
    let value = this.getValue();
    if (!value) return [];
    let elements = [ value ];
    while (true) {
      const [tag] = this.tokens[this.i];
      if (tag !== ',') break;
      this.i++;
      value = this.getValue();
      elements.push(value);
    }
    return elements;
  }
  'true'() {
    const [tag, value] = this.tokens[this.i];
    if (tag === 'Boolean' && value === 'true') {
      this.i++;
      return { type: 'True', value };
    }
  }
  'false'() {
    const [tag, value] = this.tokens[this.i];
    if (tag === 'Boolean' && value === 'false') {
      this.i++;
      return { type: 'False', value };
    }
  }
  'null'() {
    const [tag, value] = this.tokens[this.i];
    if (tag === 'Null') {
      this.i++;
      return { type: 'Null', value };
    }
  }
}