手写代码集合

  • 1、节流和防抖
  • 节流
  • 防抖
  • 2、改变this指向的函数
  • bind
  • apply
  • call
  • 3、promise
  • 概念
  • 状态
  • 实现一个promise
  • promise.all
  • promise.race
  • 4、深拷贝
  • 5、发布订阅模式
  • 6、new
  • 7、Object.create
  • 8、Ajax
  • 9、JSONP
  • 10、路由
  • 11、函数柯里化
  • 12、instanceof
  • 13、数组
  • 数组扁平化
  • 数组去重
  • map
  • reduce
  • forEach
  • 14、sleep
  • 15、用 setTimeout 实现 setInterval
  • 16、图片加载
  • 懒加载
  • 滚动加载



1、节流和防抖

节流

在 s 秒内即使被触发多次,也只能执行一次。

function throttle(fn, delay){
  let flag = true;
  return function (...args){
    if(!flag){
      return;
    }
    flag = false;
    setTimeout(() => {
      fn.apply(this, args);
      flag = true;
    }, delay)
  }
}

防抖

s 秒内函数只会被执行一次,如果 s 秒内多次被触发,则以最后一次触发为标准重新计算延迟时间。

function debounce(fn, delay){
  let timer;
  return function(...args){
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay)
  }
}

2、改变this指向的函数

bind

Function.prototype.bind = function (context){
  context = context || window;
  context.fn = this;
  let args = [... arguments].slice(1);
  return function func() {
    // 判断是否被当做构造函数使用
    if(this instanceof func) {
      return new context.fn(...args, ...arguments);
    }
    return context.fn.apply(context,args.concat(...arguments));
  }
}

apply

Function.prototype.call = function (context){
  context = context || window;
  context.fn = this;
  let result;
  let args = [...arguments].splice(1);
  if(arguments[1]){
    result = context.fn(...args);
  } else {
    result = context.fn();
  }
  delete context.fn;
  return result;
}

call

Function.prototype.apply = function (context){
  context = context || window;
  context.fn = this;
  let args = [...arguments].splice(1);
  let result = context.fn(...args);
  delete context.fn;
  return result;
}

3、promise

概念

promise 对象是一个代理对象,被代理的值在 promise 对象创建时可能是未知的,可以为异步操作的成功和失败分别绑定相应的处理方法。 让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象。解决了异步回调地狱问题。

状态

1、pending:进行中
2、fulfilled:已成功
3、rejected:失败
只有异步操作才能决定当前的状态,其他操作无法改变。
一旦状态确定,就不会再改变,这时候称为定型 resolved,状态变化情况:pending ——> fulfilled,pending ——> rejected。

实现一个promise

class Promise{
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.resolveCallback = [];
    this.rejectCallback = [];

    function resolve(value) {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.value = value;
        this.resolveCallback.forEach(fn => {
          fn();
        })
      }
    }

    function reject(reason) {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
        this.rejectCallback.forEach(fn => {
          fn();
        })
      }
    }

    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  then(onFulfilled, onRejected){
    return new Promise((resolve, reject) => {
      if (this.status === 'fulfilled') {
        setTimeout(() => {
          const x = onFulfilled(this.value)
          resolvePromise(promise2, x, resolve, reject);
        }, 0)
      }
      if (this.status === 'rejected') {
        setTimeout(() => {
          const x = onRejected(this.reason)
          resolvePromise(promise2, x, resolve, reject);
        }, 0)
      }
      if (this.status === 'pending') {
        this.resolveCallback.push(() => {
          setTimeout(() => {
            const x = onFulfilled(this.value)
            onFulfilled(promise2, x, resolve, reject);
          }, 0)
        })
        this.resolveCallback.push(() => {
          setTimeout(() => {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject);
          }, 0)
        })
      }
    })
  }
}

const resolvePromise = (promise, x, resolve, reject) => {
  if(promise === x){
    throw TypeError('循环')
  }
  if(typeof x === 'function' || (typeof x === 'object' && x !== null)){
    try{
      const then = x.then;
      if(typeof then === 'function'){
        then.call(x, y => {
          resolvePromise(promise, y, resolve, reject);
        }, r => {
          reject(r);
        })
      }
    } catch (e){
      reject(e);
    }
  } else {
    resolve(x);
  }
}

