js 要会手写的API,各个函数后面都有相应的用法(持续更新)

js 要会手写的API,各个函数后面有相应用法

1. 实现深拷贝

简易版深拷贝

function deepClone (obj = {
    
    }) {
    
    
  if (typeof obj !== "object" || obj === null) {
    
    
    return obj;
  }
  let result;
  if (obj instanceof Array) {
    
    
    result = [];
  } else if (obj instanceof Object) {
    
    
    result = {
    
    };
  }
  for (let key in obj) {
    
    
    if (obj.hasOwnProperty(key)) {
    
     // 判断当前这个key是在obj对象上,而不是在原型上
      result[key] = deepClone(obj[key]);
    }
  }
  return result;
}

let obj1 = {
    
    
  name: 'obj1',
  age: 18
};
let obj2 = deepClone(obj1);
obj2.age = 20;
console.log(obj1);
console.log(obj2);

2. 实现节流

节流:高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
思路:每次触发事件时都判断当前是否有等待执行的延时函数

function throttle(fn, delay = 200) {
    
    
  let timer = null;
  return function () {
    
    
    if (timer) {
    
    
        return ;
    }
    timer = setTimeout(() => {
    
    
        fn.call(this, arguments);
        timer = null;
    }, delay);
  }
}

// 下面以盒子拖拽为例,HTML代码就不写了
let box = document.getElementById('box');
box.addEventListener('drag', throttle(function (e) {
    
    
  console.log(e[0].offsetX, e[0].offsetY)
}), 100);

3. 实现防抖

防抖:触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
思路:每次触发事件时都取消之前的延时调用方法

function debounce(fn, delay = 1000) {
    
    
  let timer = null;
  return function() {
    
    
    if (timer) {
    
    
      clearTimeout(timer);  
    }
    timer = setTimeout(() => {
    
    
      fn.call(this, arguments);  
      timer = null;
    }, delay);
  }
}

// 下面以一个输入框防抖为例,HTML代码就不写了
let input1 = document.getElementById('input1');
input1.addEventListener('keyup', debounce(function() {
    
    
  console.log(input1.value);
}), 800)

4. 实现Object.create()

Object.create()方法用于创建一个新对象。被创建的对象继承另一个对象的原型,在创建新对象时可以指定一些属性。
注:使用Object.create()方法创建对象时,如果不是继承一个原有的对象,而是创建一个全新的对象,就要把proto设置为null,即 Object.create(null)

// 通过原型链__proto__来访问到属性
function create(proto) {
    
    
  function F() {
    
    }
  F.prototype = proto;
  F.prototype.constructor = F;
  return new F();
}
const person = {
    
    
  age: 1,
  name: 'xiaowu'  
};
let p1 = create(person);
console.log(p1);
console.log(p1.__proto__);

5. 实现new关键字

new 的实现思路:

  1. 创建一个新的对象,并继承了fn的原型,该对象也就是当前fn的实例
  2. 改变this指向,让其指向新创建处理的实例
  3. 返回值res为object类型则作为new方法的返回值返回,否则返回上述实例
function myNew(fn, ...args) {
    
    
  let obj = Object.create(fn.prototype);
  let res = fn.apply(obj, args);
  return res instanceof Object ? res : obj;
}
function Cat (name, age) {
    
    
  this.name = name;
  this.age = age;
}
Cat.prototype.say = function () {
    
    
  console.log('我的名字为' + this.name + ', 年龄是' + this.age)
};

let cat1 = myNew(Cat, 'xiao', 12);

6.实现call/apply

call()方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
apply()方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表
apply() 方法接受的是一个包含多个参数的数组

Function.prototype.myCall = function(context = window, ...args) {
    
    
  // 判断调用对象
  if (typeof this !== 'function') {
    
    
    console.error('type error');
  }
  let result = null;
  context.fn = this;
  // 调用函数
  if (args) {
    
    
    result = context.fn(args);
  } else {
    
    
    result = context.fn();
  }
  delete context.fn;
  return result;
};
let obj1 = {
    
    
  name: 'obj1',
  print: function() {
    
    
    console.log(this.name);
  }
};
let obj2 = {
    
    name: 'obj2'};
obj1.print.myCall(obj2);
Function.prototype.myApply = function(context = window, args) {
    
    
  // 判断调用对象
  if (typeof this !== 'function') {
    
    
    console.error('type error');
  }
  let result = null;
  // 将调用函数设为对象的方法
  context.fn = this;
  if (args) {
    
    
    result = context.fn(args);
  } else {
    
    
    result = context.fn();
  }
  delete context.fn;
  return result;
}

7. 实现instanceof

instanceof用于判断 “对象” 是否是指定构造函数的 “实例”,即判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上

function myInstanceof(L, R) {
    
    
  const baseTypes = ['string', 'number', 'boolean', 'null', 'undefined', 'symbol'];
  if (baseTypes.includes(typeof (L))) {
    
    
    return false;
  }
  let RP = R.prototype;
  L = L.__proto__;
  while (true) {
    
    
    if (L === null) {
    
    
      return false;
    }
    if (L === RP) {
    
    
      return true;
    }
    L = L.__proto__;
  } 
}
myInstanceof([1, 2, 3], Array); // true
myInstanceof({
    
    name: 'obj1'}, Array); // false

