@[toc]

关于 自定义JS工具类 相关类似的源码请看 https://gitee.com/ykang2020/my_utils (opens new window)

# 1. 自定义new

创建Fn构造函数的实例对象

# 定义

/**
 * 自定义new
 * 创建Fn构造函数的实例对象
 * @param {Function} Fn
 * @param  {...any} args
 * @returns
 */
export default function newInstance(Fn, ...args) {
  // 1. 创建新对象
  // 创建空的object实例对象,作为Fn的实例对象
  const obj = {};
  // 修改新对象的原型对象
  // 将Fn的prototype(显式原型)属性赋值给obj的__proto__(隐式原型)属性
  obj.__proto__ = Fn.prototype;
  // 2. 修改函数内部this指向新对象,并执行
  //
  const result = Fn.call(obj, ...args);
  // 3. 返回新对象
  // return obj
  // 与new保持一直,如果构造函数有返回值,返回值是对象a就返回对象a,否则返回实例对象
  return result instanceof Object ? result : obj;
}

# 使用

import newInstance from "./object/newInstance";

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const p = new Person("YK", 18);
console.log(p);
console.log(p.constructor);
console.log('************');
const p2 = newInstance(Person, "YK菌", 19);
console.log(p2);
console.log(p2.constructor);

console.log('************');

function Person2(name, age) {
  this.name = name;
  this.age = age;
  return { a: 100 };
}

console.log(new Person2());
console.log(newInstance(Person2));

# 结果

在这里插入图片描述

# 2. 自定义instanceof

判断obj是否是Fn类型的实例对象

# 定义

实现方法:Fn的原型对象是否是obj的原型链上的某个对象

/**
 * 自定义instanceof
 * 判断obj是否是Fn类型的实例对象
 * 实现方法:Fn的原型对象是否是obj的原型链上的某个对象
 * @param {Object} obj
 * @param {*} Fn 构造函数
 * @returns
 */
export default function myInstanceOf(obj, Fn) {
  // 得到obj的隐式原型对象
  let protoObj = obj.__proto__;
  // 原型对象存在,就遍历原型链
  while (protoObj) {
    // 实例对象的隐式原型 等于 构造函数的显式原型 就返回true
    if (protoObj === Fn.prototype) {
      return true;
    }
    // 不相等就根据原型链一直往上找 直到最后为null
    protoObj = protoObj.__proto__;
  }
  return false;
}

# 使用

import myInstanceOf from "./object/myInstanceOf";

function Person(name, age) {
  this.name = name;
  this.age = age;
}
const p = new Person("YK", 18);
console.log(myInstanceOf(p, Object), p instanceof Object);
console.log(myInstanceOf(p, Person), p instanceof Person);
console.log(myInstanceOf(p, Function), p instanceof Function);
console.log(myInstanceOf(Person, Function), Person instanceof Function);

# 结果

在这里插入图片描述

# 3. 自定义合并对象mergeObject

合并多个对象,返回一个合并后的对象,不改变原对象

# 定义

/**
 * 合并多个对象,返回一个合并后的对象,不改变原对象
 * @param  {...any} objs 
 * @returns 
 */
export default function mergeObject(...objs) {
  const result = {};

  // 遍历objs得到一个个obj
  objs.forEach((obj) => {
    // 遍历obj的键得到一个个key
    Object.keys(obj).forEach((key) => {
      // 判断result对象中有没有key值属性
      if (!result.hasOwnProperty(key)) {
        // 如果没有,就将obj中的key值属性添加到result中
        result[key] = obj[key];
      } else {
        // 如果result有了,就合并属性
        result[key] = [].concat(result[key], obj[key]);
      }
    });
  });

  return result;
}

# 使用

import mergeObject from "./object/mergeObject";

const obj1 = {
  a: [{ x: 2 }, { y: 4 }],
  b: 1,
  c: "abc",
};

const obj2 = {
  a: [{ x: 3 }, { z: 6 }],
  b: [2, 3, 4],
  c: "ccc",
  d: "d",
};

const obj = mergeObject(obj1, obj2);
console.log(obj);

# 结果

在这里插入图片描述

# 4. 对象与数组的深拷贝与浅拷贝

# 4.1 区别深拷贝与浅拷贝

  • 浅拷贝 只复制对象属性或元素本身,只是引用地址值,修改一份会影响另一份 拷贝一层,更深层次级别只拷贝引用

  • 深拷贝 不仅复制对象属性或元素本身,还复制了指向的对象,修改一份不会影响另一份 拷贝多层,每一级都会拷贝(利用递归)

# 4.2 浅拷贝

ES6中Object.assign(target, ...sources)可以实现浅拷贝 方法描述:如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。

# 定义1

ES6的展开运算符

/**
 * 实现浅拷贝
 * @param {*} target
 * @returns
 */
export function clone1(target) {
  // 判断类型 {} [] null
  if (typeof target === "object" && target !== null) {
    // 判断类型 []
    if (Array.isArray(target)) {
      return [...target];
    } else {
      return { ...target };
    }
  } else {
    return target;
  }
}

# 定义2

ES5中的for...in循环

/**
 * 实现浅拷贝ES5
 * @param {*} target
 * @returns
 */
export function clone2(target) {
  // 判断类型 {} [] null
  if (typeof target === "object" && target !== null) {
    // 创建一个容器
    const result = Array.isArray(target) ? [] : {};
    // 遍历target数据
    for (let key in target) {
      // 判断当前对象上是否包含该属性
      if (target.hasOwnProperty(key)) {
        // 将属性设置到result结果数据中
        result[key] = target[key];
      }
    }
    return result;
  } else {
    return target;
  }
}

# 使用

