VuePressVuePress
vue2
vue3
React
css
javascript
实操题目
http
真题
事件循环
题目
vue2
vue3
React
css
javascript
实操题目
http
真题
事件循环
题目
  • 值类型(基本数据类型)

值类型(基本数据类型)

  • underfined string number boolean Symbol null bigint

12

引用类型(复杂数据类型)

  • obj array fn Date RegExp Map Set WeakMap WeakSet Symbol
const obj = { a: 1 };
const arr = [1, 2, 3];
const a = null; // 特殊的引用类型, 指针指向空地址
function fn() {} //  特殊的引用类型,但不能用户存储数据, 所以没有 ‘神拷贝,复制函数’

12

typeof

typeof 可以判断值类型, 也可以判断引用类型(都是 object, 不能细分)

33

深拷贝

function deepClone(obj) {
  // 先判断
  if (typeof obj !== "object" || obj == null) {
    return obj;
  }

  // 初始化数据
  let result;
  if (obj instanceof Array) {
    result = [];
  } else {
    result = {};
  }

  // 进行拷贝
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      result[key] = deepClone(obj[key]);
    }
  }

  return result;
}

null 和 undefined 区别

  • 在 JavaScript 中,null 和 undefined 都表示没有具体的值,但它们在使用上有所不同。null 是一个可以被赋予变量的值,表示变量故意被设置为空,而 undefined 表示变量已声明但尚未赋值。下面是它们之间的一些关键区别:

== 运算符

除了 ==null 之外,其他都一律 ===

const obj = { a: 1 };
if (obj.a == null) {}

相当于:obj.a === null || obj.a === underfined

if 语句 和 逻辑运算

44

class

class Student {
  constructor(name, number) {
    this.name = name;
    this.number = number;
  }

  sayHi() {
    console.info(`姓名:${this.name}, 学号:${this.number}`);
  }
}
const zhooson = new Student("zhooson", 100);
console.info(zhooson.name);
console.info(zhooson.number);
console.info(zhooson.sayHi());

继承 extends

// 父类
class People {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.info(`姓名为${this.name}吃东西了`);
  }
}
// 子类
class Student extends People {
  constructor(name, number) {
    super(name);
    this.number = number;
  }

  sayHi() {
    console.info(`姓名:${this.name}, 学号:${this.number}`);
  }
}
const zhooson = new Student("zhooson", 100);
console.info(zhooson.name);
console.info(zhooson.number);
console.info(zhooson.sayHi());
console.info(zhooson.eat());

类型判断 instanceof

4

显示原型/隐式原型

class Student {
  constructor(name, number) {
    this.name = name;
    this.number = number;
  }

  sayHi() {
    console.info(`姓名:${this.name}, 学号:${this.number}`);
  }
}
const zhooson = new Student("zhooson", 100);

// 其中:zhooson.__proto__ === Student.prototype;
// 结论: 1. 每个class 都有 显示 原型
//       2. 每个实例 都有 隐式 原型
//       3. 实例的 __proto__ 指向对应 class 的 prototype

// 执行规则: 1. 现在自身规则寻找  2. 如果找不到去 隐式原型 寻找

原型链

。。。

手写一个 jQuery 插件


作用域

某个变量的合法的使用范围

  1. 全局作用域 window document 等
  2. 函数作用域 函数内部的变量
  3. 块级作用域 if for while 等有个花括号的 作用域 (const let 定义)

自由变量

  1. 一个变量在当前作用域没有定义,但是使用
  2. 向上级作用域一层一层找,直到找到为止
  3. 如果在全局域没有找到,则报错 xx is not underfined

闭包

函数可以访问其他函数内部变量

function create() {
  const a = 100;
  return function () {
    console.info(a);
  };
}

