比较全的JS手写实现

479 阅读9分钟

1, this指向篇

call实现

es3:

Function.prototype.call2 = function(context) {
    var context = context || window;
    context.fn = this;
    var args = [];
    for (var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    var result = eval('context.fn(' + args + ')');
    delete result.fn;
    return result;
}

es5:

Function.prototype.call2 = function(context) {
    const fn = Symbol('fn');
    const args = [...arguments].slice(1);
    var context = context || window;
    context.fn = this;
    const result = context.fn(...args);
    delete context.fn;
    return result;
}

apply实现

es3:

Function.prototype.apply = function (context, arr) {
    context = context ? Object(context) : window; 
    context.fn = this;

    var result;
    // 判断是否存在第二个参数
    if (!arr) {
        result = context.fn();
    } else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')');
    }

    delete context.fn
    return result;
}

es5:

Function.prototype.apply = function (context, arr) {
    context = context ? Object(context) : window; 
    context.fn = this;
  
    let result;
    if (!arr) {
        result = context.fn();
    } else {
        result = context.fn(...arr);
    }
      
    delete context.fn
    return result;
}

bind实现

Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}

2, 构造函数原型

new的实现

//创建一个新对象;
//将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
//执行构造函数中的代码(为这个新对象添加属性);
// 返回新对象.
function myNew() {
    var obj = new Object();
    var Constructor = Array.prototype.shift.call(arguments);
    obj.__proto__ = Constructor.prototype;
    var ret = Constructor.apply(obj, arguments);
    return typeof ret === 'object' && ret !== null ? ret : obj;
}

instanceof的实现

function instance_of(L, R) {
    var O = R.prototype;
    L = L.__proto__;
    while(true) {
        if(L === null) return false;
        if(O === L) return true;
        L = L.__proto__;
    }
}

Object.create的实现

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

Object.create() = function create(prototype) {
  // 排除传入的对象是 null 和 非object的情况
    if (prototype === null || typeof prototype !== 'object') {
    throw new TypeError(`Object prototype may only be an Object: ${prototype}`);
    }
  // 让空对象的 __proto__指向 传进来的 对象(prototype)
  // 目标 {}.__proto__ = prototype
  function Temp() {};
  Temp.prototype = prototype;
  return new Temp;
}

Object.assign

Object.assign(target, source1, ..., sourceN), 用于将源对象的所有可枚举属性复制到目标对象(target),其实就是浅拷贝的过程。

