"=="和"==="和Object.is()区别
- "=="当两边数据类型不同时,会进行类型转换,然后在进行比较。
- "==="当两边数据类型不同,不会进行类型转换,直接进行比较。
- Object.is()一般情况跟"==="判断相同,但是Onject.is处理了一些特殊的情况
+0 === -0 //true
NaN === NaN // false
Object.is(+0,-0) //false
Object.is(NaN,NaN) // true
js数据类型
- 原始数据类型(栈存储):Number,String,Boolean,Null,Undefined,Symbol,BigInt
- 引用数据类型(堆存储):Object
判断数据类型方法
- typeof
Null,数组,函数判断会返回Object,其它正确。
- instanceof
判断对象上的数据类型,原理判断其原型链上有没有找到该类型原型。 只能判断引用类型,不能判断原始数据类型
- Object.prototype.toSting.call()
使用Object原型方法判断,万能方法
object.toString()方法是不可以判断数据的类型的,因为Object.prototype.toSting.call()调用的是Object原型上的方法,可以进行判断,但是Object例如Function/Array都重新了toSting()方法,所以只能用来转字符,不能进行数据类型判断
事件冒泡/捕获/委托理解
事件冒泡
顾名思义就是事件会从内部结构逐级向上传播
例如有一个嵌套div结构,点击了最内层div,事件会逐步向外直到最外层元素。
可以通过event.stopPropagation()方法来阻止事件继续冒泡
事件捕获
与事件冒泡相反,事件捕获是事件从最外层开始,然后向下传递到目标元素的过程,在此过程中,每个元素都有机会在自己的事件处理程序中处理该事件
主要用于需要在事件到达目标元素之前进行处理的场景,比如需要在事件传播过程中进行某些拦截或处理的情况。在JavaScript中,使用
addEventListener方法时,如果第三个参数useCapture设置为true,则事件监听器将在捕获阶段被触发。
事件委托
事件委托就是使用事件冒泡的原理,在父节点(或祖先节点)上响应事件。将事件添加到父元素或者更高层的元素上,以便在子元素上触发的事件能够被父元素或更高层元素捕获并处理。而不是为每个子元素添加点击事件,当某个子元素被点击时候,就会触发父元素的点击事件。从而减少事件处理程序的数量,提高性能和代码的可维护性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 100px;
}
.li{
height: 100px;
border: 1px solid red;
background-color: yellow;
}
</style>
</head>
<body>
<div class="box">
<div class="li">1</div>
<div class="li">2</div>
<div class="li">3</div>
<div class="li">4</div>
</div>
<script>
const box = document.querySelector(".box");
box.addEventListener('click',function(e){
alert(e.target.textContent);
})
</script>
</body>
</html>
总结
- 事件捕获和事件冒泡是事件传播的两个阶段,分别对应从根节点到目标元素和从目标元素到根节点的传播过程。
- 事件委托则是一种编程技巧,它利用事件冒泡的特性,将事件处理程序绑定到父级元素,以减少事件处理程序的数量,并提高性能和代码的可维护性。
防抖节流理解及应用场景
防抖
- 防抖是指在事件被触发后,延迟一定时间执行回调函数。如果在延迟时间内又有相同事件被触发,那么会重新计时,直到延迟时间内没有再次触发事件,才会执行回调函数。
- 简单来说,防抖就是将多次连续触发的事件合并为一次执行,以避免不必要的资源浪费。
应用场景
- 按钮点击
- input搜索
- 窗口调整事件监听
节流
- 节流是指在一定时间间隔内,只执行一次回调函数。无论事件触发频率多高,都会在固定时间间隔内执行一次回调函数。
- 简单来说,节流就是每隔一段时间执行一次事件处理函数,以降低函数执行的频率。
应用场景
- 鼠标移动
- 滚动事件
- 频繁点击
var/let/const区别
块级作用域: let,const有块级作用域,var没有块级作用域,只有函数作用域。
变量提升: let,const没有变量提升,在声明之前的区域为暂时性死区,引用会报错,var有变量提升。
重复声明: let,const在同一块作用域下不可重复声明同一变量,var可以。
不可修改: const定义时必须赋值,且定义常量时不可修改。
浅拷贝和深拷贝
定义
浅拷贝:如果拷贝的是基础数据类型,则拷贝的即为基本数据类型的值,如果拷贝的是复杂数据类型,则是将拷贝值和源值指向同一块内存。
深拷贝:则是在内存中新开辟一块空间,源值和拷贝值互不影响,内存地址不一样。
浅拷贝实现
- =赋值
- ...扩展运算符赋值对象或数组
- Object.assign,Array.concat
深拷贝实现
- JSON.parse
- 通过递归实现
- 通过三方库如果
lodash的_.cloneDeep()方法。
数组去重
- 使用Set去重
const arr = [2, 1, 3, 54, 2, 5, 1, 5, 3, 6];
const newArr1 = [...new Set(arr)];
console.log(newArr1);//[ 2, 1, 3, 54, 5, 6 ]
- for循环/for...in结合includes
const arr = [2, 1, 3, 54, 2, 5, 1, 5, 3, 6];
const newArr2 = [];
arr.forEach((item) => {
if (!newArr2.includes(item)) {
newArr2.push(item);
}
});
console.log(newArr2);//[ 2, 1, 3, 54, 5, 6 ]
- for循环/for...in结合indexof
const arr = [2, 1, 3, 54, 2, 5, 1, 5, 3, 6];
const newArr3 = [];
for (let item in arr) {
if (newArr3.indexOf(arr[item]) === -1) {
newArr3.push(arr[item]);
}
}
console.log(newArr3);//[ 2, 1, 3, 54, 5, 6 ]
- filter结合indexof
const arr = [2, 1, 3, 54, 2, 5, 1, 5, 3, 6];
const newArr4 = arr.filter((item, index) => index === arr.indexOf(item));
console.log(newArr4);//[ 2, 1, 3, 54, 5, 6 ]
- reduce结合includes
const arr = [2, 1, 3, 54, 2, 5, 1, 5, 3, 6];
const newArr5 = arr.reduce((acc, cur) => {
if (!acc.includes(cur)) {
acc.push(cur);
}
return acc;
}, []);
console.log(newArr5);//[ 2, 1, 3, 54, 5, 6 ]
数组扁平化
- 使用flat
const arr1 = [
[1, 2],
[3, 4],
[5, 6, 7],
];
const formatArr1 = arr1.flat(Infinity);
console.log(formatArr1);//[1, 2, 3, 4,5, 6, 7]
- 使用reduce
const arr1 = [
[1, 2],
[3, 4],
[5, 6, 7],
];
const formatArr2 = arr1.reduce((acc, cur) => {
if (Array.isArray(cur)) {
acc.push(...cur);
} else {
acc.push(cur);
}
return acc;
}, []);
console.log(formatArr2);//[1, 2, 3, 4,5, 6, 7]
- 使用递归
function formatArr(arr) {
const result = [];
arr.forEach((item) => {
if (Array.isArray(item)) {
result.push(...formatArr(item));
} else {
result.push(item);
}
});
return result;
}
const formatArr3 = formatArr(arr1);
console.log(formatArr3);//[1, 2, 3, 4,5, 6, 7]
new操作符做了什么操作
function myNew(Constructor, ...args) {
// 创建空对象
const obj = {};
// 将构造函数原型赋值给obj原型
obj.__proto__ = Constructor.prototype;
//给实例绑定this并传参调用构造函数
const result = Constructor.apply(obj, args);
// 返回对象
return obj;
}
// 使用
function Person(name, age) {
this.name = name;
this.age = age;
this.getName = function () {
return this.name;
};
}
const person = myNew(Person, "david", 18);
const person2 = myNew(Person, "frank", 25);
console.log(person);
console.log(person.getName());
console.log(person2.getName());