Skip to content

js

JS由哪三部分组成

ECMAScript、BOM、DOM

内置对象

String、Number、Boolean、Array、Date、RegExp、Object、Math、JSON、Error

数组的常用方法

forEach、map、filter、reduce、reduceRight、some、every、indexOf、lastIndexOf、concat、slice、splice、join、sort、reverse、push、pop、shift、unshift

数据类型检测

  • typeof
  • instanceof
  • Object.prototype.toString.call

闭包

在函数内部,可以访问函数外的变量

  • 重复利用变量
  • 不会全局污染
  • 存在内存当中,提升性能
js
function fn(x) {
  return function () {
    console.log(' =====', x)
  }
}

const y = fn('abcd')

内存泄露

已经分配地址的对象,长时间占用或者无法清除导致

事件委托

利用事件冒泡实现,把子元素的事件委托给父元素,减少事件监听,提升性能

阻止事件冒泡,event.stopPropagation()

基本、引用数据类型(存放堆栈的不同)

  • Number、String、Boolean、Null、undefined、Symbol
  • Object、Array、Function

原型链

构造函数的实例共享属性和方法

js
class Person {
  sayName = function () {
    console.log('this is function sayName')
  }
}

const p = new Person()
console.log(' =====', p.__proto__ === Person.prototype)

__proto__可以理解成一个指针(实例对象当中的一个属性),指向原型对象(构造函数的原型prototype)

一个实例对象在调用属性/方法的时候,执行过程:实例本身--> 构造函数的原型 --> 原型的原型

ES6原型链加了什么东西

  • ES6核心是给原型链加了class/extends/super语法糖,简化继承
  • 新增标准化原型操作方法,替代__proto__
    • Object.getPrototypeOf()Object.setPrototypeOf()
  • static关键字区分静态方法与原型方法,优化原型链使用逻辑

__proto__和prototype的区别

  • prototype:仅函数(含类)独有,是一个对象,用于存放所有实例共享的方法 / 属性,是原型链的 “模板”
  • __proto__:所有对象(含函数实例)都有,指向该对象的原型(即其构造函数的 prototype),是实例连接原型链的 “桥梁”

new做了什么

  • 创建空对象
  • 空对象的构造函数通过原型链进行连接
  • 把构造函数的this绑定到空对象
  • 根据构造函数返回类型判断
    • 值类型 --> 返回对象
    • 引用类型 --> 返回该引用类型
js
function newFun(Fun, ...args) {
  let newObj = {}
  newObj.__proto__ = Fun.prototype
  const result = Fun.apply(newObj, args)
  return result instanceof Object ? result : newObj
}

function Person(name) {
  this.name = name
}

Person.prototype.sayName = function () {
  console.log(this.name)
}

const p = newFun(Person, 'abc')
p.sayName()
console.log(' =====', p)

继承

  • 原型链
  • 构造函数
  • 组合式
  • class

js设计原理

  • js引擎(V8引擎)
  • 运行上下文
  • 调用栈
  • 事件循环
  • 回调

this指向

  • 全局this、普通函数、匿名函数都指向window
  • apply、call、bind改变了的this指向
  • 箭头函数看外层是否改变了this,否则指向window
  • 非箭头函数,this指向最后调用它的对象
  • new改变this的指向

script标签 async defer区别

未指定直接运行js

  • async 加载DOM树的时候,同时执行js
  • defer 加载DOM树的时候,等DOM树加载完毕后,再执行js

setTimeout、setInterval最小执行时间

  • 4ms
  • 10ms

ES新特性

  • 块级作用域
  • class
  • set/map
  • 解构
  • async/await
  • 数组的新API
  • 扩展运算符
  • 模块化
  • Promise
  • 箭头函数
  • 函数参数默认值

apply、call、bind区别