if(typeof Object.assign2 != 'function') {
    Object.defineProperty(Object, 'assign2', {
        value: function(target) {
            'use strict';
            if(target == null) {
                throw new TypeError('Cannot convert undefined or null to object');
            }
            var to = Object(target);
            for (var index = 1; index < arguments.length; index++) {
                var nextSource = arguments[index];
                if(nextSource != null) {
                    for(var nextKey in nextSource) {
                        if(Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
            }
            return to;
        },
        writeable: true,
        configuration: true
    });
}

原生数据类型检测简易封装

Object.prototype.isType = function (type) {
  return function (params) {
    return Object.prototype.toString.call(params) === `[object ${type}]`
  }
}

let isString = Object.isType('String')
let isArray = Object.isType('Array')

console.log(isString(1)) // false
console.log(isString('hello')) // true

console.log(isArray(2)) // false
console.log(isArray(['hello'])) // true

if (!Array.isArray) {
    Array.isArray = function(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
   };
}

3, 克隆拷贝

浅拷贝

浅拷贝:只拷贝对象或数组的第一层内容

const shallClone = (target) => {
  if (typeof target === 'object' && target !== null) {
    const cloneTarget = Array.isArray(target) ? [] : {};
    for (let prop in target) {
      if (target.hasOwnProperty(prop)) { // 遍历对象自身可枚举属性(不考虑继承属性和原型对象)
        cloneTarget[prop] = target[prop];
    }
    return cloneTarget;
  } else {
    return target;
  }
}

深拷贝

深拷贝:深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。

简单版

JSON.parse(JSON.stringify(source));

深度版

function deepCopy(obj, cache = new WeakMap()) {
  if (!obj instanceof Object) return obj
  // 防止循环引用
  if (cache.get(obj)) return cache.get(obj)
  // 支持函数
  if (obj instanceof Function) {
    return function () {
      return obj.apply(this, arguments)
    }
  }
  // 支持日期
  if (obj instanceof Date) return new Date(obj)
  // 支持正则对象
  if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
  // 还可以增加其他对象,比如:Map, Set等,根据情况判断增加即可,面试点到为止就可以了

  // 数组是 key 为数字素银的特殊对象
  const res = Array.isArray(obj) ? [] : {}
  // 缓存 copy 的对象,用于处理循环引用的情况
  cache.set(obj, res)

  Object.keys(obj).forEach((key) => {
    if (obj[key] instanceof Object) {
      res[key] = deepCopy(obj[key], cache)
    } else {
      res[key] = obj[key]
    }
  });
  return res
}

// 测试
const source = {
  name: 'Jack',
  meta: {
    age: 12,
    birth: new Date('1997-10-10'),
    ary: [1, 2, { a: 1 }],
    say() {
      console.log('Hello');
    }
  }
}
source.source = source
const newObj = deepCopy(source)
console.log(newObj.meta.ary[2] === source.meta.ary[2]); // false
console.log(newObj.meta.birth === source.meta.birth); // false

4, 防抖截流

防抖

防抖:指触发事件后在规定时间内回调函数只能执行一次,如果在规定时间内触发了该事件,则会重新开始算规定时间。总结即延迟执行;

function debounce(fn, wait, immediate) {
    let timer = null;
    return function(...args) {
        // 立即执行的功能(timer为空表示首次触发)
        if (immediate && !timer) {
            fn.apply(this, args);
        }
        // 有新的触发,则把定时器清空
        timer && clearTimeout(timer);
        // 重新计时
        timer = setTimeout(() => {
            fn.apply(this, args);
        }, wait)
    }
}

截流

截流:当持续触发事件时,在规定时间段内只能调用一次回调函数。如果在规定时间内触发了该事件,则什么也不做,也不会重置定时器. 实现类型有时间戳版本和定时器版本;

定时器:

特点:n秒后开始执行第一次,停止触发后还会再执行一次;

function throttle(fn, wait) {
    let timer = null;
    return function(...args) {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, args);
                timer = null;
            }, wait)
        }
    }
}

时间戳:

特点:开始触发会立即执行,停止触发后不再执行;

function throttle(fn, wait) {
    let previous = 0;
    return function(...args) {
        let now = +new Date();
        if (now - previous > wait) {
            previous = now;
            fn.apply(this, args);
        }
    }
}

6, 数组对象

数组扁平化

//  使用flat
const res1 = arr.flat(Infinity);

// 使用reduce
const flatten = arr => {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, [])
}
const res4 = flatten(arr);


//使用递归
var arr = [1, [2, [3, 4]]];
function flatten(arr) {
    var result = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        if (Array.isArray(arr[i])) {
            result = result.concat(flatten(arr[i]))
        }
        else {
            result.push(arr[i])
        }
    }
    return result;
}


console.log(flatten(arr))

对象扁平化

function objectFlat(obj = {}) {
  const res = {}
  function flat(item, preKey = '') {
    Object.entries(item).forEach(([key, val]) => {
      const newKey = preKey ? `${preKey}.${key}` : key
      if (val && typeof val === 'object') {
        flat(val, newKey)
      } else {
        res[newKey] = val
      }
    })
  }
  flat(obj)
  return res
}

// 测试
const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
console.log(objectFlat(source));

数组去重

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]

利用set:

const res1 = Array.from(new Set(arr));

两层循环:

const unique1 = arr => {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        // 每删除一个树,j--保证j的值经过自加后不变。同时,len--,减少循环次数提升性能
        len--;
        j--;
      }
    }
  }
  return arr;
}