const fn = create();
const a = 200;
fn();
// 函数作为参数被传递
function print(fn) {
  const a = 200;
  fn();
}
const a = 100;
function fn() {
  console.log(a);
}
print(fn); // 100
// 闭包: 自由变量的查找是在函数定义的地方,向上级查找, 不是在执行的地方
// 场景: 1. 隐藏数据 只提供 API
function createCache() {
  const data = {};
  return {
    set: function (key, val) {
      data[key] = val;
    },
    get: function (key) {
      return data[key];
    },
  };
}
const c = createCache();
c.set("a", 100);
// c.get('a');
function f1() {
  var n = 999;
  nAdd = function () {
    n += 1;
  };
  function f2() {
    alert(n);
  }
  return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000

解释:在这段代码中,result 实际上就是闭包 f2 函数。它一共运行了两次,第一次的值是 999,第二次的值是 1000。这证明了,函数 f1 中的局部变量 n 一直保存在内存中,并没有在 f1 调用后被自动清除。 为什么会这样呢?原因就在于 f1 是 f2 的父函数,而 f2 被赋给了一个全局变量,这导致 f2 始终在内存中,而 f2 的存在依赖于 f1,因此 f1 也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。 这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在 nAdd 前面没有使用 var 关键字,因此 nAdd 是一个全局变量,而不是局部变量。其次,nAdd 的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以 nAdd 相当于是一个 setter,可以在函数外部对函数内部的局部变量进行操作。

this

this 取什么值, 是在函数执行的时候决定

2

异步

  1. 异步不会堵塞代码
  2. 同步会堵塞代码

场景: 1. ajax 调用 2. 定时器

单线程

  1. js 是单线程语言, 同时只能做一件事
  2. 浏览器和 nodejs 已经支持 js 启动进程,如 web work
  3. Js 和 Dom 渲染公用一个进程,因为 js 可以修改 dom 接口(做 dom 渲染则停止 js)

Promise

  1. 三种状态 pendding resolved rejected
  2. 变化不可逆 一次性的结果

节流/防抖

  • 防抖

防抖的原理是在事件被触发后,在指定的时间内如果事件没有再次被触发,则执行一次函数;如果在这段时间内事件再次被触发,则重新计时。简单来说,就是将多次触发的事件合并为一次执行,常用于需要等待用户操作完全停止后再进行处理的场景,如输入框搜索、窗口大小调整等

理论:俗话说, 防止抖动, 你先抖动着,啥时候停止,在执行下一步 场景: input 输入框搜索 API,等输入停止后,在触发搜索 限制执行次数,多次密集的触发只执行一次

function debounce(fn, delay) {
  let timer = 0;
  return function () {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments); // 透传 this
      timer = 0;
    }, delay);
  };
}
  • 节流 节流的原理是在规定的时间间隔内,无论事件触发了多少次,都只执行一次函数。也就是说,它会确保函数以一定的频率执行,而不是无限制地响应事件触发。节流常用于需要频繁但有规律地执行某些操作的场景,如滚动监听、鼠标移动等。

理论: 节省交互沟通,流,你一定指流量, 俗话:别急,一个一个来,按照排队时间来,插队者无效 场景:drag/scroll 期间触发某个回调,要设置一个时间间隔 限制执行频率,有节奏的执行

function throttle(fn, delay) {
  let timer = 0;
  return function () {
    if (timer) return;
    timer = setTimeout(() => {
      fn.apply(this, arguments); // 透传 this
      timer = 0;
    }, delay);
  };
}

总结: 节流关注“过程”,防抖关注“结果”

箭头函数

缺点:

  1. 没有 arguments
  2. 无法通过 apply bind call 改变 this
  3. 某些箭头函数代码难以阅读
  • 哪些场景不适用
  1. 对象方法
const obj = {
  name: "zhooson",
  getName() {
    // getName: () => { //  this 的指向则发生变化,并不是 obj了
    console.info(4, this.name);
  },
};
  1. 原型的方法
const obj = {
  name: "双越",
};

obj.__proto__.getName = () => {
  return this.name;
};
console.info(obj.getName());
  1. 构造函数
// const Foo = (name, city) => {
//   this.name = name;
//   this.city = city;
// };
// const f = new Foo('xxt', '北京!'); // 报错的
  1. call 方法
// const fn = () => {
//   console.info(this); // window
// };

function fn() {
  console.info(this); // { x: 1 }
}

fn.call({ x: 1 });

严格模式 use strict

  1. 全局变量必须实现声明
  2. 禁止使用 with
  3. 创建 eval 作用域
  4. 禁止 this 指向 window
  5. 函数参数不能重名

跨域

  1. 浏览器同源策略
  2. 同源厕率一般限制 ajax 网络请求, 不能跨域请求 server
  3. 不会限制 link img srcipt iframe 加载第三方资源

2

js 内存泄漏如何检测?场景有哪些?

  • 闭包的变量/函数都是常驻内存中

检测方式: 浏览器 perfomance memory heap 属性,增长趋势, addEventListsner

方法: 标记清除

  1. 多次创建 addEventListsner, 没有 remove 掉
  2. 全局变量,定时器引用,组件销毁时候没有清楚

WeakMap WeakSet 弱引用

检测方法:

