记录前端路上每天的成长(持续更新)
2020.12.13 关于数组去重引发的思考
数组去重,在网上可以找到好多方法,最少得有11种方法,而面试的时候,主考官最想看见的回答还是关于使用hasOwnProperty的去重方法。
具体的去重代码如下:
function unique(arr) {
var obj = {};
return arr.filter(function(item, index, arr) {
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr));
其中,有一个让人比较困惑的点,就是关于(typeof item + item),经过自己测试之后发现,它会先进行typeof item这个运算,然后typeof item的值再加上item。
就好比如下代码:
var item = 1;
console.log(typeof item + item); //number1
var item = false;
console.log(typeof item + item); //booleanfalse
var item = 'false';
console.log(typeof item + item); //stringfalse
var item = null;
console.log(typeof item + item); //objectnull
var item = {};
console.log(typeof item + item); //object[object Object]
var item = [];
console.log(typeof item + item); //object
这前几个例子不难理解,难理解的是后面两个,为什么当item是空数组或者空对象的时候,返回的结果好像不太一样。
其实这个也比较好解释,这两个例子还是遵循之前的规则,先进行typeof item所以返回的是object。然后在进行+ item这个操作。
而这个+号,会进行隐式转换,它会向字符串这个方向转换,而 - *这个符号会向数字这个方向转换。所以空数组转化成字符串是’ ',空对象转换成字符串是[object Object]。
2020.12.15 开始尝试简单的手写代码实现一些简单功能(面试常考的)
就先从最简单的Ajax开始吧
let xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
} else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
let mes = JSON.parse(xhr.responseText);
console.log(mes);
}
}
}
xhr.open('get', "url", true); //open中的第三个参数决定是否异步发送请求。true表示异步。
xhr.send(null);
//如果发送的是post请求,则在send里面发送数据,还要再加一行代码
// xhr.setRequestHeader("Content-Type", "application/json"); 这个要在调用open之后,调用send之前调用
这里面还有一个要注意的点就是xhr的readyState属性它有五个状态,分别是:
0:未初始化。尚未调用open()方法。
1:启动。已经调用open()方法,但尚未调用send()方法。
2:发送。已经调用send()方法,但尚未收到响应。
3:接收。已经接收到部分响应数据。
4:完成。已经接收到全部的响应数据,而且已经可以在客户端使用。
实现防抖和节流
防抖
防抖的定义:防抖是指在一定的时间内再次触发此事件,会清空上次的事件重新开始,如果制定的时间内没有再次触发,那么这个事件才会执行。
例如: input输入信息,不可能每次按下都发起一个ajax请求,可以等一段时间内不输入了之后在发起请求。
代码如下:
function debounce(fn, delay){
let timer = null;
return function(){
if(timer){
clearTimeout(timer);
}
timer = setTimeout(fn, delay);
}
}
然后我们在给事件绑定函数的时候就可以使用这个函数,当然这里只是核心思想,没有考虑作用域等问题。
节流
定义:节流是指在一定的时间同一事件只会触发一次,只有超过了这个时间才会再次出发
例如: 验证码60秒内不可以再次触发(实际开发肯定是使用禁止,但是原理和验证码一样)
代码如下:
function(fn, delay){
let flag = true;
return function(){
if(!flag) return;
flag = false;
setTimeout(() => {
fn();
flag = true;
},delay);
}
}
2020.12.21数组或对象的深浅拷贝
浅拷贝
浅拷贝与深拷贝的主要区别在于浅拷贝在拷贝引用类型的值的时候,只会复制它的地址,也就是说拷贝之后两者共用一个地址,只要其中一个引用类型的值发送变化的时候,另一个也会受到影响,而这往往不是我们希望看到的。具体代码如下(此处只展示一种拷贝方法):
let obj = {
a: 100,
b:[10,20,30],
c: {
x: 10
},
d: /^\d+$/
};
let obj2 = {};
for(let key in obj) {
if(!obj.hasOwnProperty(key)) break; //不是私有属性,不遍历
obj2[key] = obj[key];
}
console.log(obj, obj2);
控制台输出的结果如下:
如果当我们改变obj2里面的引用值的数据时,obj也会跟着变化,如下图:
我们改变了obj2.c.x的值,结果obj里面对应的值也发生了改变。
当然了,要想避免这个情况发生,那我们可以使用深拷贝。
深拷贝
深拷贝相对于浅拷贝来说要复杂一些,但是也有一种比较简单的方法,就是使用JSON。代码如下:
let obj = {
a: 100,
b:[10,20,30],
c: {
x: 10
},
d: /^\d+$/
};
let obj2 = JSON.parse(JSON.stringify(obj));
哈哈哈,是的,这种方法只需要一行代码,但是它也有自己的限制,那就是对函数,Symbol, undefined, 正则, Data对象等这些不能准确拷贝,但是平时开发中很少出现这种情况,所以开发中我们可以使用这种方法,但是这种方法不是面试官想听到的,所以我们还有一种比较好的方法:
function deepClone(obj){
//过滤特殊情况
if(obj === null) return null;
if(typeof obj !== "object") return obj;
if(obj instanceof RegExp) {
return new RegExp(obj);
}
if(obj instanceof Date) {
return new Data(obj);
}
//不直接创建空对象目的,克隆的结果和之前保持相同的所属类
let newObj = new obj.constructor;
for(let key in obj){
if(obj.hasOwnProperty(key)){
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}
let obj2 = deepClone(obj);
console.log(obj, obj2);
这个代码只是深拷贝的部分代码,因为它还没有考虑function这个特殊情况,这个大家可以自己下去实现,也就是一个if判断语句,最后总结一下,js的深拷贝需要考虑一下四个点:
1、JSON 克隆不支持函数、引用、undefined、Date、RegExp 等
2、递归克隆要考虑环、爆栈
3、要考虑 Date、RegExp、Function 等特殊对象的克隆方式
4、要不要克隆 proto,如果要克隆,就非常浪费内存;如果不克隆,就不是深克隆。
真正意义上的深克隆实现起来弊大于利。