打磨技巧
深浅拷贝
只针对引用类型
浅拷贝
拷贝的是值,但引用数据类型的值为地址
方法:
Object.assign(newobj,oldobj)
Array.concat(newArr,oldArr)
配合展开运算符
...
比较复制
复制相当于把将要复制对象的地址,直接进行获取,而不是创建一个新的对象,赋予属性的值和名
//实现效果分隔线函数
const cut = () => { console.log("--------"); }
//复制
const obj = {
name: 'jason',
age: 18,
family: {
baby: 'small jason'
}
}
const o = obj;
//若改变o中属性的值,obj的属性值也会发生改变
o.age = 20;
console.log(o.age);
console.log(obj.age);
cut();
//浅拷贝assign
const obj1 = { ...obj };
obj1.age = 19;
//若更改了里面第二层及以上的内容,原来拷贝的对象中的内容也会发生改变
obj1.family.baby = 'tall jason';
console.log(obj1.family);
console.log(obj.family);
console.log(obj1.age);
console.log(obj.age);
cut();
//浅拷贝concat
const a = [1, 2, 3];
const b = [1, 2, 3, [1, 2, 3]];
const a1 = [...a];
const b1 = [...b];
a1[2] = 4;
console.log(a[2]);
console.log(a1[2]);
b1[3][1] = 4;
//若更改了里面第二层及以上的内容,原来拷贝的对象中的内容也会发生改变
console.log(b[3][1]);
console.log(b1[3][1]);
cut();
深拷贝
三种方法
- 递归实现
-
lodash
的cloneDeep(obj)
- 利用
JSON
转化为字符串再转换为对象进行完成
递归实现
先复习递归函数
在函数体中调用自身,并带有条件退出函数的函数
小案例
function getTime() {
document.querySelector('div').innerHTML = new Date().toLocaleString();
setTimeout(getTime, 1000);
}
getTime();
三种方法的实现
//定义输出分隔线函数
function cut() {
console.log("-------------------");
}
//1.递归函数实现
const obj = {
name: 'jason',
age: 28,
family: {
baby: "small jason",
toy: ['car', 'pani']
}
}
const obj1 = {};
function deepcopy(newobj, oldobj) {
for (let k in oldobj) {
//判断当前的属性值是否为数组或者对象,是则进行递归调用,换其他参数
if (oldobj[k] instanceof Array) {
newobj[k] = [];
deepcopy(newobj[k], oldobj[k]);
} else if (oldobj[k] instanceof Object) {
newobj[k] = {};
deepcopy(newobj[k], oldobj[k]);
} else {
//相当于浅拷贝
newobj[k] = oldobj[k];
}
}
}
//调用深拷贝函数
deepcopy(obj1, obj);
obj1.family.baby = 'tall jason';
obj1.family.toy[1] = 'plane';
console.log(obj1.family);
console.log(obj.family);
cut();
//2.lodash中的deepcopy()方法
const obj2 = _.cloneDeep(obj);
obj2.family.baby = 'tall jason';
obj2.family.toy[1] = 'plane';
console.log(obj2.family);
console.log(obj.family);
cut();
//3.JSON字符串再转换为对象,开辟一个新的对象
const obj3 = JSON.parse(JSON.stringify(obj));
obj3.family.baby = 'tall jason';
obj3.family.toy[1] = 'plane';
console.log(obj3.family);
console.log(obj.family);
第二种方法需要有lodash的文件
异常处理
try catch finally throw
try中包含的是要检查是否有异常的代码,catch中为抓取异常后的操作,finally为无论如何都会执行的代码
throw用于在函数体中应有的异常,或者是一些常见异常的抛出
const noHave = new Error('参数不能为空孩子');
function add(x, y) {
if (!x || !y) {
throw noHave;
}
console.log(x - (-y));
}
let x = 1;
let y = 2;
try {
add(x);
add(y);
add(x, y);
} catch (noHave) {
alert('你应该重新输入两个参数')
x = prompt('输入x');
y = prompt('输入y');
add(x, y);
} finally {
console.log('你的函数总该运行成功了把');
}
this指向
-
普通函数谁调用就指向谁
-
箭头函数并不存在this
默认绑定外层的this,若外层没有,则向更外层进行寻找,都找不到,默认为window
//1.普通函数
console.log(this);
setTimeout(function () {
console.log(this);
}, 1000)
const obj = {
name: 'jason',
play: function () {
console.log(this);
}
}
obj.play();
//2.箭头函数
const obj1 = {
name: 'jason',
play: () => {
console.log(this); //windows
}
}
const btn = document.querySelector('button')
btn.addEventListener('click', () => {
console.log(this);
})
改变this指向
call()
apply()
bind()
//call(this,其他参数)
function fn(x, y) {
console.log(this);
console.log(x + y);
}
const obj = {
name: 'jason'
}
// fn();
fn.call(obj, 1, 2);
//apply(this,[其他参数])
const obj1 = {
name: 'Jason'
}
const a = [1, 2];
fn.apply(obj1, a);
//bind(this) 不调用函数,返回一个函数,改变了this指向
const obj2 = {
name: 'biber'
}
const fn1 = fn.bind(obj2);
fn1(1, 2);
防抖(debounce)
单位时间内,频繁触发事件,只执行最后一次
如王者回城,搜索框,及手机号验证输入 ==> 提高性能,减少服务端请求压力
实现:
- lodash提供的防抖函数
- 手搓一个出来
let i = 0;
function mouseMove() {
i++;
box.innerHTML = i;
}
const box = document.querySelector('.box');
box.innerHTML = i;
box.addEventListener('mousemove', _.debounce(mouseMove, 500))
let i = 0; //盒子里面的变量
const box = document.querySelector('.box');
box.innerHTML = i;
function mousemove() {
i++;
box.innerHTML = i;
}
function debounce(fn, t) {
let timer;
//使用函数来作为返回,使用了闭包,timer只可以被返回后的函数修改,外界无法修改timer
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, t);
}
}
box.addEventListener('mousemove', debounce(mousemove, 300));
节流 throttle
单位时间内,频繁触发事件,只执行一次
如王者技能冷却
实现:
- lodash提供的节流函数
- 手搓一个
const box = document.querySelector('.box');
let i = 0;
box.innerHTML = i;
function mousemove() {
i++;
box.innerHTML = i;
}
box.addEventListener('mousemove', _.throttle(mousemove, 500));
const box = document.querySelector('.box');
let i = 0;
box.innerHTML = i;
function mousemove() {
i++;
box.innerHTML = i;
}
function throttle(fn, t) {
let timer = null;
return function () {
if (!timer) {
timer = setTimeout(function () {
mousemove();
//清空定时器 不用clearTimeout 因为定时器仍在工作,无法通过此方法进行清除
timer = null;
}, 500)
}
}
}
box.addEventListener('mousemove', throttle(mousemove, 500));
节流综合案例
视频播放时,通过节流每秒记录一次数据到本地存储,刷新页面后,将本地存储的播放时间位置赋给视频的当前时间的属性(要考虑第一次刷新页面时,没有数据在本地存储中)
const video = document.querySelector('video');
video.ontimeupdate = _.throttle(function () {
localStorage.setItem('currentTime', video.currentTime);
}, 1000)
video.onloadeddata = function () {
video.currentTime = localStorage.getItem('currentTime') || 0; //逻辑中断,若前面为真,则后面的0赋值不会执行
}