防抖的原理就是:就是要等你触发完事件n秒内不再触发事件,才执行。
// 第一版
function debounce(func, wait) {
var timeout;
return function () {
clearTimeout(timeout)
timeout = setTimeout(func, wait);
}
}
节流的原理:如果你持续触发事件,每隔一段时间,只执行一次事件。
// 第二版
function throttle(func, wait) {
var timeout;
var previous = 0;
return function() {
context = this;
args = arguments;
if (!timeout) {
timeout = setTimeout(function(){
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
深浅拷贝
数据类型存储
js中存在两大数据类型
- 基本类型
- 引用类型
基本类型数据保存在在栈内存中
引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中
浅拷贝
function shallowClone(obj) {
const newObj = {};
for(let prop in obj) {
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
- Object.assign
- slice()
- concat()
-
深拷贝
JSON.stringify()
const obj2=JSON.parse(JSON.stringify(obj1));
会忽略 undefined symbol 函数
递归
var deepCopy = function(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
``` const isComplexDataType = obj => (typeof obj === ‘object’ || typeof obj === ‘function’) && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date)
return new Date(obj) // 日期对象直接返回一个新的日期对象
if (obj.constructor === RegExp)
return new RegExp(obj) //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj)
// 可以获得对象的所有属性 let allDesc = Object.getOwnPropertyDescriptors(obj)
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
//继承原型链
hash.set(obj, cloneObj) // 获取 不可枚举属性以及 Symbol 类型 for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
// 下面是验证代码
let obj = {
num: 0,
str: ‘’,
boolean: true,
unf: undefined,
nul: null,
obj: { name: ‘我是一个对象’, id: 1 },
arr: [0, 1, 2],
func: function () { console.log(‘我是一个函数’) },
date: new Date(0),
reg: new RegExp(‘/我是一个正则/ig’),
};
Object.defineProperty(obj, ‘innumerable’, {
enumerable: false, value: ‘不可枚举属性’ }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 设置loop成循环引用的属性
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
console.log(‘obj’, obj)
console.log(‘cloneObj’, cloneObj)
![image.png](https://cdn.nlark.com/yuque/0/2022/png/756869/1653725163301-7123bbe6-d46f-4860-aac4-713cb2c40ec9.png#clientId=ub3dd323a-afc2-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc9672a53&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1110&originWidth=1080&originalType=binary&ratio=1&rotation=0&showTitle=false&size=189322&status=done&style=none&taskId=u6cec3121-e96a-4193-bafd-bb2979105f9&title=)<br />改进后的优点
1. 针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys 方法;
2. 当参数为 Date、RegExp 类型,则直接生成一个新的实例返回;
3. 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性,以及对应的特性,顺便结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链;
4. 利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏(你可以关注一下 Map 和 weakMap 的关键区别,这里要用 weakMap),作为检测循环引用很有帮助,如果存在循环,则引用直接返回 WeakMap 存储的值。
<a name="paFbz"></a>
### 手写new
new 关键词的主要作用就是执行一个构造函数,返回一个实例对象,在new 的过程中,根据构造函数的情况,来确定是否可以接受参数的传递。
1. 创建一个新对象;
2. 将构造函数的作用域赋给新对象(this 指向新对象);
3. 执行构造函数中的代码(为这个新对象添加属性);
4. 返回新对象。
// 第二版的代码 function objectFactory() {
var obj = new Object(),
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
var ret = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? ret : obj;
};
<a name="ZyEh3"></a>
### curry化分析
简单来说,就是在一个函数中预先填充几个参数,这个函数返回另一个函数,这个返回的新函数将其参数和预先填充的参数进行合并,再执行函数逻辑
const curry = (fn, length) => { length = length || fn.length return function (…args) { if (args.length < length) { return curry(fn.bind(this, …args), length - args.length) } else { return fn.call(this, …args) } } }
const curry = fn => { return tempFn = (…arg1) => { if (arg1.length >= fn.length) { return fn(…arg1) } else { return (…arg2) => tempFn(…arg1, …arg2) } } }
<a name="ZLflV"></a>
### 手写bind
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
// bind后的函数,可能会作为 构造函数使用
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
<a name="yhMjj"></a>
### js 的大数相加结果错误问题
因为JavaScript的Number类型是遵循IEEE 754规范表示的<br />JavaScript能精确表示的数字是有限的,JavaScript可以精确到个位的最大整数为2的53次方,超过这个范围就会精度丢失,造成JavaScript无法判断大小,从而会出现下面的<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/756869/1657524265063-826f796d-f94a-449b-961d-5c83a94ebe4f.png#clientId=u1702b8b1-6ede-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uafb6c89f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=52&originWidth=302&originalType=binary&ratio=1&rotation=0&showTitle=false&size=5465&status=done&style=none&taskId=u88df4bcd-063c-4ec2-b26b-c736803c154&title=)<br />如何解决呢,这里我的解决思路是采用BigInt(a)来将a转化为大整数,然后在计算过程中都要用大数的计算规则,数字后+n,这是因为大数不能和普通的number类型进行运算否则会报错,然后将最后的计算结果toString转化为字符串形式,注意不能使用parseInt和Number转化为数值类型,这样的话因为最后返回的依然是number类型依旧会失去精度
// 对于大数问题,可以用BigInt()和toString来解决 let b = BigInt(“9007199254740992”) //这里传入的是字符串是因为Oj在读取每行内容的时候是以字符串形式读取的,当然参数也可以是数值型 b = b + 1n console.log(Number(b));//9007199254740992 console.log(parseInt(b)); //9007199254740992 // toString方法将大数变成字符串,且大数的最后一位没有n console.log(b.toString()); //9007199254740993
```