浏览器 和 nodejs 事件循环机制是什么? event loop

  1. js 是单线程,防止代码堵塞, 我们吧代码分为 同步和异步
  2. 同步代码给 js 引擎执行, 异步代码交给宿主环境
  3. 同步代码放入执行栈中, 异步代码等待时机成熟送入任务队列中
  4. 执行栈完毕后,去任务队列看是否有异步任务,有就送到执行栈执行,返回循环查看执行, 这个过程叫 事件循环

宏任务

  • 宏任务 setTimeout setInterval ajax

  • 微任务 promise await async

微任务在下一轮 QOM 渲染之前执行,宏任务在之后执行

微任务 比 宏任务快

vdom 真的很快吗?

  1. virtual dom 即 vdom
  2. 用 js 对象 模拟 dom 节点数据
  3. 有 react 最先提出

vue react 框架的价值

  1. 组件化
  2. 数据视图分离,数据驱动试图 - 这是核心
  3. vdom 并不快, js 直接操作 dom 是最快的

js bridge

  • js 无法直接调用 native api
  • 需要通过特定的格式来调用

移动 h5 click 有 300ms 延迟, 怎么解决?

  1. 以前的方案,faskclick 的方案
  2. 现在方案:<meta name="viewport" content="width=device-width, initial-scale=1.0">

token 和 cookie 有什么区别?

  • cookie: HTTP 标准;跨域限制;配合 session 使用; (最大 4k), cookie 默认被浏览器存储
  • token:无标准;无跨域限制;用于 JWT; token 需要自己手动存储

jwt 弊端

  1. 用户信息存储在客户端,无法快速封禁某用户
  2. 万一服务端秘钥被泄漏,则用户信息全部丢失

jwt session 那个更好?

  1. 如有严格管理用户信息的需求(保密、快速封禁)推荐 Session
  2. 如没有特殊要求,则使用 JWT(如创业初期的网站)
  3. 聊天类 session 可以快速封禁用户,jwt 需要等 token 失效才可以
  4. 万一 jwt 的 token 的密钥丢失就很轻松获取用户信息

如何实现单点登录(SSO)

  1. 基于 cookie cookie 默认是不共享的, 但是有些情况是可以设置共享的 如a.baidu.com和b.baidu.com 主域名是一样的就可以共享

  2. SSO(第三方后端方法)

script defer 和 async 的区别?

  1. defer HTML 继续解析,并行下载 JS,HTML 解析完再执行 JS
  2. async HTML 继续解析,并行下载 JS,执行 JS,再解析 HTML

websocket 和 http 有什么区别?

  1. WebSocket 协议名是 ws://,可双端发起请求
  2. WebSocket 没有跨域限制

网页 和 iframe 的通讯?

  1. 使用 postMessage 通讯
  2. 同源采用 cookie ,不同源采用 session
  3. websocket

首屏 优化?

  1. 路由懒加载
  2. ssr 技术
  3. APP 预获取
  4. 图片懒加载
  5. 分页

如果一个 H5 很慢, 如果排查性能问题?

Map 和 Set 的区别

  1. Map 是有序结构, key 是任意类型, Object 无序结构,key 俩种类型(字符串或者 symbol 类型)
  2. Set 可以去重
  3. Map 和 Set 比 Object/Array 执行要快
  4. new Map().set('1', 1) new Map().get('1')和 new Set().add(1)

为什么要禁用第三方浏览器 cookie

1

  1. 为了用户的隐私安全
  2. 第三方 cookie 会记录用户的行为和数据,方便做广告
  3. 有些浏览器默认禁止,chrome 增加 samesite(谷歌有广告)

setTimeout 和 setInterval 的区别?

区别 1: setInterval 不会等待你的函数执行完毕就开始计时下一个间隔, 而 setTimeout 会等待你的函数执行完毕后再开始计时。

例子:如果你设定了一个 setInterval,每隔 1 秒执行一次函数,但是这个函数需要 2 秒才能执行完毕,那么 setInterval 在函数还在执行的时候就开始计时下一个间隔,所以实际上函数会每隔 1 秒执行一次,而不是每隔 2 秒。 而 setTimeout 则不同,它会等待函数执行完毕后再开始计时,所以如果你设定了一个 setTimeout,每隔 1 秒执行一次函数,但是这个函数需要 2 秒才能执行完毕,那么实际上函数会每隔 3 秒执行一次,因为 setTimeout 会等待 2 秒的函数执行时间加上 1 秒的延迟时间。