promise.all

解决并发问题,多个异步并发获取最终的结果,如果有一个失败则失败。

Promise.all = function (iterators){
  let count = 0,
      res = [];
  return new Promise((resolve, reject) => {
    for(let i=0; i<iterators.length; i++){
      Promise.resolve(iterators[i]).then((data) => {
        res[i] = data;
        if(++count === iterators.length){
          resolve(res);
        }
      }).catch(e => {
        reject(e);
      })
    }
  })
}

promise.race

Promise.race = function (iterators){
  return new Promise((resolve, reject) => {
    for(let p in iterators){
      Promise.resolve(p).then((res) => {
        resolve(res);
      }).catch(e => {
        reject(e);
      })
    }
  })
}

4、深拷贝

function deepClone(obj) {
  // 过滤一些特殊情况
  if(obj === null) {
    return null;
  }
  if(typeof obj !== "object") {
    return obj;
  }
  if(obj instanceof RegExp) { // 正则
    return new RegExp(obj);
  }
  if(obj instanceof Date) { // 日期
    return new Date(obj);
  }
  var newObj = obj instanceof Array ? []:{};
  for(let key in obj) {
    if(obj.hasOwnProperty(key)) {
      newObj[key] = deepClone(obj[key]);
    }
  }
  return newObj;
}

5、发布订阅模式

class Publisher{
  constructor() {
    this.watcher = {};
  }

  on(event, fn){
    if(!this.watcher[event]){
      this.watcher[event] = [];
    }
    this.watcher[event].push(fn);
  }

  emit(event, ...args){
    if(!this.watcher[event]){
      return;
    }
    this.watcher[event].forEach(fn => {
      fn.apply(fn, args);
    });
  }

  off(event, fn){
    if(!this.watcher[event]){
      return;
    }
    let index = this.watcher[event].indexOf(fn);
    this.watcher[event].splice(index, 1);
  }

  once(event, fn){
    function callback(){
      fn();
      this.off(event, fn);
    }
    this.on(event, callback);
  }
}

6、new

1、创建类实例:新建一个空对象,这个对象原型指向构造函数的 prototype,执行构造函数后返回这个对象。
2、初始化实例:构造函数被传入参数并调用,this 指向该实例的 obj。
3、如果是对象则返回结果,否则返回原始值。

function createNew(con, ...args) {
  let obj = {};
  obj.__proto__ = con.prototype;
  // 将构造函数中的this指向这个对象,并传递参数
  let result = con.apply(obj, args);
  return result instanceof Object ? result : obj
}

7、Object.create

1、创建一个临时性的构造函数。
2、将传入的对象作为这个构造函数的原型。
3、最后返回这个临时类型的一个新实例。

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

8、Ajax

1、创建Ajax对象。
2、向服务器发送请求,使用xmlHttpRequest对象的open和send方法。
3、监听状态变化,执行相应回调函数。
4、发送请求。

// 原生Ajax
function ajax(url){
  let xhr = new XMLHttpRequest();
  xhr.open('get', url);
  xhr.onreadystatechange = function (){
    if(xhr.status === 200 && xhr.readyState === 4){
      let result = xhr.responseText;
      let obj = JSON.parse(result);
    }
  }
  xhr.send();
}

// 封装为promise
var myAjax = function (url, data){
  return new Promise((resolve, reject) => {
    var xhr = new XMLHttpRequest();
    xhr.open('get', url);
    xhr.onreadystatechange = function (){
      if(xhr.status === 200 && xhr.readyState === 4){
        resolve(JSON.parse(xhr.responseText));
      } else if (xhr.readyState === 4 && xhr.status !== 200){
        reject('error');
      }
    }
    xhr.send(data);
  })
}

9、JSONP

JSONP 是 JSON 的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
JSONP 由回调函数数据组成。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的 JSON 数据。

function jsonp(url, data, callback) {
  // 处理json对象,拼接url
  data.callback = callback;
  let params = [];
  for (let item in data) {
    params.push(item + '=' + data[item]);
  }
  // 创建script元素
  let script = document.createElement('script');
  script.src = url + '?' + params.join('&');
  document.body.appendChild(script);
  // 返回promise
  return new Promise((resolve, reject) => {
    window[callback] = function (data){
      try {
        resolve(data);
      } catch (e) {
        reject(e)
      } finally {
        // 移除script元素
        script.parentNode.removeChild(script)
        console.log(script)
      }
    }
  })
}

