Js手写(专注js各种手写)

134 阅读10分钟

1.call

/**
 * 概念:call是Function.prototype上的一个属性方法,目的是改变function的this指向
 * 使用:function.call(obj,arg1,arg2,arg3) 把function的this设置为obj,function的参数设置为arg1、arg2、arg3
 */

var person = {
  fullName: function(city, country) {
    console.log(this,'this');
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
const result1 = person.fullName.call(true, "Seattle", "USA");
console.log(result1);

/**
 * 特殊情况
 * 1.call(null或undefined) this在浏览器中为window、在node中为global
 * 2.call(1) [Number: 1] this
 * 3.call('1') [String: '1'] this
 * 4.call(true) [Boolean: true] this
 */

Function.prototype.myCall = function(obj,...params){
  if(obj===null||obj===undefined) obj = globalThis;
  if(typeof obj === 'number') obj = new Number(obj);
  if(typeof obj === 'string') obj = new String(obj);
  if(typeof obj === 'boolean') obj = new Boolean(obj);
  const NAME = '0123456789_call';
  obj[NAME] = this;
  return obj[NAME](...params);
}

const result2 = person.fullName.myCall(true, "Seattle", "USA");
console.log(result2);

2.apply

/**
 * 概念:apply是Function.prototype上的一个属性方法,目的是改变function的this指向
 * 使用:function.apply(obj,[arg1,arg2,arg3]) 把function的this设置为obj,function的参数设置为arg1、arg2、arg3
 */

var person = {
  fullName: function(city, country) {
    console.log(this,'this');
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
const result1 = person.fullName.apply(true, ["Seattle", "USA"]);
console.log(result1);

/**
 * 特殊情况
 * 1.apply(null或undefined) this在浏览器中为window、在node中为global
 * 2.apply(1) [Number: 1] this
 * 3.apply('1') [String: '1'] this
 * 4.apply(true) [Boolean: true] this
 */

Function.prototype.myApply = function(obj,params){
  if(obj===null||obj===undefined) obj = globalThis;
  if(typeof obj === 'number') obj = new Number(obj);
  if(typeof obj === 'string') obj = new String(obj);
  if(typeof obj === 'boolean') obj = new Boolean(obj);
  const NAME = '0123456789_apply';
  obj[NAME] = this;
  return obj[NAME](...params);
}

const result2 = person.fullName.myApply(true, ["Seattle", "USA"]);
console.log(result2);

3.bind

/**
 * 概念:bind是Function.prototype上的一个属性方法,目的是改变function的this指向
 * 使用:function.bind(obj,arg1,arg2,arg3) 把function的this设置为obj,返回新函数fun,
 *      执行fun时会把传递给fun的参数,如fun(arg4,arg5),会把arg1、arg2、arg3、arg4、arg5拼接起来传入function中并执行
 */
var person = {
  fullName: function(city, country) {
    console.log(this,'this');
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}

//执行方式一
const fun1 = person.fullName.bind(true,"Seattle","USA");
const result1 = fun1();
console.log(result1);
//执行方式二
const fun2 = person.fullName.bind(true,"Seattle");
const result2 = fun2("USA");
console.log(result2);


Function.prototype.myBind = function(obj,...params1){
  const that = this;
  if(obj===null||obj===undefined) obj = globalThis;
  if(typeof obj === 'number') obj = new Number(obj);
  if(typeof obj === 'string') obj = new String(obj);
  if(typeof obj === 'boolean') obj = new Boolean(obj);
  return function(...params2){
    const params = [...params1,...params2];
    const NAME = '0123456789_bind';
    obj[NAME] = that;
    return obj[NAME](...params);
  }
}
//执行方式一
const fun3 = person.fullName.myBind(true,"Seattle","USA");
const result3 = fun3();
console.log(result3);
//执行方式二
const fun4 = person.fullName.myBind(true,"Seattle");
const result4 = fun4("USA");
console.log(result4);

4.reduce


//Array.prototype.reduce 适用于汇总
//Array.prototype.reduce(function(total,currentValue,currentIndex,arr),initialValue) currentIndex为currentValue在arr中的下标
//arr.reduce(function(total,currentValue,currentIndex,arr),initialValue) 当arr为空数组,有initialValue返回initialValue,无initialValue抛错

//情景一:数字的汇总
// const arr = [10,20,30,40,50];

// const result = arr.reduce(function(total,current,index,z){
//   console.log(index,'index');
//   console.log(z,'z');
//   total+=current;
//   return total;
// },0)

// console.log(result);
//情景二:对象的汇总
// const arr = [{name:'kobe'},{age:24},{gender:'男'}];

// const result = arr.reduce(function(obj,current,index,z){
//   obj = {...obj,...current};
//   return obj;
// })

// console.log(result);


Array.prototype.myReduce = function (fun, init) {
  const arr = this;
  const len = arr.length;
  if (len === 0) {
    if (init !== undefined) return init;
    throw new TypeError("Reduce of empty array with no initial value");
  }
  let index = null;
  if (init) {
    index = 0;
  } else {
    init = arr[0];
    index = 1;
  }
  while (index < len) {
    init = fun(init, arr[index], index, arr);
    index++;
  }
  return init;
}

//情景一:数字的汇总
// const arr = [10,20,30,40,50];
const arr = [];

const result = arr.myReduce(function (total, current, index, z) {
  total += current;
  return total;
},0)

console.log(result, 'myReduce');
//情景二:对象的汇总
// const arr = [{name:'kobe'},{age:24},{gender:'男'}];

// const result = arr.myReduce(function(obj,current,index,z){
//   obj = {...obj,...current};
//   return obj;
// })

// console.log(result,'myReduce');

5.实现sleep函数

//sleep函数的作用是让程序暂停一段时间

//方式一
function sleep(time){
  const temp = Date.now();
  while((Date.now()-temp) < time){
    // console.log(Date.now());
  }
}

function fn1(){
  console.log(123);
  sleep(3000);
  console.log(456);
}
fn1()

//方式二
function sleep(time){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>resolve(),time)
  })
}
async function fn2(){
  console.log(123);
  await sleep(3000);
  console.log(456);
}
fn2()

6.函数柯里化

//函数柯里化是指传递参数具体化
function sun(a,b,c,d){
  return a+b-c*d;
}
console.log(sun(1,2,3,4));

function kelihua(fn){
  const len = fn.length;
  let params = [];
  function temp(...param){
    params = params.concat(param);
    if(params.length === len){
      return fn(...params);
    }
    return temp;
  }
  return temp;
}
console.log(kelihua(sun)(1)(2)(3)(4));
console.log(kelihua(sun)(1)(2,3)(4));

7.获取URL参数

   
//字符分割
// function getParams(url){
//   const str = location.search.substring(1);
//   const arr = str.split("&");
//   const obj = {};

//   for(let i = 0;i < arr.length;i++){
//     let [key,value] = arr[i].split("=");
//     key = decodeURIComponent(key);
//     value = decodeURIComponent(value);
//     obj[key] = value;
//   }

//   return obj;
// }

//URLSearchParams
function getParams(){
  const search = new URLSearchParams(location.search);
  const iterator = search.entries();
  const obj = {};
  for(let [key,value] of iterator){
    obj[key] = value;
  }
  console.log(obj);
  return obj;
}
getParams() 

8.new的实现

function MyObj(a,b){
  this.a = a;
  this.b = b;
}

const myobj = new MyObj(10,20);
console.log(myobj);

function codingNewMyObj(a,b){
  const obj = {};
  obj.a = a;
  obj.b = b;
  return obj;
}
console.log(codingNewMyObj(30,40));

9.Object.create实现

function myObjectCreate(obj){
  if(typeof obj !== "object" || typeof obj !== "function"){
    throw new Error("obj 需为对象或Function构造函数")
  }
  const t = {};
  t.__proto__ = obj;
  return t;
}

const temp = {a:123};
const temp1 = myObjectCreate(temp);
console.log(temp,temp.__proto__);
console.log(temp1);

10.防抖

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

11.节流

//适用于scroll

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

//时间戳
function throttle(fn,delay){
  let start = Date.now();
  return function(...args){
    const end = Date.now();
    if((end-start)>delay){
      fn.apply(this,args);
      start = end;
    }
  }
}

12.深拷贝


function copy(obj){
  if(typeof obj !== "object" || obj === null){
    throw new Error("obj需为对象");
  }

  function fn(data){
    const temp = {};
    const arr = Object.entries(data);
    let t = null;
    while(t = arr.shift()){
      console.log(t,'t');
      if(typeof t[1] === "object" && t[1] !== null){
        temp[t[0]] = fn(t[1]);
      }else{
        temp[t[0]] = t[1];
      }
    }
    return temp;
  }

  return fn(obj);
}

const obj1 = {a:1,b:{c:2},d:3};
const obj2 = copy(obj1);
console.log(obj1,obj2);
console.log(obj1 === obj2);
console.log(obj1.b === obj2.b);

13.手写ajax

function myAjax(method,url,data){
  return new Promise((resolve,reject)=>{
    const xhr = new XMLHttpRequest();
    xhr.open(method,url,true);
    xhr.send(data);
    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4 && xhr.status === 200){
        resolve(xhr.responseText)
      }
    }
    xhr.send()
  })
}

