js中字符串,json数据的处理【匹配url、邮箱、电话,版本号,千位分割,判断回问】-CSDN博客
手写实现js类/方法【改变this、map、Object、发布订阅、promise】-CSDN博客
目录
A.JSON.parse() 和 JSON.stringify()
arr instanceof Array ? [] : {}
Symbol属性:Object.getOwnPropertySymbols
requestAnimationFrame代替settimeout更精准
setTimeout、setInterval最短时长为4ms
arr.filter((value, index, self) => self.indexOf(value) === index)
new :Fn=[...arguments].shift()
*寄生组合式继承:call,create,constructor
Object.defineProperty(obj, prop, descriptor)
被打断的时间+原定time执行一次
并且执行的是最后一次触发的事件
触发事件后在 n 秒内函数至多只能执行一次,如果在 n 秒内又触发了事件,会重计算函数执行时间,延迟执行
本该在-----执行
但是--再次触发
于是-------执行
必须等待完time,即才能执行,time期间触发都会从0计时
- function debounce(fun,time) {
- let flag // 定义状态
- return function () {
- clearTimeout(flag)// 在执行之前 清除 定时器的 flag 不让他执行
- flag = setTimeout(() => {
- fun.call(this,arguments)//拿到正确的this对象,即事件发生的dom
- }, time)
- }
- }
应用
search搜索联想
用户在不断输入值时,用防抖来节约请求资源。
window触发resize
不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
间隔至少time执行
无视打断,time内执行的是第一次触发的事件
连续触发事件但是在 n 秒中只执行一次函数。
定时在-----执行
在--再次触发,无效
最终在-----执行
两种方式可以实现,分别是时间戳版和定时器版。
- function throttle(fun, time) {
- let flag // 定义一个空状态
- return function () { // 内部函数访问外部函数形成闭包
- if (!flag) { // 状态为空执行
- flag = setTimeout(() => {
- fns.apply(this, arguments) // 改变this指向 把 event 事件对象传出去
- flag = null // 状态为空
- }, time)
- }
- }
- }
应用
鼠标不断点击触发
mousedown(单位时间内只触发一次)
监听滚动事件
比如是否滑到底部自动加载更多,用throttle来判断
- 数据类型限制:不能复制、undefined、Date、Symbol、函数等等其他的js数据类型,其实也很好理解,因为JSON只支持以上列举的数据类型,其他要么被忽略要么报错。
- 丢失原型的信息:最终拷贝的对象原型只会是
Object.prototype。- 不能处理循环引用:对象包含循环引用,JSON.stringify() 将会导致错误。
- 性能问题:对于大型复杂对象或深度嵌套的对象,容易产生性能问题
- const originalObject = { a: 1, b: { c: 2 } };
-
- // 通过将对象转换为字符串,再将其解析回对象来实现深拷贝
- const deepCopy = JSON.parse(JSON.stringify(originalObject));
-
- console.log(deepCopy); // 输出深拷贝后的对象
- function deepClone(obj) {
- if (typeof obj !== "object" || obj === null) {
- return obj;
- }
-
- const res = Array.isArray(obj) ? [] : {};
-
- for (const key in obj) {
- if (obj.hasOwnProperty(key)) {
- res[key] = deepClone(obj[key]);
- }
- }
-
- return res;
- }
要拷贝的属性只能是对象自身的,不能是原型链上的
拷贝的属性不仅仅是
string类型的键,还有symbol类型的键对象身上可能会有循环引用,需要处理,而不是陷入死循环
Symbols 在 for...in 迭代中不可枚举。
Object.getOwnPropertyNames() 不会返回 symbol 对象的属性
- // 复制 Symbol 类型属性
- const symbols = Object.getOwnPropertySymbols(obj);
- for (const symbolKey of symbols) {
- res[symbolKey] = deepClone(obj[symbolKey]);
- }
Reflect.ownKeys
Reflect.ownKeys简单来说会返回一个数组,属性包括:对象自身的所有属性(包括所有可枚举的和不可枚举的的string和symbol类型
- // 拿到对象身上所有的属性,返回一个数组
- const keys = Reflect.ownKeys(obj);
-
- for (const key of keys) {
- res[key] = deepClone(obj[key]);
- }
在每次对复杂数据类型进行深拷贝前保存其值,如果下次又出现了该值,就不再进行拷贝,直接截止。
WeakMap中的键是弱引用,当键对象在其他地方没有被引用时,它可以被垃圾回收,这有助于防止内存泄漏。
- function deepClone(obj, clones = new WeakMap()) {
- // 如果是原始类型或 null,直接返回
- if (typeof obj !== "object" || obj === null) {
- return obj;
- }
-
- // 检查是否已经克隆过该对象,防止循环引用
- if (clones.has(obj)) {
- return clones.get(obj);
- }
-
- // 判断是否数组还是普通对象
- const res = Array.isArray(obj) ? [] : {};
-
- // 将当前对象添加到克隆Map中
- clones.set(obj, res);
-
- // 拿到所有 key
- const keys = Reflect.ownKeys(obj);
-
- for (const key of keys) {
- res[key] = deepClone(obj[key], clones);
- }
-
- return res;
- }
-
- // test
- const obj = {
- foo: "bar",
- num: 42,
- arr: [1, 2, 3],
- obj: { dd: true },
- [Symbol("symbol属性")]: "hello",
- };
-
- // 循环引用
- obj.newObj = obj;
-
- const copy = deepClone(obj);
- console.log(copy);
- //返回值timeoutID是一个正整数,表示定时器的编号。
- let timeoutID = scope.setTimeout(function[, delay]),//delay表示最小等待时间,真正等待时间取决于前面排队的消息
- clearTimeout(timeoutID) //取消该定时器。
- var c = 10; // 设置初始倒计时时间为10秒
- var t; // 声明一个变量用来存储 setTimeout 的返回值
-
- function timedCount() {
- c -= 1; // 每次调用函数,倒计时时间减一秒
-
- if (c === 0) {
- clearTimeout(t); // 当倒计时为零时,清除定时器
- console.log("倒计时结束"); // 可以在这里添加任何你想要执行的操作,比如提示用户倒计时结束
- return;
- }
-
- console.log("剩余时间:" + c + "秒"); // 在控制台打印剩余时间,你也可以将其显示在页面上的某个元素中
-
- t = setTimeout(function () {
- timedCount();
- }, 1000); // 每隔一秒调用一次自身,实现倒计时
- }
-
- timedCount(); // 调用函数开始倒计时
- import React, { useState, useEffect } from 'react';
-
- const Countdown = ({ initialCount }) => {
- const [count, setCount] = useState(initialCount);
-
- useEffect(() => {
- // 如果倒计时已经结束,则不再减少计时
- if (count === 0) return;
-
- // 设置定时器
- const timerId = setTimeout(() => {
- setCount(count - 1);
- }, 1000);
-
- // 清理函数
- return () => clearTimeout(timerId);
- }, [count]);
-
- return (
- <div>
- <h1>倒计时: {count} 秒h1>
- div>
- );
- };
-
- export default Countdown;
- startTime = Date.now();
-
- requestAnimationFrame(function update() {
- var currentTime = Date.now();
- var deltaTime = currentTime - startTime;
-
- if (deltaTime >= 1000) { // 每秒更新一次
- startTime = currentTime;
- timedCount(); // 递归调用自身,实现倒计时
- } else {
- requestAnimationFrame(update); // 继续请求下一帧
- }
- });
- // 使用闭包实现
- function mySetInterval(fn, t) {
- let timer = null;
- function interval() {
- fn();
- timer = setTimeout(interval, t);
- }
- interval();
- return {
- // cancel用来清除定时器
- cancel() {
- clearTimeout(timer);
- }
- };
- }
- function mySetTimeout(fn, time) {
- let timer = setInterval(() => {
- clearInterval(timer);
- fn();
- }, time);
- }
-
- // 使用
- mySetTimeout(() => {
- console.log(1);
- }, 2000);
Java、Python、C++、JS都是基于 IEEE 754 标准的双精度浮点数来表示数字
0.2的二进制表示是无限循环的,近似等于0.0011001100110011001100

IEEE-754 标准下双精度浮点数由三部分组成,分别如下:
阅读 JavaScript 浮点数陷阱及解法可以了解到公式的由来。

- /*** method **
- * add / subtract / multiply /divide
- * floatObj.add(0.1, 0.2) >> 0.3
- * floatObj.multiply(19.9, 100) >> 1990
- *
- */
- var floatObj = function() {
-
- /*
- * 判断obj是否为一个整数
- */
- function isInteger(obj) {
- return Math.floor(obj) === obj
- }
-
- /*
- * 将一个浮点数转成整数,返回整数和倍数。如 3.14 >> 314,倍数是 100
- * @param floatNum {number} 小数
- * @return {object}
- * {times:100, num: 314}
- */
- function toInteger(floatNum) {
- var ret = {times: 1, num: 0}
- if (isInteger(floatNum)) {
- ret.num = floatNum
- return ret
- }
- var strfi = floatNum + ''
- var dotPos = strfi.indexOf('.')
- var len = strfi.substr(dotPos+1).length
- var times = Math.pow(10, len)
- var intNum = Number(floatNum.toString().replace('.',''))
- ret.times = times
- ret.num = intNum
- return ret
- }
-
- /*
- * 核心方法,实现加减乘除运算,确保不丢失精度
- * 思路:把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
- *
- * @param a {number} 运算数1
- * @param b {number} 运算数2
- * @param digits {number} 精度,保留的小数点数,比如 2, 即保留为两位小数
- * @param op {string} 运算类型,有加减乘除(add/subtract/multiply/divide)
- *
- */
- function operation(a, b, digits, op) {
- var o1 = toInteger(a)
- var o2 = toInteger(b)
- var n1 = o1.num
- var n2 = o2.num
- var t1 = o1.times
- var t2 = o2.times
- var max = t1 > t2 ? t1 : t2
- var result = null
- switch (op) {
- case 'add':
- if (t1 === t2) { // 两个小数位数相同
- result = n1 + n2
- } else if (t1 > t2) { // o1 小数位 大于 o2
- result = n1 + n2 * (t1 / t2)
- } else { // o1 小数位 小于 o2
- result = n1 * (t2 / t1) + n2
- }
- return result / max
- case 'subtract':
- if (t1 === t2) {
- result = n1 - n2
- } else if (t1 > t2) {
- result = n1 - n2 * (t1 / t2)
- } else {
- result = n1 * (t2 / t1) - n2
- }
- return result / max
- case 'multiply':
- result = (n1 * n2) / (t1 * t2)
- return result
- case 'divide':
- result = (n1 / n2) * (t2 / t1)
- return result
- }
- }
-
- // 加减乘除的四个接口
- function add(a, b, digits) {
- return operation(a, b, digits, 'add')
- }
- function subtract(a, b, digits) {
- return operation(a, b, digits, 'subtract')
- }
- function multiply(a, b, digits) {
- return operation(a, b, digits, 'multiply')
- }
- function divide(a, b, digits) {
- return operation(a, b, digits, 'divide')
- }
-
- // exports
- return {
- add: add,
- subtract: subtract,
- multiply: multiply,
- divide: divide
- }
- }();
bignumber.js:处理大数字和高精度运算"_new"函数,该函数会返回一个对象,
该对象的构造函数为函数参数、原型对象为函数参数的原型,核心步骤有:
- const _new = function() {
- const object1 = {}
- const Fn = [...arguments].shift()
- object1.__proto__ = Fn.prototype
- const object2 = Fn.apply(object1, arguments)
- return object2 instanceof Object ? object2 : object1
- }
通过寄生组合式继承使"Chinese"构造函数继承于"Human"构造函数。要求如下:
1. 给"Human"构造函数的原型上添加"getName"函数,该函数返回调用该函数对象的"name"属性
2. 给"Chinese"构造函数的原型上添加"getAge"函数,该函数返回调用该函数对象的"age"属性
- function Human(name) {
- this.name = name
- this.kingdom = 'animal'
- this.color = ['yellow', 'white', 'brown', 'black']
- }
- Human.prototype.getName = function() {
- return this.name
- }
-
- function Chinese(name,age) {
- Human.call(this,name)//call函数借助”Human“的构造器来获得通用属性
- this.age = age
- this.color = 'yellow'
- }
-
- //返回的对象__proto__属性为对象参数的原型
- Chinese.prototype = Object.create(Human.prototype)//使用现有的对象来作为新创建对象的原型
- //修复"Chinese"构造函数的原型链,即自身的"constructor"属性需要指向自身
- Chinese.prototype.constructor = Chinese
-
- Chinese.prototype.getAge = function() {
- return this.age
- }