10、路由

function Route(){
  this.route = new Map;
  this.currentUrl = '';
}
Route.prototype.registerRoute = function (url ,cb){
  this.route.set(url || '', cd || function (){});
}
Route.prototype.refreshRoute = function (){
  this.currentUrl = location.hash.slice(1) || '/';
  this.route.get(this.currentUrl || '')();
}
Route.prototype.init = function (){
  window.addEventListener('load', this.refreshRoute.bind(this), false);
  window.addEventListener('hashchange', this.refreshRoute.bind(this), false);
}
window.Route = new Route();
window.Route.init();

11、函数柯里化

柯里化:将接受多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回一个接收剩余参数的新函数。
作用:参数复用、提前返回和延迟执行。

function curry(fn) {
  var length = fn.length;
  var args = args || [];
  return function() {
    var newArgs = args.concat(Array.prototype.slice.call(arguments))
    // 目前收集到的参数小于最初的length,则继续收集参数
    if(newArgs.length < length) {
      return curry.call(this, fn, newArgs);
    } else {
      return fn.apply(this, newArgs);
    }
  }
}

12、instanceof

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

function instanceOf (left, right) {
  if (typeof left !== 'object' || left === null) {
    return false;
  }
  let proto = Object.getPrototypeOf(left);
  while (proto) {
    if (right.prototype === proto) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
  return false;
}

13、数组

数组扁平化

// for循环
function flatten(arr) {
  var array = [];
  for(var i = 0; i < arr.length; i ++) {
    if (Array.isArray(arr[i])) {
      array = array.concat(flatten(arr[i]));
    } else {
      array.push(arr[i]);
    }
  }
  return array;
}

// reduce
function flatten(arr) {
  return arr.reduce(function(array, item){
    return array.concat(Array.isArray(item) ? flatten(item) : item);
  }, [])
}

数组去重

var arr = [1,1,1,2,2,2,3,3,4,5];
var newArr = [];
// forEach
arr.forEach(item => {
  if(newArr.indexOf(item) === -1){
    newArr.push(item);
  }
})

// set
newArr = [...new Set(arr)];

map

Array.prototype.map = function (callback, context){
  let arr = [];
  for(var k=0; k<this.length; k++){
    arr.push(callback.call(context, this[k], k, this));
  }
  return arr;
}

reduce

Array.prototype.reduce = function (callback, value){
 for(var i=0; i<this.length; i++){
    value = callback(this[i], value);
  }
  return value;
}

forEach

Array.prototype.forEach= function (callback){
  for (var i = 0; i < this.length; i++) {
    callback(this[i])
  }
}

14、sleep

function sleep(time){
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  });
}

15、用 setTimeout 实现 setInterval

// 递归执行
function Interval(fn, time){
  function interval(){
    setTimeout(interval, time);
    fn();
  }
  setTimeout(interval, time);
}

// 规定次数
function Interval(fn, time, num){
  function interval(){
    if(typeof num === 'undefined' || num --> 0){
      setTimeout(interval, time);
      try{
        fn();
      } catch (e){
        num = 0;
        throw e.toString();
      }
    }
  }
  setTimeout(interval, time);
}

16、图片加载

懒加载

function lazy(){
  var imgs = document.getElementsByTagName('img');
  // 视口高度
  var viewHeight = document.documentElement.clientHeight;
  // 滚动条高度
  var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
  for(var i=0; i<imgs.length; i++){
    var offsetHeight = imgs[i].offsetTop;
    if(offsetHeight < viewHeight + scrollHeight){
       imgs[i].src = imgs[i].dataset.src;
    }
  }
}

滚动加载

window.addEventListener('scroll', function (){
  var clientHeight = document.documentElement.clientHeight;
  var scrollTop = document.documentElement.scrollTop;
  var scrollHeight = document.documentElement.scrollHeight;
  if(clientHeight + scrollTop >= scrollHeight){
   // 此时页面已滚动到底部
  }
}, false)

参考文章:https://zhuanlan.zhihu.com/p/119027881、、https://www.jianshu.com/p/98df7dc36cb8https://zhuanlan.zhihu.com/p/258068663