14.手写Promise

15.instanceof


function myInstanceof(obj,OBJ){
  while(obj.__proto__){
    if(obj.__proto__ === OBJ.prototype){
      return true;
    }
    obj = obj.__proto__;
  }
  return false;
}

class A{};
const a = new A();
console.log(myInstanceof(a,A)); 
console.log(myInstanceof(a,Object)); 
console.log(myInstanceof(a,String)); 

16.深度比较isEqual

function isEqual(obj1,obj2){
  if(typeof obj1 !== "object" || typeof obj2 !== "object"){
    return obj1 === obj2;
  }
  if(obj1 === obj2){
    return true;
  }
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if(keys1.length !== keys2.length){
    return false;
  }
  for(let i = 0;i < keys1.length;i++){
    if(keys1[i] !== keys2[i]){
      return false;
    }
    if(!isEqual(obj1[keys1[i]],obj2[keys2[i]])){
      return false;
    }
  }
  return true;
}

// const obj1 = {
//   a:1,
//   b:{
//     c:2,
//     d:3
//   }
// }

// const obj2 = {
//   a:1,
//   b:{
//     c:2,
//     d:3
//   }
// }
// console.log(isEqual(obj1,obj2)); 

const obj1 = {
  a:1,
  b:{
    c:2,
    d:3
  }
}