都是用来改变this指向和函数调用

  • call 传一个参数列表,call方法和apply方法调用一致,只是传参不同,其中call的性能更好,
  • apply 传一个数组
  • bind 传参不会立即执行,会返回一个改变this指向的函数,这个函数还可以继续传参 bind()()

递归当中的问题

在递归当中,需要指定对应的终止条件(return)避免栈溢出

深拷贝

  • JSON.parse(JSON.stringify(obj))
  • 扩展运算符
  • 递归+遍历

事件循环

浏览器是单线程,为了不阻塞,用任务队列来调度代码

  • 微任务(microtask)
    • Promise.then/catch/finally
    • async/await 后面
    • queueMicrotask
  • 宏任务(macrotask)
    • setTimeout/setInterval
    • 事件点击、AJAX、I/O
    • requestAnimationFrame、script 标签

执行顺序

  • 先执行所有同步代码
  • 执行当前所有微任务(清空微任务队列)
  • 然后取一个宏任务执行
  • 再清空微任务
  • 循环往复

Ajax

  • 创建XmlHttpRequest对象
  • xhr.open()建立连接
  • xhr.send()发送请求
  • 接收响应
  • 渲染

get、post区别

  • 安全性
  • 获取/提交
  • 缓存方式
  • 请求退回,get无影响,post会重新提交
  • 浏览记录
  • get参数只能url编码、post可以传文件等其他格式

Promise内部原理

Promise是用来解决回调地狱问题的

  • pendding 初始化
  • fulfilled 成功
  • rejected 失败

其中当使用了Promise之后,是无法取消的,如果没有给Promise设置回调,那么其内部的错误无法反馈到外部

Promise、async、await区别

都是用于处理异步请求,分别在ES6和ES7被加入,其中async、await也是基于Promise进行实现的

  • Promise通过then、catch捕获异常、并且Promise是链式调用、不方便维护
  • async和await是通过try、catch捕获异常,让异步代码看起来和同步一样,易于维护

Promise 的方法

reject resole catch all

  • allSettled在其中一个promise返回错误时还可以继续等待结果
  • race 接收一个数组 得到最快返回的 这是一个新的promise
  • any 只要有一个reject就会得到一个失败的promise,但是会等全部promise执行完

手写Promise

实现resolve和reject

关键知识点

  1. 闭包的应用:resolvereject 函数形成了闭包,可以访问构造函数内部的变量。
  2. 立即执行函数模式:Promise 的执行器函数会在创建时立即执行,这是 Promise 与其他异步 API 的重要区别。
  3. 异常捕获:通过 try-catch 块捕获执行器中的同步错误,并自动转为 rejected 状态。
  4. 状态不可逆:通过检查 this.#state === PENDING 确保状态只能改变一次。

相关资源