indexOf:

const unique2 = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
  }
  return res;
}

include:

const unique3 = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) res.push(arr[i]);
  }
  return res;
}

filter:

const unique4 = arr => {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
}

Map:

const unique5 = arr => {
  const map = new Map();
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!map.has(arr[i])) {
      map.set(arr[i], true)
      res.push(arr[i]);
    }
  }
  return res;
}

reduce实现

Array.prototype._reduce = function (callback, initVal) {
  let i = 0
  let result = initVal
  if (typeof initVal === 'undefined') {
    result = this[0]
    i++
  }

  for (i; i < this.length; i++) {
    result = callback(result, this[i])
  }
  return result
}

const arr = [1, 2, 3]
let result = arr._reduce((a, b) => {
  return a + b
}, 0)
console.log(result) // 6

push和pop实现

//Array的push底层实现是依赖于 数组的length属性的
Array.prototype.push2 = function(...rest){
  this.splice(this.length, 0, ...rest)
  return this.length;
}
Array.prototype.pop2 = function(){
  return this.splice(this.length - 1, 1)[0];
}

6, Promise

完整版

function Promise(executor) {
  let _this = this;
  this.state = "pending";
  this.value = undefined;
  this.reason = undefined;
  this.onResolvedCallbacks = [];
  this.onRejectedCallbacks = [];

  // 讓其處理器函數立即執行
  try {
    executor(resolve, reject)
  } catch (err) {
    reject(err)
  }

  function resolve(value) {
    if (_this.state === "pending") {
      _this.state = "resolved";
      _this.value = value;
      _this.onResolvedCallbacks.forEach(fn => fn(value))
    }
  }
  function reject(reason) {
    if (_this.state === "pending") {
      _this.state = "rejected";
      _this.reason = reason;
      _this.onRejectedCallbacks.forEach(fn => fn(reason))
    }
  }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
  onRejected = typeof onRejected === "function" ? onRejected : err => { throw err }

  let promise2 = new Promise((resolve, reject) => {
    if (this.state === "pending") {
      this.onResolvedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      })
      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject (e)
          }
        }, 0)
      })
    }
    if (this.state === "resolved") {
      setTimeout(() => {
        try {
          let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject)
        } catch (e) {
          reject(e)
        }
      }, 0)
    }

    if (this.state === "rejected") {
      setTimeout(() => {
        try {
          let x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject)
        } catch (e) {
          reject(e)
        }
      }, 0)
    }
  })
  return promise2;
}

Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}

Promise.resolve = function(val) {
  // 直接抛出一个成功状态的Promise
  return new Promise((resolve, reject) => {
    resolve(val)
  })
}

Promise.reject = function(val) {
  // 直接抛出一个拒绝状态的Promise
  return new Promise((resolve, reject) => {
    reject(val)
  })
}

// race方法
Promise.race = function(promises) {
  // return一个Promise
  return new Promise((resolve, reject) => {
    // 遍历执行promises
    for (let i = 0; i < promises.length; i++) {
      // then只要接收到状态改变,直接抛出
      promises[i].then(resolve, reject)
    }
  })
}

// all方法
Promise.all = function(arr) {
  // 只有一个目的 获取到所有的promise,都执行then,把结果放到数组,一起返回
  // 递归实现和遍历实现都可以

  // 用于存放每次执行后返回结果
  let aResult = []
  return new Promise(function(resolve, reject) {
    let i = 0
    // 开始逐次执行数组中的函数(重要)
    next()
    function next() {
      arr[i].then(function(res) {
        // 存储每次得到的结果
        aResult.push(res)
        i++
        if (i >= arr.length) {
          // 如果函数数组中的函数都执行完,便resolve
          resolve(aResult)
        } else {
          next()
        }
      })
    }
  })
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    reject( new TypeError('請避免Promise循環引用'))
  }
  let called;
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(x, (y) => {
          if (called) return;
          called = true;
          resolvePromise(promise2, 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)
  }
}

