目录
- html
- css
- 层叠上下文 z-index
- js
- js技巧
- 深浅拷贝
- 浅拷贝(拷贝的是地址,只能修改第1层)
- 实战一下
- 直接赋值和浅拷贝的区别?
- 浅拷贝怎么理解?
- 深拷贝
- 异常处理
- throw关键字
- try...catch...
- call、apply、bind
- call(调用函数并可以传递参数)
- apply(经常跟数组有关系)
- ⭐️bind(不调用函数,还想改变this指向,就用bind)
- 发送短信小案例(在不使用_that,箭头函数,全局变量的情况下,只用bind来实现功能)
- 防抖和节流
- 防抖函数
- 节流函数
- 小案例
html
css
层叠上下文 z-index
mdn官网对z-index层叠上下文的介绍
不设置z-index值,默认值为auto
,啥也没有;
设置z-index值哪怕值为-1、0
也会创建层叠上下文
例如:有3个子盒子,那这3个子盒子的z-index只在设置了层叠上下文的父盒子里面生效
js
js技巧
深浅拷贝
浅拷贝(拷贝的是地址,只能修改第1层)
实战一下
↓↓↓拷贝对象↓↓↓
const obj = {
name: '张三',
age: 18
};
// 1、展开运算符
const o = {
...obj,
age: 21
};
// 2、Object.assgin()
Object.assgin(o, obj);
o.age = 21;
↓↓↓拷贝数组↓↓↓
- 扩展运算符;
- slice()、map()等数组方法;
- forEach() + push()方法;
- 手动循环赋值。
直接赋值和浅拷贝的区别?
- 直接赋值:只要是对象,都会互相影响,因为是直接拷贝对象栈里面的地址。
- 浅拷贝:如果是一层对象,不互相影响,如果出现多层对象拷贝还会相互影响。
浅拷贝怎么理解?
拷贝对象之后,里面的属性值是简单数据类型直接拷贝值,如果是引用数据类型则拷贝的是地址。
深拷贝
- 递归(函数内部自己调用自己;容易"栈溢出"错误(stack overflow),所以必须要加退出条件return);
// 1、普通拷贝没问题直接复制就行;2、如果遇到数组,再次调用递归函数;3、如果遇到对象,再次调用递归函数;4、先数组再对象。
function deepCopy(newObj, oldObj) {
for (let key in oldObj) {
// 先写Array,在写Object。因为数组一定属于Object,Object不一定属于数组
if (oldObj[key] instanceof Array) {
newObj[key] = [];
deepCopy(newObj[key], oldObj[key]);
}
else if (oldObj[key] instanceof Object) {
newObj[key] = {};
deepCopy(newObj[key], oldObj[key]);
}
else {
newObj[key] = oldObj[key];
}
}
}
const obj1 = {
name: '张三',
age: 18,
habit: ['王者荣耀', '吃鸡'],
family: {
baby: '小张三'
},
address: null,
phone: undefined
}
const obj2 = {};
deepCopy(obj2, obj1);
// 这种也行,ai写的代码更简洁。总之代码初衷是可读性高,自己看哪种要好吧~
function deepCopy(newObj, oldObj) {
for (let key in oldObj) {
if (oldObj.hasOwnProperty(key)) {
if (oldObj[key] && typeof oldObj[key] === 'object') {
newObj[key] = oldObj[key].constructor === Array ? [] : {};
deepCopy(newObj[key], oldObj[key]);
} else {
newObj[key] = oldObj[key];
}
}
}
}
- lodash官网的cloneDeep方法;
第三方JavaScript 实用工具库: lodash官网
里面有_.cloneDeep
方法,直接按照文档引用就行。 - JSON.parse(JSON.stringify())
const obj2 = JSON.parse(JSON.stringify(obj1));
异常处理
throw关键字
- 抛出异常时使用
- 会终止程序
- 经常和Error对象一起使用
throw '抛出错误';
throw new Error('抛出错误');
try…catch…
try {
obj.name = '张三';
}
catch {
throw new Error('try里面的代码不对,obj为undefined,找不到该name');
}
finally {
console.log('不管try里面的代码对不对,都会走我这儿');
}
call、apply、bind
具体语法请去mdn官网查看
call(调用函数并可以传递参数)
const obj = {
name: '张三'
}
function fn(x, y) {
console.log(this);
console.log(x + y);
}
fn(); // this指向window
fn.call(obj, 1, 2); // this指向obj
apply(经常跟数组有关系)
和call的第一个参数都是this指向,但是apply第二个参数传递必须得是数组
。
const obj = {
name: '张三'
}
function fn(x, y) {
console.log(this);
console.log(x + y);
}
fn(); // this指向window
fn.call(obj, [1, 2]); // this指向obj
// 求数组最大值
console.log(Math.max.apply(null, arr));
⭐️bind(不调用函数,还想改变this指向,就用bind)
const obj = {
name: '张三'
}
function fn(x, y) {
console.log(this);
console.log(x + y);
}
fn(); // this指向window
const fnBind = fn.bind(obj, 1, 2); // this指向obj
fnBind(); // bind会返回一个新的函数,参数跟apply一样
总结:
1、call和apply会调用函数,改变函数内部的this指向,只不过apply第二个参数传递必须得是数组
;
2、bind不会调用函数,可以改变函数内部this指向。call和bind参数一样,都是call/bind(thisArg, arg1, arg2, /* …, */ argN)
,只不过bind不修改原数组会return出一个新数组
(我们就修改这个新数组里面的this指向咯)
发送短信小案例(在不使用_that,箭头函数,全局变量的情况下,只用bind来实现功能)
<button>发送短信</button>
document.querySelector('button').addEventListener('click', function() {
this.disabled = true;
setTimeout(function() {
this.disabled = false;
}.bind(this), 2000)
})
防抖和节流
防抖函数
function mydebounce(fn, t) {
let timer;
return function() {
timer && clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, t);
}
}
节流函数
注意事项:定时器里面不能清除定时器,因为定时器还在运作,可以手动赋空进行覆盖
function mythrottle(fn, t) {
let timer;
return function() {
if (!timer) {
timer = setTimeout(() => {
fn();
console.log(timer);
timer = null;
console.log(timer);
}, t)
}
}
}
小案例
<button>点击</button>
let num = 0;
const btn = document.querySelector('button');
btn?.addEventListener('click', mydebounce(() => {
btn.innerHTML = `第${num++}次`;
}, 500))