区别 2: 错误处理:如果 setTimeout 或 setInterval 的回调函数中有错误,它们的处理方式是不同的。 setTimeout 在回调函数出错时,不会再次尝试执行该函数。 但是 setInterval 不管回调函数是否出错,都会继续按照设定的间隔时间执行。

区别 3: 返回值:setTimeout 和 setInterval 都会返回一个定时器 ID,可以用来在稍后取消定时器。 但是,这个 ID 在不同的环境中可能会有不同的表现。在浏览器中,它是一个数字;而在 Node.js 中,它是一个对象。

扩展: 如果页面卡住了, 后面过段时间,页面好了, setTimeout 和 setInterval 还能使用的吗? setTimeout 还可以用, 因为每隔一段时间, 向异步队列里面丢一个 setTimeout 事件

Promise.all 如果所有 Promise 都成功,返回的 Promise 会 resolve 一个包含所有结果的数组。如果其中任何一个 Promise 失败,返回的 Promise 会 reject 失败的原因。

const promise1 = fetch("https://api.example.com/data1");
const promise2 = fetch("https://api.example.com/data2");
Promise.all([promise1, promise2])
  .then(([result1, result2]) => {
    console.log(result1, result2);
  })
  .catch((error) => {
    console.error(error);
  });

追问:那如果一定要 then 怎么处理?

// 封装一层 promise
const promise1 = Promise.resolve("Success 1");
const promise2 = Promise.reject("Error 2");
const promise3 = Promise.resolve("Success 3");

const handlePromise = (promise) => {
  return promise.then(
    (result) => ({ status: "fulfilled", value: result }),
    (error) => ({ status: "rejected", reason: error }),
  );
};

Promise.all([
  handlePromise(promise1),
  handlePromise(promise2),
  handlePromise(promise3),
]).then((results) => {
  console.log(results);
  // 处理结果
  results.forEach((result) => {
    if (result.status === "fulfilled") {
      console.log("Success:", result.value);
    } else {
      console.error("Error:", result.reason);
    }
  });
});

// 使用 promise.allSettled

const promise1 = new Promise((resolve, reject) =>
  setTimeout(resolve, 100, "成功"),
);
const promise2 = new Promise((resolve, reject) =>
  setTimeout(reject, 200, "失败"),
);
const promise3 = new Promise((resolve, reject) =>
  setTimeout(resolve, 300, "成功"),
);

Promise.allSettled([promise1, promise2, promise3]).then((results) => {
  console.info(333, results);
  results.forEach((result, index) => {
    if (result.status === "fulfilled") {
      console.log(`Promise ${index + 1} 成功:`, result.value);
    } else {
      console.log(`Promise ${index + 1} 失败:`, result.reason);
    }
  });
});

call apply bind 详解

跳转链接

js 正则

好的,下面是 JavaScript 正则表达式中常用符号的专业名词、解释及其用法:

基本符号

  1. .(点号)

    • 名称:通配符
    • 解释:匹配除换行符以外的任何单个字符。
    • 用法:a.b 可以匹配 aab、acb 等。
  2. ^(脱字符)

    • 名称:行首匹配
    • 解释:匹配输入的开始位置。
    • 用法:^abc 匹配以 abc 开头的字符串。
  3. $(美元符号)

    • 名称:行尾匹配
    • 解释:匹配输入的结束位置。
    • 用法:abc$ 匹配以 abc 结尾的字符串。
  4. *(星号)

    • 名称:零次或多次匹配
    • 解释:匹配前一个表达式 0 次或多次。
    • 用法:bo* 可以匹配 b、bo、boo 等。
  5. +(加号)

    • 名称:一次或多次匹配
    • 解释:匹配前一个表达式 1 次或多次。
    • 用法:a+ 可以匹配 a、aa、aaa 等。
  6. ?(问号)

    • 名称:零次或一次匹配
    • 解释:匹配前一个表达式 0 次或 1 次。
    • 用法:a? 可以匹配 a 或空字符串。

字符类

  1. [abc]

    • 名称:字符类
    • 解释:匹配方括号内的任意一个字符。
    • 用法:[abc] 可以匹配 a、b 或 c。
  2. [^abc]

    • 名称:否定字符类
    • 解释:匹配不在方括号内的任意字符。
    • 用法:[^abc] 可以匹配 d、e 等。
  3. [0-9]

    • 名称:数字字符类
    • 解释:匹配任何数字。
    • 用法:[0-9] 可以匹配 0 到 9 之间的任意数字。