// promises-aplus-tests测试
Promise.defer = Promise.deferred = function() {
  let defer = {}
  defer.promise = new Promise((resolve, reject) => {
    defer.resolve = resolve
    defer.reject = reject
  })
  return defer
}
try {
  module.exports = Promise
} catch (e) {}

面试版

    function myPromise(constructor){
        let self=this;
        self.status="pending" //定义状态改变前的初始状态
        self.value=undefined;//定义状态为resolved的时候的状态
        self.reason=undefined;//定义状态为rejected的时候的状态
        function resolve(value){
            //两个==="pending",保证了状态的改变是不可逆的
           if(self.status==="pending"){
              self.value=value;
              self.status="resolved";
           }
        }
        function reject(reason){
            //两个==="pending",保证了状态的改变是不可逆的
           if(self.status==="pending"){
              self.reason=reason;
              self.status="rejected";
           }
        }
        //捕获构造异常
        try{
           constructor(resolve,reject);
        }catch(e){
           reject(e);
        }
    }
    myPromise.prototype.then=function(onFullfilled,onRejected){
       let self=this;
       switch(self.status){
          case"resolved":
            onFullfilled(self.value);
            break;
          case"rejected":
            onRejected(self.reason);
            break;
          default:       
       }
    }

Promise.all的實現

function promiseAll(promises) {
    return new Promise(function(resolve, reject) {
        let resolvedCounter = 0;
        let resolveNum = promises.length;
        var resolvedValues = new Array(resolveNum);
        for (let i = 0; i < resolveNum; i++) {
            Promise.resolve(promises[i]).then(function(value) {
                resolvedCounter++;
                resolvedValues[i] = value;
                if(resolvedCounter === resolveNum) {
                    return resolve(resolvedValues)
                }
            }, function(reason) {
                return reject(reason);
            })
        }
    })
}
var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
promiseAll([p1, p2, p3]).then(function (results) {
    console.log(results);  // [1, 2, 3]
});

7, Async/await

function _asyncToGenerator(fn) {
  return function() {
    var self = this,
      args = arguments;
    // 将返回值promise化
    return new Promise(function(resolve, reject) {
      // 获取迭代器实例
      var gen = fn.apply(self, args);
      // 执行下一步
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
      }
      // 抛出异常
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
      }
      // 第一次触发
      _next(undefined);
    });
  };
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    // 迭代器完成
    resolve(value);
  } else {
    // -- 这行代码就是精髓 --
    // 将所有值promise化
    // 比如 yield 1
    // const a = Promise.resolve(1) a 是一个 promise
    // const b = Promise.resolve(a) b 是一个 promise
    // 可以做到统一 promise 输出
    // 当 promise 执行完之后再执行下一步
    // 递归调用 next 函数,直到 done == true
    Promise.resolve(value).then(_next, _throw);
  }
}
const asyncFunc = _asyncToGenerator(function*() {
  const e = yield new Promise(resolve => {
    setTimeout(() => {
      resolve('e');
    }, 1000);
  });
  const a = yield Promise.resolve('a');
  const d = yield 'd';
  const b = yield Promise.resolve('b');
  const c = yield Promise.resolve('c');
  return [a, b, c, d, e];
});

asyncFunc().then(res => {
  console.log(res);
});

8, 设计模式

实现双向绑定

defineProperty版本

