JS基础知识总结

151 阅读6分钟

"=="和"==="和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>

总结

  • 事件捕获和事件冒泡是事件传播的两个阶段,分别对应从根节点到目标元素和从目标元素到根节点的传播过程。
  • 事件委托则是一种编程技巧,它利用事件冒泡的特性,将事件处理程序绑定到父级元素,以减少事件处理程序的数量,并提高性能和代码的可维护性。

防抖节流理解及应用场景

防抖

  • 防抖是指在事件被触发后,延迟一定时间执行回调函数。如果在延迟时间内又有相同事件被触发,那么会重新计时,直到延迟时间内没有再次触发事件,才会执行回调函数。
  • 简单来说,防抖就是将多次连续触发的事件合并为一次执行,以避免不必要的资源浪费。

应用场景

  1. 按钮点击
  2. input搜索
  3. 窗口调整事件监听

节流

  • 节流是指在一定时间间隔内,只执行一次回调函数。无论事件触发频率多高,都会在固定时间间隔内执行一次回调函数。
  • 简单来说,节流就是每隔一段时间执行一次事件处理函数,以降低函数执行的频率。

应用场景

  1. 鼠标移动
  2. 滚动事件
  3. 频繁点击

var/let/const区别

块级作用域: let,const有块级作用域,var没有块级作用域,只有函数作用域。

变量提升: let,const没有变量提升,在声明之前的区域为暂时性死区,引用会报错,var有变量提升。

重复声明: let,const在同一块作用域下不可重复声明同一变量,var可以。

不可修改: const定义时必须赋值,且定义常量时不可修改。

浅拷贝和深拷贝

定义

浅拷贝:如果拷贝的是基础数据类型,则拷贝的即为基本数据类型的值,如果拷贝的是复杂数据类型,则是将拷贝值和源值指向同一块内存。

深拷贝:则是在内存中新开辟一块空间,源值和拷贝值互不影响,内存地址不一样。

浅拷贝实现

  1. =赋值
  2. ...扩展运算符赋值对象或数组
  3. Object.assign,Array.concat

深拷贝实现

  1. JSON.parse
  2. 通过递归实现
  3. 通过三方库如果lodash_.cloneDeep()方法。

数组去重

  1. 使用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 ]
  1. 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 ]
  1. 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 ]
  1. 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 ]
  1. 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 ]

数组扁平化

  1. 使用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]
  1. 使用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]
  1. 使用递归
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());