@[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)