// 数据
const data = {
  text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// 数据劫持
Object.defineProperty(data, 'text', {
  // 数据变化 --> 修改视图
  set(newVal) {
    input.value = newVal;
    span.innerHTML = newVal;
  }
});
// 视图更改 --> 数据变化
input.addEventListener('keyup', function(e) {
  data.text = e.target.value;
});

proxy版本

// 数据
const data = {
  text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// 数据劫持
const handler = {
  set(target, key, value) {
    target[key] = value;
    // 数据变化 --> 修改视图
    input.value = value;
    span.innerHTML = value;
    return value;
  }
};
const proxy = new Proxy(data, handler);

// 视图更改 --> 数据变化
input.addEventListener('keyup', function(e) {
  proxy.text = e.target.value;
});

实现基本的Event Bus

class EventEmeitter {
  constructor() {
    this._events = this._events || new Map();
    this._maxListeners = this._maxListeners || 10;
  }
}

EventEmeitter.prototype.emit = function(type, ...args) {
  let handler;
  handler = this._events.get(type);
  if (Array.isArray(handler)) {
    for (let i = 0; i < handler.length; i++) {
      if (args.length > 0) {
        handler[i].apply(this, args);
      } else {
        handler[i].call(this);
      }
    }
  } else {
    if (args.length > 0) {
      handler.apply(this, args);
    } else {
      handler.call(this);
    }
  }

  return true;
};

EventEmeitter.prototype.addListener = function(type, fn) {
  const handler = this._events.get(type);
  if (!handler) {
    this._events.set(type, fn);
  } else if (handler && typeof handler === "function") {
    this._events.set(type, [handler, fn]);
  } else {
    handler.push(fn);
  }
};

EventEmeitter.prototype.removeListener = function(type, fn) {
  const handler = this._events.get(type);

  if (handler && typeof handler === "function") {
    this._events.delete(type, fn);
  } else {
    let postion;
    for (let i = 0; i < handler.length; i++) {
      if (handler[i] === fn) {
        postion = i;
      } else {
        postion = -1;
      }
    }
    if (postion !== -1) {
      handler.splice(postion, 1);
      if (handler.length === 1) {
        this._events.set(type, handler[0]);
      }
    } else {
      return this;
    }
  }
};
let emitter = new EventEmeitter()
emitter.addListener('ages', age => {
  console.log(age)
})
emitter.emit('ages', 18)

实现观察者模式

观察者模式(基于发布订阅模式) 有观察者,也有被观察者

class Subject { // 被观察者 学生
  constructor(name) {
    this.state = '开心的'
    this.observers = []; // 存储所有的观察者
  }
  // 收集所有的观察者
  attach(o){ // Subject. prototype. attch
    this.observers.push(o)
  }
  // 更新被观察者 状态的方法
  setState(newState) {
    this.state = newState; // 更新状态
    // this 指被观察者 学生
    this.observers.forEach(o => o.update(this)) // 通知观察者 更新它们的状态
  }
}

class Observer{ // 观察者 父母和老师
  constructor(name) {
    this.name = name
  }
  update(student) {
    console.log('当前' + this.name + '被通知了', '当前学生的状态是' + student.state)
  }
}

let student = new Subject('学生'); 

let parent = new Observer('父母'); 
let teacher = new Observer('老师'); 

// 被观察者存储观察者的前提,需要先接纳观察者
student. attach(parent); 
student. attach(teacher); 
student. setState('被欺负了');

9, 高阶函数

函数柯里化

**含义:**是给函数分步传递参数,每次传递部分参数,并返回一个更具体的函数接收剩下的参数,这中间可嵌套多层这样的接收部分参数的函数,直至返回最后结果。

// add的参数不固定,看有几个数字累计相加
function add (a,b,c,d) {
  return a+b+c+d
}

function currying (fn, ...args) {
  // fn.length 回调函数的参数的总和
  // args.length currying函数 后面的参数总和 
  // 如:add (a,b,c,d)  currying(add,1,2,3,4)
  if (fn.length === args.length) {  
    return fn(...args)
  } else {
    // 继续分步传递参数 newArgs 新一次传递的参数
    return function(...newArgs) {
      // 将先传递的参数和后传递的参数 结合在一起
      let allArgs = [...args, ...newArgs]
      return currying(fn, ...allArgs)
    }
  }
}

let fn1 = currying(add, 1, 2) // 3
let fn2 = fn1(3)  // 6
let fn3 = fn2(4)  // 10

偏函数

function partial(fn) {
    var args = [].slice.call(arguments, 1);
    return function() {
        var newArgs = args.concat([].slice.call(arguments));
        return fn.apply(this, newArgs);
    };
};

10, 其他

图片懒加载

        var viewHeight = document.documentElement.clientHeight;

        function lazyload() {
            var imgs = document.querySelectorAll('img[data-original][lazyload]');
            imgs.forEach(item => {

                if (item.dataset.original == '') {
                    return;
                }
                var rect = item.getBoundingClientRect();
                if (rect.bottom >= 0 && rect.top < viewHeight) {
                    var img = new Image();
                    img.src = item.dataset.original;
                    img.onload = function() {
                        item.src = img.src;
                    }
                    item.removeAttribute('data-original');
                    item.removeAttribute('lazyload')
                }
            })
        }
        lazyload();
        document.addEventListener('scroll', lazyload)

解析URL为对象

function parseParam(url) {
  const paramsStr = /.+\?(.+)$/.exec(url)[1];
  const paramsArr = paramsStr.split('&');
  let paramsObj = {};
  paramsArr.forEach(param => {
    if (/=/.test(param)) { // 处理有 value 的参数
      let [key, val] = param.split('='); // 分割 key 和 value
      val = decodeURIComponent(val); // 解码
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字

      if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else { // 如果对象没有这个 key,创建 key 并设置值
        paramsObj[key] = val;
      }
    } else { // 处理没有 value 的参数
      paramsObj[param] = true;
    }
  })

  return paramsObj;
}
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',
  id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
  city: '北京', // 中文需解码
  enabled: true, // 未指定值得 key 约定为 true
}
*/


封装异步加载图片方法

function loadImg(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function() {
            console.log('一张图片加载完成');
            resolve(img);
        };
        img.onerror = function() {
            rejecct(new Error('ccould not load image ad' + url));
        }
        img.src = url;
    })
}