import { clone1, clone2 } from "./object/clone";

const obj = { x: "abc", y: { m: 1 } };

const result = clone1(obj);

result.y.m = 2;

console.log(obj);
console.log(result);

const obj2 = { x: "abc", y: { m: 1 } };

const result2 = clone2(obj2);

result2.y.m = 3;

console.log(obj2);
console.log(result2);

# 结果

在这里插入图片描述

# 4.3 深拷贝

# 定义1 使用json的API

/**
 * 深拷贝乞丐版 函数(方法)属性会丢失,循环引用会出错
 * @param {*} target 
 * @returns 
 */
export function deepClone1(target) {
  // 通过数据创建JSON格式的字符串
  let str = JSON.stringify(target);
  // 将 JSON 字符串创建为JS数据
  let data = JSON.parse(str);
  return data;
}

# 使用

import { deepClone1 } from "./object/deepClone";

const obj = {
  a: 1,
  b: ["e", "f", "g"],
  c: { h: 20 },
  d: function () {},
};

const result = deepClone1(obj);

result.c.h = 34;

console.log(obj);
console.log(result);

# 结果

在这里插入图片描述

# 定义2

/**
 * 使用递归
 * 解决问题1: 函数属性不会丢失
 * 循环引用会出错
 * @param {*} target
 */
export function deepClone2(target) {
  if (typeof target === "object" && target !== null) {
    // 创建一个容器
    const result = Array.isArray(target) ? [] : {};
    // 遍历target
    for (let key in target) {
      // 检测该属性是否为对象本身的属性(不能拷贝原型对象上的属性)
      if (target.hasOwnProperty(key)) {
        result[key] = deepClone2(target[key]);
      }
    }
    return result;
  } else {
    return target;
  }
}

# 使用

import { deepClone1, deepClone2 } from "./object/deepClone";

const obj = {
  a: 1,
  b: ["e", "f", "g"],
  c: { h: 20 },
  d: function () {},
};

const result = deepClone2(obj);

result.c.h = 34;

console.log(obj);
console.log(result);

# 结果

在这里插入图片描述

# 定义3

/**
 * 使用递归
 * 解决问题1: 函数属性不会丢失
 * 解决问题2: 循环引用不会出错
 * @param {*} target
 */
export function deepClone3(target, map = new Map()) {
  if (typeof target === "object" && target !== null) {
    // 克隆数据之前进行判断,查看数据之前是否被克隆过
    let cache = map.get(target);
    if (cache) {
      return cache;
    }
    // 创建一个容器
    const result = Array.isArray(target) ? [] : {};
    // 将新的结果保存到容器中
    map.set(target, result);
    // 遍历target
    for (let key in target) {
      // 检测该属性是否为对象本身的属性(不能拷贝原型对象上的属性)
      if (target.hasOwnProperty(key)) {
        result[key] = deepClone3(target[key], map);
      }
    }
    return result;
  } else {
    return target;
  }
}

# 使用

import { deepClone1, deepClone2,deepClone3 } from "./object/deepClone";

const obj = {
  a: 1,
  b: ["e", "f", "g"],
  c: { h: 20 },
  d: function () {},
};

obj.b.push(obj.c);
obj.c.j = obj.b;

const result = deepClone3(obj);

result.c.h = 34;

console.log(obj);
console.log(result);

# 结果

在这里插入图片描述

# 定义4 最终优化 优化遍历性能

/**
 * 最终版 优化遍历性能
 * 数组: while | for | forEach() 优于 for-in | keys()&forEach()
 * 对象: for-in 与 keys()&forEach() 差不多
 * @param {*} target
 */
export function deepClone4(target, map = new Map()) {
  if (typeof target === "object" && target !== null) {
    // 克隆数据之前进行判断,查看数据之前是否被克隆过
    let cache = map.get(target);
    if (cache) {
      return cache;
    }
    // 创建一个容器
    const result = Array.isArray(target) ? [] : {};
    // 将新的结果保存到容器中
    map.set(target, result);

    // 优化遍历
    // 如果目标数据是数组 使用 forEach 循环
    if (Array.isArray(target)) {
      target.forEach((item, index) => {
        result[index] = deepClone4(item, map);
      });
    } else {
      // 如果是对象,获取所有的键名, 再 forEach 遍历
      Object.keys(target).forEach((key) => {
        result[key] = deepClone4(target[key], map);
      });
    }

    // 遍历target
    for (let key in target) {
      // 检测该属性是否为对象本身的属性(不能拷贝原型对象上的属性)
      if (target.hasOwnProperty(key)) {
        result[key] = deepClone4(target[key], map);
      }
    }

    return result;
  } else {
    return target;
  }
}

# 5. 封装字符串相关函数

# 5.1 字符串反转

生成一个倒序的字符串

export function reverseString(str) {
  // 将字符串转换成数组
  let arr = str.split("");
  // 使用数组的翻转方法
  arr.reverse();
  // 将数组拼接成字符串
  let s = arr.join("");
  return s;
}

# 5.2 检测回文字符串

export function palindrome(str) {
  return reverseString(str) === str;
}

# 5.3 截取字符串

export function truncate(str, size) {
  return str.slice(0, size) + "...";
}

# 5.4 测试

import { palindrome, reverseString, truncate } from "./string/reverseString";

let str1 = "ykiloveu";
console.log(reverseString(str1));

let str2 = "ykky";
console.log(palindrome(str2));

let str3 = "这里是YK菌的博客欢迎访问啊大家";
console.log(truncate(str3, 9));

在这里插入图片描述

关于 自定义JS工具类 相关类似的源码请看 https://gitee.com/ykang2020/my_utils (opens new window)

上次更新: 2022/4/22 15:59:56