JavaScript

JavaScript 与 ECMAScript

  • JavaScript的实现包含ECMAScript、BOM和DOM
  • ECMAScript是一个脚本语言标准,由ECMA-262定义并提供核心功能,只对语言的语法、类型、语句、关键字、保留字、操作符、全局对象等进行定义及规范描述(比如火狐的数据排序用的归并,谷歌V8的排序用的是一种变体快排)
  • ES6新增:块级作用域变量、类、模块、迭代器、生成器、箭头函数、期约、反射、代理等
  • ES7新增:数组 includes 方法,指数操作符 ** (即 Math.power()
  • ES8新增:对象valuesentries 方法,异步函数等
  • ES……:巴拉巴拉
  • ES11新增:可选链操作符、空值合并操作符等

DOM

  • Document Object Model,文档对象模型,提供与网页内容交互的方法和接口

BOM

  • Browser Object Model,浏览器对象模型,提供与浏览器交互的方法和接口
  • navigator对象的属性通常用于确定浏览器的类型
  • history管理历史记录
  • loaction管理地址
  • screen屏幕信息

HTML中的JavaScript

  • 通过 <script> 元素标签插入到 HTML 中
  • 默认情况下,<script> 元素按照出现的次序被解释
  • 页面在解析到 <body> 的起始标签时开始渲染,故位于 <head> 中的 <script> ,其加载、解析和解释的过程会阻塞渲染。可通过将 <script> 放置到 <body> 末尾优化
  • 外部脚本文件可通过 defer 属性告知浏览器延迟执行该脚本(解析到 </html> 再执行 )
  • 外部脚本文件可通过 async 属性告知浏览器该脚本不必等待该脚本下载或执行完毕后再加载其他脚本(不必保证次序),页面的加载也不必等待该脚本。异步脚本会保证再页面的load事件前执行
  • deferasync 的脚本不应该在加载期间修改DOM
  • 通过指定 type 属性为 module ,该脚本会被当作 ES6 模块

严格模式

  • ES5增加了严格模式的概念,通过在脚本或函数开头加上 "use strict"; 这一预处理指令开启,严格模式下不规范的写法会被处理,不安全的活动也会抛出错误

变量

  •   var const let
    作用域 函数作用域 块级作用域 块级作用域
    是否提升变量声明 否(严格说是因为存在暂时性死区) 否(严格说是因为存在暂时性死区)
    相同作用域内重复声明 允许 不允许 不允许
    在全局作用域下声明时是否属于顶层对象属性
    初始化 默认初始化为undefined 必须初始化 默认初始化为undefined
  • ECMAScript变量的数据类型:原始值、引用值
  • 对象(引用值本身)是储存在堆内存中的,保存引用值的变量实际上通过引用来访问引用值本身
  • 所有函数的参数都是按值传递的,如将函数当作参数传递时,传递的是指针而不是函数本身,函数名只是一个保存指针的变量

数据类型

  • 原始类型(简单数据类型):UndefinedNullBooleanNumberStringSymbol、(Bigint
  • 引用类型(复杂数据类型):Object–>FunctionArrayMapSetDateRegExp、原始值包装类型等
  • typeof 操作符可能的返回类型:undefinedbooleannumberstringsymbolbigintobjectfunction
  • typeof null 返回的是 object

类型转换

  • 真假值转换
    • undefine、null -> false
    • 数值:+0、-0、NaN -> false,其他都是 true
    • 字符串:空字符串转换为false,非空字符串true
    • 对象:true
  • 数值转换
    • 布尔值:true->1 false->0
    • null->0
    • undefine->NaN
    • 字符串
      • 包含数字字符时,转换为相应的数值(忽略其他字符),’123a’-> 123
      • 包含有效浮点数字符时,转换为相应的浮点数值
      • 包含有效的十六进制格字符时,转换为对应的十进制数值
      • 空字符串转换为0
      • 其他情况转换为NaN
    • 对象:先调用valueOf()方法,按照上述规则转换,如果最后是个NaN,再调用toString()方法,按照字符串规则转换
  • 字符串转换
    • toString()
      • 几乎所有的值都有一个toString()方法,用来获取当前值的字符串等价物
      • null和undefined值没有toString()方法
      • 数值使用toString()时,接受一个底数参数来切换进制
    • String()转型函数
      • 如果值有toString()方法,则返回该方法无入参调用的结果
      • 若值时null或undefined,返回相应的字符串、
  • 非全等比较(比较前会进行强制类型转换)
    • 如果有任一操作数为布尔值,则转换为数值类型比较
    • 数值和字符比较,将字符转为数值比较
    • 只有一个操作数为对象时,调用对象的valueOf()方法获取原始值,再继续比较
    • 两个都为对象,判断是不是同一个
    • null和undefined相等
    • 任一操作数为NaN,结果为false(两个NaN比较也是false)

基本引用类型和集合引用类型

  • 引用值(或对象)是某个特定引用类型的实例
  • 基本引用类型包括DateRegExp、原始值包装类型、单例内置对象
  • 集合引用类型包括ObjectFunctionArray、定型数组、MapWeakMapSetWeakSet(不太清楚红宝书怎么区分基本和集合引用类型的)
  • 原始值包装类型包含BooleanStringNumber:
    • 每种包装类型都映射到同名的原始类型
    • 以读模式访问原始值时,后台会实例化一个相应的包装类型对象,来操作相应的数据,因此原始值会拥有类似对象的行为
    • 涉及原始值的语句执行完毕后,包装对象就会销毁
    • 通过new操作符调用原始值包装类型构造含数得到的是对象,调用同名转型函数得到的是值
  • 内置对象指的是与宿主环境无关,程序执行时就存在的对象,其中单例的有:
    • Global:浏览器将其实现为window
    • Math:包含辅助完成复杂计算的属性和方法

Symbol

  • symbol与string、number类似,是一个基础类型的值,而不是对象,需要通过Symbol函数生成,不需要通过构造函数生成(通过new操作符调用Symbol方法会报错)
  • symbol可以转换为布尔值,但不能转换为数值
  • symbol用作属性名时,不可使用点运算符
  • symbol属性不可迭代,但也不是私有属性,可通过Object.getOwnPropertySymbols()获取

Object和Map

  • 相同的内存,Map能比Object多储存大约50%的键值对
  • Map的插入稍快与Object
  • 两者查找性能相当,但少量键值对下Object会快一些。两者的查找速度都不会随着键值对数量线性增加
  • Map的delete()操作性能更佳

WeakMap和WeakSet

  • 与Map和Set的主要区别是垃圾回收程序的区别对待,weak类型不会阻止垃圾回收(若将一个dom节点作为WeakMap的key,dom节点销毁时,会相应的回收这个key的内存)

迭代器和生成器

  • 迭代器是一个可由任意对象实现的接口,任何实现了Iterable的属性接口的对象都有一个Symbol.iterator属性(@@iterator),可通过定义对象的Symbol.iterator属性来实现自定义迭代
  • 迭代器通过连续调用next()方法获取 IteratorObject,该对象包含一个donevaluedone表示是否还有更多的迭代值可访问
  • 生成器通过在函数名前面加一个*表示,不可使用箭头函数定义
  • 生成器通过yield关键字停止和开始,通过生成器对象的next()方法恢复执行,通过return()方法可提前终止生成器
  • 生成器对象可作为可迭代对象

执行上下文、作用域和作用域链

  • 变量或函数的执行上下文决定了它们可以访问哪些数据以及它们的行为
  • 每个执行上下文中都包含一个关联的变量对象,用来存放该上下文中定义的变量和函数,在函数执行时则是将活动对象(只在函数执行时存在,最初只存在 arguments 变量)来当作变量对象
  • 函数定义时,就会为它创建作用域链,预装载全局变量对象,作用域链的下一个变量对象来自包含该函数的上下文,以此类推直到全局上下文的变量对象,作用域链会保存在内部隐藏属性 [[scope]]
  • 函数调用时会创建该函数的执行上下文,并通过复制函数的 [[scope]] 创建一个作用域链,然后创建该函数的活动对象推入作用域链的前端
  • 执行函数时,其上下文会推到上下文栈中,标识符的解析会沿着作用域链查找,执行完毕后弹出并销毁上下文

闭包

  • 闭包是指那些引用了另一个函数作用域中的变量的函数,通常在嵌套函数中实现。内部的函数作为参数传递或结果返回,仍能访问到其所在的外部函数的变量

  • 闭包形成的根本原因:当一个函数执行完毕时,将会销毁其执行上下文以及附带的活动对象,但由于外部函数的活动对象已被添加到内部函数的作用域链中,故无法被销毁,仍然保留在内存中,供内部函数使用

  • 使用场景:防抖、节流、单次调用函数等需要缓存的场景

    • export const debounce = (func, wait) => {
          let timeout;
          let canceled = false;
          const f = function (...args) {
              if (canceled)
                  return;
              clearTimeout(timeout);
              timeout = setTimeout(() => func.call(this, ...args), wait);
          };
          f.cancel = () => {
              clearTimeout(timeout);
              canceled = true;
          };
          return f;
      };
      
    • export const throttle = (func, wait, immediate) => {
          let timeout;
          let canceled = false;
          let lastCalledTime = 0;
          const f = function (...args) {
              if (canceled)
                  return;
              const now = Date.now();
              const call = () => {
                  lastCalledTime = now;
                  func.call(this, ...args);
              };
              // 第一次执行
              if (lastCalledTime === 0) {
                  if (immediate) {
                      return call();
                  }
                  else {
                      lastCalledTime = now;
                      return;
                  }
              }
              const remain = lastCalledTime + wait - now;
              if (remain > 0) {
                  clearTimeout(timeout);
                  timeout = setTimeout(() => call(), wait);
              }
              else {
                  call();
              }
          };
          f.cancel = () => {
              clearTimeout(timeout);
              canceled = true;
          };
          return f;
      };
      
    • export const once = (func) => {
          let called = false;
          let result;
          return function (...args) {
              if (called)
                  return result;
              called = true;
              result = func.call(this, ...args);
              return result;
          };
      };
      

尾调用优化

  • 尾调用优化值外部函数的返回值是一个内部函数的返回值时,通过重用栈帧的方式来优化内存管理(提前弹出外部函数的栈帧)
  • 尾调用优化的条件
    • 严格模式下
    • 外部函数的返回值是对尾调用函数的调用
    • 尾调用函数返回后不需要进行额外的计算
    • 尾调用函数不是闭包

对象

  • 对象的属性分为数据属性和访问器属性,ECMA-262通过内部特征来描述属性的特征,内部特征通过两个中括号表示
    • 数据属性特征:[[configurable]][[Enumberable]][[Writable]][[value]]
    • 访问器属性特征:[[configurable]][[Enumberable]][[Get]][[Set]]
    • 修改这些特征需要通过Object.defineProperty()方法
    • 通过Object.getOwnPropertyDescriptor()方法获取以上属性特征描述

构造函数

  • 任何函数只要通过new操作符调用就是构造函数,函数内部可通过new.target返回值来判断是否被用作构造函数调用
  • 按照惯例,构造函数的名称首字母应当大写
  • 用new操作符执行构造函数时会执行以下操作:
    1. 在内存中创建一个新对象
    2. 新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性
    3. 构造函数内部this被赋值为这个新对象
    4. 执行构造函数内部代码
    5. 返回构造函数返回的对象或是刚创建的新对象
  • 实例与构造函数原型之间有直接联系,但实例与构造函数之间没有直接联系

原型

  • 每个函数都会创建一个prototype属性,指向原型对象,默认情况下,原型对象的constructor属性指向该函数
  • Object.getPrototypeOf可返回实例的内部特性[[prototype]]
  • isPrototypeOf()方法判断一个对象是否另一个对象的原型
  • in操作符判断某个属性是否在实例或者原型上
  • hasOwnProperty()方法可判断某个属性是否在实例上

原型链和继承

  • 原型链是ECMAScript的主要继承方式,当一个原型是另一个类型的实例的时候,就可以层层套娃,实现原型链
  • instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
  • 一些继承的实现方式:
    • 盗用构造函数:是为了解决原型中包含引用值问题而出现的,也称作经典继承,基本思路是在子类的构造函数中调用父类构造函数。但由于必须在构造函数中定义方法,故函数不能重用,此外子类也无法访问父类原型上的属性方法。
    • 组合继承:结合原型链与盗用构造函数的优点,基本思路是使用原型链继承原型上的属性和方法,通过盗用构造函数继承实例属性(在构造函数中使用引用值)
    • 原型式继承:一种不通过自定义类型实现原型实现的对象之间的信息共享,实际上就是ES5增加的Object.create(),适合一些不需要单独创建构造函数的场景(换句话说之间通过原型对象之间来信息共享,而不是新通过自定义类型)
    • 寄生式继承:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象
    • 寄生式组合继承:使用寄生式继承来继承父类的原型,然后将增强的对象赋值给子类的原型,避免了父类构造函数的多次调用

  • 跟函数类似,通过类声明和类表达式来定义一个类,但类声明无法提升
  • 类受块级作用域限制
  • 默认情况下,类中的代码在严格模式下执行
  • new操作符实例化类相当于使用new调用类的构造函数
  • 类中定义的constructor不会被当作构造函数,若直接用new操作符调用,instanceof操作符的值会不一样
  • 类实际上是一个函数,与函数一样是JavaScript的一等公民,可以当作参数传递
  • 在类块中定义的方法将作为原型方法
  • 类可直接定义访问器,定义属性的set和get方法
  • 通过extends关键字继承任何拥有[[Construct]]和原型的对象(可继承普通的构造函数)
  • 在构造函数中通过super调用父类的构造函数,在静态方法中通过super调用父类的静态方法
  • 类很大程度上是基于既有原型机制的语法糖

代理和反射

  • TODO

关于this

  • 在标准函数中,this引用的是把该函数当成方法调用的上下文对象

  • 在箭头函数中,this引用的是定义箭头函数的上下文

  • 在全局函数中调用标准函数,非严格模式下this等于window,严格模式下等于undefined;作为某个对象的方法调用,this等于这个对象

  • 通过 call() 和 apply() 方法调用标准函数可改变函数的this值,两个不同点位apply第二个参数接受一个参数数组,通过模拟方法作为对象属性调用可实现call和apply

    Function.prototype.myApplay = function(newThis, argArray){
        const tempObj = newThis ?? window
        const funcSymb = Symbol('tempFunc')
        const tempObj[funcSymb] = this
        tempObj[funcSymb](...argArray)
    }
    
  • 通过 bind() 方法可以创建一个新的函数实例,传入的对象将绑定为新实例的this值,使用闭包可模拟一个bind方法

    Function.prototype.myBind = function (newThis) {
        const self = this
        return function (){
            self.apply(newthis, arguments)
        }
    }
    

标准函数与箭头函数

  • 函数的length属性保存函数定义的命名参数的个数
  • 非严格模式下,可通过arguments.callee获取正在执行函数的指针,通过该指针实现递归可避免函数名丢失的问题;严格模式下则可以通过命名函数表达式来实现递归来解决该问题
  • IIFE指立即调用的函数表达式,ES6之前通常使用该方法模拟块级作用域
  • 箭头函数无法使用argumentssupernew.target, 也不能用作构造函数,也没有prototype属性

同步和异步

  • 在JavaScript这种单线程事件循环模型中,同步操作和异步操作时代码依赖的核心机制
  • 同步行为对应内存中顺序执行的处理器指令,异步行为类似与系统中断,即当前进程的外部实体可触发(某段)代码执行

事件循环与任务(消息)队列

  • JavaScript设计之初就是一门处理浏览器网页交互的脚本语言,这决定了它注定是一门单线程语言。多个线程同时操作ui是很麻烦的事情
  • 当JS线程调用setTimeout、addEventListener时,会触发其他线程(定时器触发线程、事件触发线程),以此实现异步非阻塞
  • 其他线程执行完毕,会将对应的回调函数交给任务队列维护,当JS的执行栈为空时,会从消息队列中以此取出任务执行
  • 执行回调函数时会创造新的栈帧,执行栈再次为空时,继续取消息队列,以此反复,称之为事件循环
  • 事件循环是一种并发模型,它的优点是能够保证函数执行时不会被抢占。但当一个消息处理过久时会使得用户无法交互
  • 队列又分为宏队列和微队列,每执行完一个宏任务,会依次执行清空微任务队列

宏任务与微任务

  • 宏任务:I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame、requestIdleCallback(浏览器空闲时段调用)、ajax
  • 微任务:process.nextTick(node中)、promise.then catch finally、MutationObserver、MessageChannel

期约和异步函数

  • catch()相当于then(null,resolve)的语法糖
  • 手写一个promise:
    • 首先要清楚Promise/A+规范内容
      • 三个状态:pending(默认) fulfilled rejected
      • 构造函数接收一个执行器executor(),立即执行
      • 转换为成功状态需要提供状态值,可以是undefined、thenable、promise,转换为失败状态也需要提供状态值
      • 状态只能pending->fulfilled或pending->rejected,状态确认后不可改变
      • 调用then时,promise成功执行onFulfilled,失败执行onRejected,参数都是状态值
      • then中抛出异常则向下一个then传递
    • class Promise {
        constructor(executor) {
          // 状态
          this.status = 'PENDING'; // FUlFILLED REJECTED
          // 成功值和回调队列
          this.value = undefined;
          this.onResolvedCallbacks = [];
          // 失败值和回调队列
          this.reason = undefined;
          this.onRejectedCallbacks= [];
          
          let resolve = (value) => {
            if (this.status === 'PENDING') {
              this.status = 'FULFILLED';
              this.value=value;
              // 依次将对应的函数执行
              this.onResolvedCallbacks.forEach(fn=>fn());
            }
          };
          let reject = (reason) => {
            if (this.status === 'PENDING') {
              this.status = 'REJECTED';
              this.reason = reason;
              // 依次将对应的函数执行
              this.onRejectedCallbacks.forEach(fn=>fn());
            }
          };
          //立即执行executor
          try {
            executor(resolve, reject);
          } catch (e) {
            reject(e);
          }
        }
          
        then(onFulfilled, onRejected) {
          if (this.status === 'FULFILLED') {
            onFulfilled(this.value);
          }
          
          if (this.status === 'REJECTED') {
            onRejected(this.reason);
          }
          
          if (this.status === 'PENDING') {
            // 如果promise的状态是 pending,
            // 需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
            this.onResolvedCallbacks.push(() => {
              onFulfilled(this.value)
            });
          
            this.onRejectedCallbacks.push(()=> {
              onRejected(this.reason);
            })
          }
          
        }
      }
      
  • 异步函数实际上是generator的语法糖,await是Promise的语法糖,需要用try/catch捕获
  • Promise.race()返回状态最先改变的那个
  • Promise.all()返回一个期约,该期约会等内部期约全部解决后再解决(内部有一个拒绝,则该期约拒绝)

事件

  • DOM事件流分为事件捕获,到达目标、事件冒泡三个阶段
  • DOM中发生的事件的信息会被收集到事件对象event中
  • preventDefault()方法用于取消cancelable属性为true的事件的默认行为
  • stopPropagation()方法用于立即阻止事件流在DOM结构中传播
  • DOM3 Events中定义的事件类型:UIEvent(涉及与BOM交互的事件)、FocusEventMouseEventWheelEventInputEventKeyboardEventCompositionEvent(合成事件,使用某种输入法编辑器输入时触发)
  • mouseentermouseover事件区别:前者不冒泡,经过一个元素的子元素时,mouseenter不会再次触发,而mouseover会反复触发

模块

  • ES6之间的模块化方式
    • IIFE+闭包
    • CommonJs:require指定依赖,使用exports对象指定自己的公共API。模块加载为同步操作,node中用的比较多
    • AMD:异步模块定义,按需获取依赖加载,通过define定义,一般在浏览器端使用
    • UMD:通用模块定义,为了统一CommonJs和AMD生态定义的规范。UMD定义的模块会在启动时检测要使用哪个模块系统,然后进行适配,最后把所有逻辑包装在一个IIFE中
  • ES6模块
    • 带有type="module"属性的脚本会告知浏览器其作为ES6模块执行(rollup和esbuild等基于此实现)
    • 借用了CommonJs和AMD的优秀特性:模块只能加载一次、单例、支持循环依赖等
    • 默认在严格模式下执行、不共享全局命名空间、模块顶级this为undefined、var声明的变量不会添加到window中、异步加载执行
    • 支持命名导出(通过as提供别名导出)和默认导出(实际上是使用default关键字作为别名)
    • 导入应放在顶部

正则

客户端存储

  • 各浏览器对cookie,localStorage,sessionStore的限制不尽相同
  • 一般每个cookie不能超过4096个字节,每个域下的cookie数目和总字节大小通常有限制
  • 大多数浏览器会限制每个源5MB的localStorage空间
  • 存储事件可监听
  • sessionStorage只会储存到浏览器关闭(会话),刷新页面和关闭tab后重新打开都不受影响

浏览器

渲染

  • 渲染过程
    • HTML->DOM树,CSS->CSSOM规则树
    • DOM树+CSSOM规则树=渲染树
    • 遍历渲染树,先开始布局
    • 绘制节点
  • 浏览器是多进程的,每开一个tab也会重新分配cpu资源,也就是新开一个渲染进程,该进程有多个线程
    • JS引擎线程
    • 事件触发线程
    • 定时触发器线程
    • 异步http请求线程
    • GUI渲染线程

跨域

  • 协议、域名、端口任一不同即为跨域
  • 常用跨域解决办法
    • CORS
    • JSONP
    • postMessage

性能优化

垃圾回收

  • 标记清理:对不使用的值加上标记,统一回收
  • 引用计数:该策略已不再使用,但我们仍应该及时解除全局变量、全局变量属性和循环的引用

计算机网络

五层网络模型

  • 应用层:HTTP、FTP、SMTP、DNS
  • 传输层:TCP(可靠的)、UPD(尽力而为的)
  • 网络层:网际协议 IP
  • 链路层:点到点协议、MAC地址
  • 物理层:阿巴阿巴

TCP三次握手

  • 三次握手真的很巧妙,你想不出有什么方式能用更少的次数来确认双方的发信和收信能力
  • 具体过程
    1. A发送信息给B,信息携带一个标记msg1(A伸手)
    2. B收到了A的信息,给B回信,同时带上原来的msg1标记,新加一个msg2标记(AB第一次握手,B确认了A的发信能力)
    3. A收到B的回信,再回个信,带上ms1和msg2(第二次握手,A从回信中发现了自己的标记msg1,确认了自己的收发信能力和B的收发信能力)
    4. B收到A的回信,建立连接(第三次握手,B从回信中发现了自己的标记msg2,确认了自己的收发信能力和B的收信能力)

TCP四次挥手

  • 数据传输结束,断开连接时的通信过程
  • 具体过程
    1. A通知B要释放(第一次挥手)
    2. B通知A收到,A进入等待状态(第二次挥手)
    3. B的高层应用没有数据发送,则向A发送释放报文(第三次挥手)
    4. A收到B释放报文,回复B确认,进入等待状态(第四次挥手)
    5. B收到A确认呢后进入关闭状态
    6. A等待一段时间后(2倍的最长报文段状态)进入关闭状态

HTTP和HTTPS

  • HTTPS = HTTP + SSL/TLS,(TLS是SSL标准化后的产物)
  • SSL使用非对称加密,对称指的是加密解密使用同一密钥,非对称使用不同密钥
  • HTTPS证书中包含了公钥,发送数据时会使用该公钥加密,接受端使用私钥解密,加大了破解成本,提高安全性
  • 加密和解密过程会有一定程度的性能损耗

HTTP2

  • 目前广泛使用的时HTTP协议版本为1.1
  • 建立在HTTPS协议的基础上,安全
  • 通过二进制分帧来进行数据传输,高效
  • 多路复用和连接共享,没有HTTP1中同个域的并发限制(得益于分帧机制,帧可以乱序发送,不再依赖多个TCP实现并行)

常见状态码

  • 1xx: 接受,继续处理
  • 200: 成功,并返回数据
  • 201: 已创建
  • 202: 已接受
  • 203: 成为,但未授权
  • 204: 成功,无内容
  • 205: 成功,重置内容
  • 206: 成功,部分内容
  • 301: 永久移动,重定向
  • 302: 临时移动,可使用原有URI
  • 304: 资源未修改,可使用缓存
  • 305: 需代理访问
  • 400: 请求语法错误
  • 401: 要求身份认证
  • 403: 拒绝请求
  • 404: 资源不存在
  • 500: 服务器错误

WebSocket

  • 基于http

Nginx

CDN

计算机基础

进程与线程

  • 进程:cpu分配资源的最小单位
  • 线程:cpu调度的最小单位

VUE

生命周期

  • img

双向绑定原理

  • VUE2

    • 监听器:数据劫持,通过defineProperty重写set/get,属性发生变化的时候通过订阅器通知订阅者

    • 订阅器:收集依赖,对使用到劫持数据的,加入订阅者队列
    • 订阅者:收到变化通知执行相应方法,更新视图等
    • 解析器:解析指令,初始化模板//不太懂,TODO
  • VUE3

    • proxy

虚拟DOM原理

  • 用JavaScript对象模拟真实的DOM树
  • 视图需要更新时,通过diff算法比较新旧虚拟DOM树的差异
  • 通过pach算法将差异应用到真实DOM树中

模板解析

  • 解析成抽象语法树AST
  • 遍历静态节点并优化
  • 生成渲染函数

diff算法

  • 传统的diff算法复杂度为O(n3),vue做了优化,只对同级节点进行比较,时间复杂度为O(n)。React中也是如此

  • 广度优先,对同层级比较增删改差异

TypeScript

###

打包编译

Webpack

  • loader:加载器,将其他类型的文件转换为webpack能有识别的JS或JSON文件
  • plugin:执行其他任务,如打包优化、资源管理、注入变量等
  • 热更新和热替换
  • 支持sourceMap
  • Externals:告知已在全区中存在的变量,不重复打包

Rollup

  • 使用ES6模块(最终由浏览器实现)
  • 静态分析代码中import,排除任何未实际使用的代码(Tree-shaking)
  • 适合打包纯js库

CSS

权重计算

  • !import 规则之外,最高级

  • 内联样式
  • ID选择器
  • 类选择器(包括属性选择和伪类)
  • 类型选择器(标签和伪元素)

清除浮动

  • 清除浮动是指内部只包含元素浮动的情况下,外部容器可能会存在高度塌陷时,需要清除浮动带来的影响(或内部其他元素需要消除浮动元素的影响)
  • 解决方法:
    • 添加新元素(伪元素或看不见的元素),并应用clear:both
    • 触发BFC

BFC

  • 特征

    • 指块级格式化上下文,一块独立的渲染区域

    • 元素在垂直方向上递进

    • 同一个BFC内两个相邻的BOX外边距会发出重叠

    • 靠近BFC边框的元素,外边距处与边框重叠(元素绝对布局时则会怼到BFC的外边距那儿去)

    • BFC会计算浮动子元素的高度

  • 触发方式

    • float不为none
    • overflow不为visible
    • display为inline-block table-cell或者table-caption
    • postion为absolute或fixed

其他格式化上下文

  • IFC:inline
  • GFC:grid
  • FFC:flex

数据结构与算法

时间复杂度(大O表示法)

  • 简单理解,时间复杂度就是单位化操作时间(操作步数)后,对运行时间求极限

  • 无论代码执行多少行,只要没有循环等复杂结构,都可以算入单位化的时间内(假设单位时间为1)

  • 常见的复杂度度量(小到大)

    • 常数阶O(1):执行时间并不会随着变量增长而改变

    • 对数阶O(logn):通常发生在以某种方式跳过循环的算法中

      let i = 1;
      while(i<n)
      {
          i = i * 2;
      }
      
    • 线性阶O(n):执行时间随着变量线性增长

    • 线性对数阶O(nlogn):一般发生在线性阶算法嵌套了对数阶的算法的算法中

    • 次方阶O(n**k):通常发生在多层嵌套中

    • 指数阶O(k**n):阿巴阿巴

排序算法

  • 冒泡排序:一轮一轮往外冒泡
  • 选择排序:每次都从剩下的里选出最大的
  • 插入排序:每一项都有一轮机会与左边的值比较,插入到最后一个比他大的项的前面
  • 归并排序:一种分治算法,不段分裂,到最小模块开始比较,比较结果合成大一级的模块继续比较,直至完成,复杂度O(nlogn)
  • 快速排序:一种分治算法,选择一个基准值,将小于基准值的放左边,大于基准值的放右边,对左右两个子集不断重复上述操作。快排的速度取决于基准值的选择,最坏情况O(n**2),平均O(nlogn),最好??
  • 计数排序:分布式排序算法,用来排序整数的,将整数映射为索引值,记录每个索引出现次数,生成一个新的排序数组
  • 桶排序:分布式排序算法
  • 基数排序:分布式排序算法

搜索算法

  • 顺序搜索
  • 二分搜索
  • 内插搜索

BFS和DFS

  • BFS的实现,可用一个队列,将每一层的元素依次放进去
  • DFS直接利用函数的调用栈,递归实现

算法技巧

  • 分治:递归划分解决子问题,每个子问题与原问题性质相同
  • 动态规划:画格子,根据上个子问题的解来求当前子问题的解
  • 贪心:通过局部最优解求得近似全局最优解
  • 回溯:当一条路错的时候,回到分岔口