8. 实现 Promise.all

将多个promise实例包装成一个新的promise实例,
返回的值的顺序是以参数为顺序,与异步请求时间无关

Promise.myAll = function(promises) {
    
    
  return new Promise((resolve, reject) => {
    
    
    const len = promises.length;
    let results = new Array(len),
          count = 0;
    for (let i = 0; i < len; i++) {
    
    
      Promise.resolve(promises[i])
        .then(value => {
    
    
          count++;
          results[i] = value;
          if (count === len) {
    
    
            resolve(results);
          } 
        })
        .catch(err => {
    
    
          reject(err);
        })
    }
  }) 
};

let p1 = new Promise((resolve) => {
    
    
  setTimeout(resolve, 1000, 'p1');
});
let p2 = new Promise((resolve) => {
    
    
  setTimeout(resolve, 2000, 'p2');
});
let p3 = new Promise((resolve) => {
    
    
  setTimeout(resolve, 3000, 'p3');
});
let pAll = Promise.myAll([p1, p2, p3]);
pAll.then(
  success => {
    
    
    console.log(success); // [ 'p1', 'p2', 'p3' ]
  },
  error => {
    
    
    console.log(error); // 一旦一个失败,不再往下执行,不管时间是不是最长,也不会再输出之前正确的回调
  }
)

9. 实现 Promise.race

通俗的讲,谁先完成就是输出谁,
返回的值是以时间短的回调的值, error也一样

Promise.myRace = function(promises) {
    
    
  return new promises((resolve, reject) => {
    
    
    for (const p of promises) {
    
    
      Promise.resolve(p)
        .then(res => {
    
    
          resolve(res);
        })
        .catch(err => {
    
    
          reject(err);
        })
    }
  })
};
let p1 = new Promise((resolve, reject) => {
    
    
  setTimeout(reject, 1000, 'p1');
});
let p2 = new Promise((resolve) => {
    
    
  setTimeout(resolve, 2000, 'p2');
});
let p3 = new Promise((resolve) => {
    
    
  setTimeout(resolve, 3000, 'p3');
});
let pRace = Promise.myRace([p1, p2, p3]);
pRace.then(
  success => {
    
    
    console.log(`pRace ${
      
      success}`); // 'p2'
  },
  error => {
    
    
    console.log(`pRace ${
      
      error}`);
  }
)

10. 手写用Promise封装的完整 Ajax

function ajax(mode, url, data, flag) {
    
    
  return new Promise((resolve, reject) => {
    
    
    let xhr = null;
    if (window.XMLHttpRequest) {
    
    
      xhr = new XMLHttpRequest();
    } else {
    
    
      xhr = new ActiveXObject();
    }
    mode = mode.toUpperCase();
    if (mode === 'GET') {
    
    
      xhr.open(mode, url + '?' + data, flag); // flag 设置为true,代表异步 
      xhr.send(null);
    } else if (mode === 'POST') {
    
    
      xhr.open(mode, url, flag);
      xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
      xhr.send(data);
    }
    xhr.onreadystatechange = function() {
    
    
      if (xhr.readyState === 4) {
    
    
        if (xhr.status === 200) {
    
    
          resolve(xhr.responseText);
        } else if (xhr.status === 404) {
    
    
          reject(new Error('404 Not Found'));
        }
      } 
    };
  })
}

补充:

  1. xhr.readyState
  • 0 - (未初始化) 还没有调用 send() 方法
  • 1 - (载入) 已调用 send() 方法,正在发送请求
  • 2 - (载入完成) send() 方法执行完成,已经接收到全部响应内容
  • 3 - (交互) 正在解析响应内容
  • 4 - (完成) 响应内容解析完成,可以在客户端调用
  1. xhr.status(记录几个常用状态码)
  • 2 开头的状态码

200 服务器已成功处理了请求
201 请求成功并且服务器创建了新的资源
202 服务器已接受请求,但尚未处理
203 服务器已成功处理了请求,但返回的信息可能来自另一资源
204 服务器成功处理了请求,但没有返回任何内容
206 服务器已经成功处理了部分 GET 请求

  • 3 开头的状态码

301 永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL
302 临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL
303 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码
304 客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码
307 请求的资源临时从不同的URL 响应请求

  • 4 开头的状态码

400 服务器无法理解当前请求或请求参数有误
401 当前请求需要用户验证
403 服务器拒绝本次访问(访问权限出现问题)
404 请求失败,请求所希望得到的资源未被在服务器上发现。

  • 5 开头的状态码

500 服务器遇到错误,无法完成请求
503 临时的服务器维护或者过载,服务器当前无法处理请求。通常,这只是暂时状态

更多状态码请前往百度百科进行查询


记录总结一下自己学到的东西。可能写得不是很好,有错误的东西请大家指正,互相学习!!

猜你喜欢

转载自blog.csdn.net/qq_40513916/article/details/108691678