红绿灯

红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次,交替进行

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}
const light = function(timer, cb) {
    return new Promise(resolve => {
        setTimeout(() => {
            cb();
            resolve()
        }, timer)
    })
}
const step = function() {
    Promise.resolve().then(() => {
        return light(3000, red)
    }).then(() => {
        return light(2000, green)
    }).then(() => {
        return light(1000, yellow)
    }).then(() => {
        return step()
    })
}
step();

setTimeout实现setInterval

function mySetInterval(fn, millisec,count){
  function interval(){
    if(typeof count ==='undefined'||count-- > 0){
      setTimeout(interval, millisec);
      try{
        fn()
      }catch(e){
        count = 0;
        throw e.toString();
      }
    }
  }
  setTimeout(interval, millisec)
}
const fn = function() {
    console.log(new Date());
}
mySetInterval(fn, 1000, 10);

使用promise+async await实现异步循环打印

let sleep = function(time, i) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(i);
        }, time);
    })
}
let start = async function() {
    for (let i = 0; i < 6; i++) {
        let result = await sleep(1000, i);
        console.log(result);
    }
}
start();

使用Promise实现每隔1秒输出1 2 3

const arr = [1, 2, 3];
arr.reduce((p, x) => {
    return p.then(() => {
        return new Promise(r => {
            setTimeout(() => r(console.log(x)), 1000)
        })
    })
}, Promise.resolve());

下划线命名改为驼峰命名

function toCamelCase(str) {
    if(typeof str !== 'string') {
        return str;
    }
    return str.split('_')
              .map(item => item.charAt(0).toUpperCase() + item.substr(1, item.length))
              .join('');
}
toCamelCase('stoney_is_my_name');

js 实现sleep函数

利用一个伪死循环阻塞主线程
function sleep(delay) {
  var start = (new Date()).getTime();
  while ((new Date()).getTime() - start < delay) {
    continue;
  }
}

function test() {
  console.log('111');
  sleep(2000);
  console.log('222');
}