const obj2 = {
  a:1,
  b:{
    c:2,
    e:3
  }
}
console.log(isEqual(obj1,obj2)); 

17.jsonp

function myJsonp(url,callbackName){
  return new Promise((resolve,reject)=>{
    const script = document.createElement("script");
    url = `${url}?callbackName=${callbackName}`;
    script.src = url;
    document.body.appendChild(script);
  
    window[callbackName] = function(data){
  
      document.body.removeChild(script);
      resolve(data);
    }
  })
}

18.使用setTimeout实现setInterval

function mySetInterval(time){
  function fn(){
    setTimeout(()=>{
      console.log(123);
      fn();
    },time);
  }
  fn();
}

mySetInterval(3000);

19.实现数组扁平化


function myFlat(arr,num){
  let data = [];
  num = num ? num : 1;

  do{
    data = [];
    for(let i = 0;i < arr.length;i++){
      const temp = Array.isArray(arr[i])?arr[i]:[arr[i]];
      console.log(temp,'temp');
      data.push(...temp);
    }
    arr = data;
  }while(--num);

  return data;
}

// const arr = [1,[2,[3,4]]];
// const arr1 = arr.flat();
// console.log(arr,arr1,arr === arr1);

const arr = [1,[2,[3,4]]];
const arr1 = myFlat(arr,2);
console.log(arr,arr1,arr === arr1);

20.实现数组的push、filter、map


Array.prototype.myPush = function(...params){
  for(let i = 0;i < params.length;i++){
    this[this.length - 1] = params[i];
  }
  return this.length;
}

Array.prototype.myFilter = function(fn){
  const arr = [];
  for(let i = 0;i < this.length;i++){
    if(fn(this[i])){
      arr.push(this[i])
    }
  }
  return arr;
}

Array.prototype.myMap = function(fn){
  const arr = [];
  for(let i = 0;i < this.length;i++){
    arr.push(fn(this[i]));
  }
  return arr;
}

21.红黄绿循环打印


function fn(){
  let num = -1;

  function t(){
    setTimeout(()=>{
      if(num === 2){
        num = -1;
      }
      num += 1;
      switch(num){
        case 0:
          console.log("红");
          t();
          break;
        case 1:
          console.log("黄");
          t();
          break;
        case 2:
          console.log("绿");
          t();
          break;
        default:
          break;
      }
    },1000)
  }
  t();
}

fn();

22.用Promise实现异步加载图片


function lazyLodingImg(url){
  return new Promise((resolve,reject)=>{
    const img = new Image();
    img.src = url;
    img.onload = function(){
      console.log("onload");
      resolve(img);
    }
    img.onerror = function(){
      console.log("onerror");
      resolve(new Error("加载图片失败"))
    }
  })

}

console.log(123);
lazyLodingImg("https://file.ccmapp.cn/group1/M00/16/64/rApntl7CSdeAbpYqABArOjGaasg001.jpg");
console.log(456);