元字符

  1. \d

    • 名称:数字字符
    • 解释:匹配任何数字字符,等价于 [0-9]。
    • 用法:\d 可以匹配 0 到 9。
  2. \D

    • 名称:非数字字符
    • 解释:匹配任何非数字字符,等价于 [^0-9]。
    • 用法:\D 可以匹配字母、符号等非数字字符。
  3. \w

    • 名称:单词字符
    • 解释:匹配任何字母、数字或下划线字符,等价于 [A-Za-z0-9_]。
    • 用法:\w 可以匹配 a 到 z、A 到 Z、0 到 9 以及 _。
  4. \W

    • 名称:非单词字符
    • 解释:匹配任何非字母、数字或下划线字符,等价于 [^A-Za-z0-9_]。
    • 用法:\W 可以匹配空格、符号等非单词字符。
  5. \s

    • 名称:空白字符
    • 解释:匹配任何空白字符,包括空格、制表符等。
    • 用法:\s 可以匹配空格、制表符等。
  6. \S

    • 名称:非空白字符
    • 解释:匹配任何非空白字符。
    • 用法:\S 可以匹配字母、数字、符号等非空白字符。

量词

  1. {n}

    • 名称:精确匹配
    • 解释:匹配前一个字符恰好 n 次。
    • 用法:a{3} 匹配 aaa。
  2. {n,}

    • 名称:至少匹配
    • 解释:匹配前一个字符至少 n 次。
    • 用法:a{2,} 匹配 aa、aaa 等。
  3. {n,m}

    • 名称:范围匹配
    • 解释:匹配前一个字符至少 n 次,至多 m 次。
    • 用法:a{2,4} 匹配 aa、aaa、aaaa。

断言

  1. x(?=y)

    • 名称:正向先行断言
    • 解释:匹配 x,仅当 x 后面跟着 y。
    • 用法:\d(?=px) 匹配 3px 中的 3。
  2. x(?!y)

    • 名称:负向先行断言
    • 解释:匹配 x,仅当 x 后面不跟着 y。
    • 用法:\d(?!px) 匹配 3em 中的 3¹²³。

for of 和 for in ? 哪个用于枚举,哪个用于迭代? 可迭代有哪些?可枚举数据有哪些?

  1. for...in 枚举 (遍历对象的可枚举属性(包括对象原型链上的属性))
  for (let key in object) {
    console.log(key, object[key]);
  }
  1. for of 迭代 (用于遍历可迭代对象(如数组、字符串、Map、Set、NodeList 等),但不能直接用于对象)
  for (let value of iterable) {
    console.log(value);
  }

尖头函数优缺点? 哪些场景不适用

优点:

  1. 代码简单
  2. 调用的时候确定 this 作用域

缺点:

  1. 对象方法不能用
const obj = {
  value: 42,
  getValue: function(){
   return this.value  // `this` 指向obj作用域
  }
};
  1. 构造函数不能用
const MyFunc = () => {};
const instance = new MyFunc(); // 报错:MyFunc is not a constructor

内存泄漏 检测方法?哪些关键帧?初次渲染对应哪个关键帧?

  1. 可以在chorme 浏览器 performance memory

  2. 关键帧 核心性能指标

关键帧定义健康值
FP (First Paint)首次像素渲染<1s
FCP (First Contentful Paint)首次内容渲染<1.8s
LCP (Largest Contentful Paint)最大内容渲染<2.5s
TTI (Time to Interactive)可交互时间< 3.5s
CLS (Cumulative Layout Shift)布局稳定性<0.1s
  • DCL: (DOMContentLoaded). 表示 HTML 文档加载完成事件。当初始 HTML 文档完全加载并解析之后触发,无需等待样式、图片、子 frame 结束。作为明显的对比,load 事件是当个页面完全被加载时才触发。

  • FP(First Paint)首屏绘制,页面刚开始渲染的时间。

  • FCP(First Contentful Paint)首屏内容绘制,首次绘制任何文本,图像,非空白canvas 或 SVG 的时间点。

  • FMP(First Meaningful Paint)首屏有意义的内容绘制,这个“有意义”没有权威的规定,本质上是通过一种算法来猜测某个时间点可能是 FMP。有的理解为是最大元素绘制的时间,即同LCP(Largest Contentful Paint )。

  • L(Onload)页面所有资源加载完成事件。

  • LCP(Largest Contentful Paint )最大内容绘制,页面上尺寸最大的元素绘制时间。

Last Updated:
Contributors: zhanghusheng