定时器
function sleep1(ms, callback) {
    setTimeout(callback, ms)
}
//sleep 1s
sleep1(1000, () => {
    console.log(1000)
})

es6异步处理
const sleep = time => {
 return new Promise(resolve => setTimeout(resolve,time)
 ) } 
 sleep(1000).then(()=>{ console.log(1) })

es7---- async/await是基于Promise的,是进一步的一种优化
function sleep(time) {
	return new Promise(resolve =>
 	 setTimeout(resolve,time)
 ) } 
async function output() {
	let out = await sleep(1000); 
	console.log(1); 
	return out;
} 
output();

解构

// 将 destructuringArray([1, [2, 3], 4], "[a, [b], c]") => {a: 1, b: 2, c: 4}
const targetArray = [1, [2, 3], 4];
const formater = "[a, [b], c]";

const destructuringArray = (values, keys) => {
  try {
    const obj = {};
    if (typeof keys === 'string') {
      keys = JSON.parse(keys.replace(/\w+/g, '"$&"'));
    }
    const iterate = (values, keys) =>
      keys.forEach((key, i) => {
        if(Array.isArray(key)) iterate(values[i], key)
        else obj[key] = values[i]
      })    
    iterate(values, keys)   
    return obj;
  } catch (e) {
    console.error(e.message);
  }
}

主动中止Promise调用链

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => { // 异步操作
      resolve('start')
  }, 1000);
});

p1.then((result) => {
   console.log('a', result); 
   return Promise.reject('中断后续调用'); // 此时rejected的状态将直接跳到catch里,剩下的调用不会再继续
}).then(result => {
   console.log('b', result);
}).then(result => {
   console.log('c', result);
}).catch(err => {
   console.log(err);
});

// a start
// 中断后续调用

每次加三的数组

const addThree = (start, target, arr = []) => {
  if (start === target) {
    arr.push(start)
    return arr
  }
  if (start > target) {
    return arr
  }
  arr.push(start)
  return addThree(start += 3, target, arr)
}
console.log(addThree(3, 40))

字符串repeat实现

// 原生
'ni'.repeat(3); // 'ninini'

//一
String.prototype.repeatString1 = function (n) {
  return Array(n + 1).join(this);
}
console.log('ni'.repeatString1(3));

//二
String.prototype.repeatString2 = function (n) {
  return Array(n).fill(this).join('');
}
console.log('ni'.repeatString2(3));

渲染十万条数据

setTimeout(() => {
      const total = 100000
      const once = 20
      const loopCount = total / once
      let countOfRender = 0
      let ul = document.querySelector("ul");
      function add() {
        const fragment = document.createDocumentFragment();
        for (let i = 0; i < once; i++) {
          const li = document.createElement("li");
          li.innerText = Math.floor(Math.random() * total);
          fragment.appendChild(li);
        }
        ul.appendChild(fragment);
        countOfRender += 1;
        loop();
      }
      function loop() {
        if (countOfRender < loopCount) {
          window.requestAnimationFrame(add);
        }
      }
      loop();
    }, 0);

es5实现继承

寄生组合式继承 核心实现是:用一个 F 空的构造函数去取代执行了 Parent 这个构造函数。

function Parent(name) {
    this.name = name;
}
Parent.prototype.sayName = function() {
    console.log('parent name:', this.name);
}
function Child(name, parentName) {
    Parent.call(this, parentName);  
    this.name = name;    
}
function create(proto) {
    function F(){};
    F.prototype = proto;
    return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
    console.log('child name:', this.name);
}
Child.prototype.constructor = Child;

var parent = new Parent('father');
parent.sayName();
var child = new Child('son', 'father');
child.sayName();

rem实现原理

// 原始配置
function setRem () {
  let doc = document.documentElement
  let width = doc.getBoundingClientRect().width
  let rem = width / 75
  doc.style.fontSize = rem + 'px'
}
addEventListener("resize", setRem)