23.图片懒加载SDK

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .box {
      display: flex;
      flex-wrap: wrap;
      width: 600px;
      margin: 0 auto;
      font-size: 0;
    }

    .box>.img {
      width: 200px;
      vertical-align: top;
      flex: 1;
      display: flex;
      flex-direction: column;
    }

    .img>img {
      width: 200px;
    }
  </style>
</head>

<body>
  <div class="box">
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/5e60c42e062e654f9113496900675294_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/75631b031ff234640edb416feecc9cc1_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/32e5ac518a46755c2fab839c11ba353f_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/552af0047051f642f511f83f89b1b8a9_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/957b39be46c507c7949a59e09d517107 (1)_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/17299f65e64f65dcd51c0756a235f1e2_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/957b39be46c507c7949a59e09d517107_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/7694cc962421085180149618fa8638ff_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/8190548e74ca74cd79921897ebe78252_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/32e5ac518a46755c2fab839c11ba353f_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/552af0047051f642f511f83f89b1b8a9_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/957b39be46c507c7949a59e09d517107 (1)_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/17299f65e64f65dcd51c0756a235f1e2_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/957b39be46c507c7949a59e09d517107_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/7694cc962421085180149618fa8638ff_副本.jpeg"
        alt="" srcset="">
    </div>
    <div class="img">
      <img src=""
        data-src="/Users/hwl/Desktop/project/2023/JavaScript/js手写/23.图片懒加载/8190548e74ca74cd79921897ebe78252_副本.jpeg"
        alt="" srcset="">
    </div>
  </div>
  <script>
    window.onload = function () {
      const imgs = document.querySelectorAll("img[data-src]");
      const box = document.querySelector(".box");

      for (let i = 0; i < imgs.length; i++) {
        if (i > 9) {
          return;
        }
        // console.log(imgs[i],"imgs[i]");
        // imgs[i].src = imgs[i].dataSet.src;
        imgs[i].src = imgs[i].getAttribute("data-src");
        imgs[i].removeAttribute("data-src");
      }
    }

    function lazy(fn, time) {
      let key = true;
      return function (...args) {
        if (key) {
          key = false;
          setTimeout(() => {
            key = true
            fn.apply(this, args);
          }, time);
        }
      }
    }
    function imgLoad() {
      const imgs = document.querySelectorAll("img[data-src]");
      for (let i = 0; i < imgs.length; i++) {
        const top = imgs[i].getBoundingClientRect().top;
        if (top < window.innerHeight) {
          imgs[i].src = imgs[i].getAttribute("data-src");
          imgs[i].removeAttribute("data-src");
        } else {
          return;
        }
      }
    }
    
    window.onscroll = lazy(imgLoad,100);
  </script>
</body>

</html>

24.单例莫斯、观察者模式、发布订阅模式 ???


//单例模式
function gongchang(){
  let temp = null;
  return function(){
    if(!temp){
      temp = {};
    }
    return temp;
  }
}

const fn = gongchang();
const obj1 = fn();
const obj2 = fn();
const obj3 = fn();
console.log(obj1 === obj2,obj1 === obj3);

//观察者模式
class Observer{
  constructor(name){
    this.name = name;
  }
  getState(state){
    console.log(`${this.name}知道宝宝状态变为${state}`);
  }
}

class Watcher{
  constructor(){
    this.state = "睡觉";
    this.os = [];
  }
  addObserver(o){
    this.os.push(o);
  }
  setState(val){
    this.state = val;
    for(let i = 0;i < this.os.length;i++){
      this.os[i].getState(this.state);
    }
  }
}

const father = new Observer("爸爸");
const mather = new Observer("妈妈");
const w = new Watcher();
w.addObserver(father);
w.addObserver(mather);
w.setState("吃饭");

//发布订阅模式
class EventEmitter{
  constructor(){
    this.emitter = {}
  }
  on(name,callback){
    if(!this.emitter[name]){
      this.emitter[name] = [];
    }
    this.emitter[name].push(callback);
  }
  emit(name,...args){
    for(let i = 0;i < this.emitter[name].length;i++){
      this.emitter[name][i](...args);
    }
  }
}

const emitter = new EventEmitter();
emitter.on("one",function(a,b,c){
  console.log(a+b+c);
})
emitter.on("one",function(a,b){
  console.log(a-b);
})
emitter.on("two",function(a,b){
  console.log(a*b);
})

emitter.emit("one",1,2,3);
emitter.emit("two",4,5);