js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 私用变量(es6的symbol)
  #state = PENDING;
  #result = undefined;

  constructor(executor) {
    const resolve = (data) => {
      this.#changeState(FULFILLED, data);
    };
    const reject = (error) => {
      this.#changeState(REJECTED, error);
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  #changeState(state, result) {
    if (this.#state === PENDING) {
      this.#state = state;
      this.#result = result;
      console.log(this.#state, '----', this.#result);
    }
  }
}

const p = new MyPromise((resolve, reject) => {
  resolve(1);
});

实现then方法

  • then方法:链式调用的关键(注册回调函数,返回新的 Promise)
    • 接收两个参数:onFulfilled(成功回调)、onRejected(失败回调)
    • 返回新的 Promise 实例(支持链式调用)
    • 将回调函数和新的 resolve/reject 打包推入队列
    • 尝试执行 #run() 立即处理
  • run方法:回调队列调度器(根据当前状态执行队列中的回调函数)
    • PENDING状态:直接返回,等待状态改变
    • 状态已改变:循环处理队列中的所有回调
    • 状态分支:FULFILLED 执行成功回调,REJECTED 执行失败回调
    • 出队处理:使用 shift() 从队列头部取出
  • handleCallback方法:回调执行器(处理回调函数的执行和结果传递)
    • 非函数回调(值穿透)
      • 直接将结果传递给下一个 Promise
      • 使用 queueMicrotask 异步执行
      • 保持 Promise 链的正常传递
    • 函数回调
      • 微任务调度(异步执行)
      • 执行回调函数获取返回值
      • 用返回值 resolve 下一个 Promise
      • try-catch 捕获异常并 reject
实现Promise的then方法
js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 私用变量(es6的symbol)
  #state = PENDING;
  #result = undefined;
  #thenQueue = [];

  constructor(executor) {
    const resolve = (data) => {
      this.#changeState(FULFILLED, data);
    };
    const reject = (error) => {
      this.#changeState(REJECTED, error);
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  #changeState(state, result) {
    if (this.#state === PENDING) {
      this.#state = state;
      this.#result = result;
      console.log(this.#state, '----', this.#result);
      this.#run();
    }
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this.#thenQueue.push({onFulfilled, onRejected, resolve, reject});
      this.#run();
    });
  }

  #run() {
    if (this.#state === PENDING) return;
    while (this.#thenQueue.length) {
      const {onFulfilled, onRejected, resolve, reject} = this.#thenQueue.shift();
      if (this.#state === FULFILLED) {
        this.#handleCallback(onFulfilled, resolve, reject);
      } else {
        this.#handleCallback(onRejected, resolve, reject);
      }
    }
  }

  #handleCallback(callback, resolve, reject) {
    if (typeof callback !== 'function') {
      queueMicrotask(() => {
        const settled = this.#state === FULFILLED ? resolve : reject;
        settled(this.#result);
      });
      return;
    }

    queueMicrotask(() => {
      try {
        const data = callback(this.#result);
        resolve(data);
      } catch (e) {
        reject(e);
      }
    });
  }
}

const p = new MyPromise((resolve, reject) => {
  resolve(1);
});


p.then(res => {
  console.log(res);
  return 2;
}).then(res => {
  console.log(res);
  return 3;
}).then(res => {
  console.log(res);
  return 4;
});

多次点击按钮发送请求,只允许发送最后一次请求(请求取消)

  • Axios:内置了CancelToken
  • Fetch API:依赖浏览器原生的AbortController
  • 按钮禁用

游览器的存储方式

  • cookie
    • 兼容好,请求头会自动携带,
    • 存储小,使用麻烦
  • localStorage
    • 操作方便,永久存储,兼容好
    • 值的类型被限定,并且在隐私模式下不可读取,不能进行爬虫
  • sessionStorage
    • 会话级的,页面关闭后失效
  • indexedDB
    • 键值存储,可快速读取

token存localStorage、cookie的区别

  • localStorage 后续请求发送都需要手动添加token
  • cookie 会自动发送token,不能跨域

token登录流程

  • 请求登录
  • 服务端验证,下发token
  • 前端存储token
  • 后续请求携带token
  • 服务端验证token
  • 响应

页面渲染流程

  • DNS解析
  • 建立TCP连接
  • 发送HTTP请求
  • 服务器响应
  • 页面渲染
    • 获取HTML、CSS
    • 把HTML解析成DOM树、把CSS解析成样式表CSSOM树
    • DOM+CSSOM树生成渲染树
    • 布局 把渲染树进行渲染
  • 断开TCP连接

DOM树和渲染树的区别

  • DOM树和HTML标签一一对应,包含了head和隐藏元素
  • 渲染树不包含head和隐藏元素

精灵图与Base64

  • 精灵图:把图片整合到一张图片中,减少HTTP请求次数,提升性能
  • Base64:将图片转换成字符串,然后通过url的形式进行展示

svg (Xml语法格式的图像)

  • 可直接插入页面,是DOM树的一部分
  • 可作为文件进行引入 <img src="xxx.svg" />
  • 可转成base64引入

JWT

json web token

  • 获取后端响应的JWT(token)携带了用户信息
  • HTTP请求头携带Authorization
  • 后端解析token校验
  • 解析出用户信息做后续逻辑操作

npm底层环境原理

node project manager

  • 网站 npmjs.com
  • 注册表 用来管理所有的包
  • 命令行工具

HTTP请求头与响应头的区别

请求头

  • Accept 支持的类型
  • Host 主机地址
  • User-Agent 浏览器信息
  • Referer 来源
  • Cookie 认证信息
  • Date 时间
  • connection 连接方式
  • x-request-with 与服务器通信的协议

响应头

  • Content-Type 内容类型
  • Server 服务器信息
  • Location 服务器地址
  • Refresh 重定向

缓存类型

  • 强缓存(本地缓存)不发送请求,直接从缓存中读取
  • 弱缓存(协商缓存)发送请求判断是否使用缓存,服务器返回304,从缓存中读取

同源策略

协议+域名+端口(一致表示同源)

其中:img、link、script标签是允许跨域的

解决跨域的方案

  • JSONP
  • CORS 后端允许跨域
  • nginx反向代理
  • webSocket

防抖、节流

  • 防抖:在 n 时间以后触发函数,若时间未到再次被触发,则会重置执行函数的倒计时。(多用于搜索框优化)
  • 节流:在 n 时间内无论触发多少次函数,只会执行第一次触发。从而稀释出发频率。 (多用于按钮,以及页面滚动)

后端接口无数据响应

  • 填充默认数据
  • if判断数据是否为空

无感登录(无感刷新token)

在token过期的时候,不需要用户操作,后台自动刷新token

  • 在响应器当中拦截,判断token返回过期之后,重新调用获取token
  • 后端返回过期时间,前端根据超时时间判断是否过期,何时发送请求获取token
  • 前端定时请求获取token

大文件上传

  • 分片上传
  • 断点续传
    • 服务端返回,从哪里重新开始上传,在浏览器端判断

函数中定义变量,返回一个立即执行函数,外部怎么修改这个变量

可以通过返回一个对象,该对象包含一个用于修改变量的方法

js
function myFunction() {
  let myVar = 0;

  return {
    getVar: function () {
      return myVar;
    },
    setVar: function (value) {
      myVar = value;
    }
  };
}

const obj = myFunction();
obj.setVar(10);

实现const

  • object.freeze({})
  • Object.defineProperty
js
Object.defineProperty(obj, 'value', {
  value: value,
  writable: false,
  enumerable: true,
  configurable: false
});

实现instanceof

js
function myInstanceOf(obj, constructor) {
  let prototype = Object.getPrototypeOf(obj);
  while (prototype !== null) {
    if (prototype === constructor.prototype) {
      return true;
    }
    prototype = Object.getPrototypeOf(prototype);
  }
  return false
}

requestAnimationFrame和requestIdleCallback在渲染优化中的执行时机差异

特性requestAnimationFramerequestIdleCallback
执行时机每帧重绘前(paint 前)每帧结束后,空闲时
优先级⭐⭐⭐⭐⭐(最高)⭐⭐(最低)
执行频率~60 次/秒(60fps)不固定,可能不执行
是否阻塞渲染是(如果耗时过长)否(自动切片)
适用场景动画、关键更新日志、预加载、非关键任务
是否与屏幕同步✅ 是❌ 否
超时机制❌ 无✅ 有 timeout 选项

parseInt输出

["1", "2", "3"].map(parseInt)等价于:["1", "2", "3"].map((item, index) => parseInt(item, index))

  • parseInt(item, index)
    • item 数组当前项
    • index 数组当前项的索引(进制)

故输出结果为:[1, NaN, NaN]

  • parseInt('1', 0) = 1 (默认进制为 10)
  • parseInt('2', 1) = NaN (不存在1进制)
  • parseInt('3', 2) = NaN (2 进制没有 3 这个数字)

By Modify.