Skip to content

Instantly share code, notes, and snippets.

@yzfgithub
Last active February 11, 2025 10:09
Show Gist options
  • Select an option

  • Save yzfgithub/6fb4a5abe0f4490778183f8865020575 to your computer and use it in GitHub Desktop.

Select an option

Save yzfgithub/6fb4a5abe0f4490778183f8865020575 to your computer and use it in GitHub Desktop.
面试题:

html

  • 行内元素:span,a; 块级元素: div,h1。。。 p ul li
  • html语义化: 正确的标签做正确的事情,页面内容结构清晰,利于seo,方便维护阅读理解
  • label标签:定义表单控件之间的关系,
  • iframe:优点:可以原封不动展示页面,方便做模版内容嵌入,做微服务,可以通过postMessage进行通信。 缺点:滚动条,阻塞onload,无法检索,不利于seo,浏览器共享连接池,影响加载速度
  • h5中form关闭自动完成:autocomplete=off
  • !DOCTYPE: 声明使用xhtml还是html,xhtml是xml重写的html,规范更严格
  • src和href区别:href用在link和a上,引用和页面关联,src表示引用资源,用资源替换当前元素,如img,video,script,iframe
  • dom tree构建过程:html解释器将网络或者本地磁盘获取的html网页和资源从字节流解释成dom树结构, java scrip执行在将字符串解释成词语创建各种节点的时候, html解释器加载文件流的 时候会 进行词法分析、语法分析,将词语转成节点后,多个节点形成的树状结构就是dom树。
  • img的title和alt:title是图片信息,alt是图片不显示时显示的文字
  • h5离线存储:.appcache 文件的缓存机制(不是存储技术),通过这个文件上的解析清单离线存储资源,这些资源就会像 cookie 一样被存储了下来, 使用: 在html上加一个 manifest = “xxx”
  • 浏览器只有不是异步加载,都应该会阻塞dom解析和渲染;内联样式阻塞渲染,但是解析正常; 加载和执行js都会阻塞dom渲染

css

  • 标准盒模型:内容宽度= content; ie的传统是: 内容宽度 = (content+padding+border),通过box-sizing控制
  • 重排/重绘:渲染树中一部分因为元素的规模尺寸,布局,隐藏等改变而需要重新构建/由外观改变引起重绘
  • BFC:块级格式化上下文,对盒子内部的渲染区域赋予特殊的渲染环境,不让它影响外部,如何触发:float,position,overflow,display:inline-block

js

  • 原型链: 每个函数都有个prototype属性,该属性指向生成实例的__proto__指向的对象,就是原型,原型也是对象构造的,如果实例没找到属性就会一直向上层原型寻找,这种链状结构是原型链

  • 6种继承方式:构造函数继承<子函数内每次执行都要创建一遍方法>,原型链继承<引用数据类型被所有实例共享>,组合继承:常用, 原型式继承,寄生式继承,寄生组合式继承

  • 作用域:可访问变量,对象,函数的集合,js执行的环境

  • 严格模式有哪些不同:禁止变量未声明就赋值,限制动态绑定,有一些保留字不能用,还一堆,没细心了解,目前基本都是严格模式。

  • 闭包:在一个函数内部创建另一个函数,把局部作用域定义的变量暴露出来供外部引用,应用:回调返回,防抖函数,封装私有变量

  • 浮点数精度问题原因:浮点数64位双精度存储时,十进制转换为二进制的时候可能遇到无限值,存储的时候省略后边多余的位数,导致问题的发生。

  • 变量提升:只有var定义的变量可以提升: let 创建过程被提升了,初始化没有提升 var创建和初始化都被提升了 fun创建,初始化,赋值都被提升了 const只有创建,初始化,没有赋值, 所谓暂时死区,是指具有块级作用域,变量需要先声明,然后再使用,否则的话,就会报错。 函数作用域let不可以重复声明,但是var可以

  • 函数提升:只提升函数声明,不提升函数表达式

  • this指向:this总是 指向一个对象,指向函数的调用方, 如果没有调用就指向window,可以通过call,bind,apply改变this指向,箭头函数this指向函数定义的对象,不能用bind等修改指向

  • new操作符:1.在堆上开辟一块新内存,创建一个新的对象,2.给新对象添加新的属性和方法, 3.this指向新的对象实例

  • 立即执行函数:函数在创建后立即执行

  • typeof & instanceof: instanceof:判断对象是否是某个构造函数的实例,返回true/false, 原理是对象的__proto__是否指向构造函数的原型 typeof:获取变量或表达式的类型,返回结果为:number, boolean, string, function,object, undefined

  • bind实现:传1个this指向对象和配置0-n个参数,this指向参数在函数内重新定义that = this,返回一个新的函数,that.apply(firstParams, Array.prototype.slice.call(arguments, 1).concat(Array.prototype.slice.call(arguments))); bind返回的是一个函数,必须主动调用 apply和call:立即执行, apply第二个参数是一个数组, call可以传多个参数,分隔; apply在es5之后可以是一个类数组,包含length的对象

  • forin && forof: 获得键名, 获得键值, of不能用在非iterable; forin还遍历原型链上定义的属性

  • ajax:readyState状态码:5个,0-4, 初始化,载入,载入完成,交互接收,接收 完成

  • script的defer和async:defer表明脚本立即下载但是等页面解析完后才执行(立即下载,稍后执行),async表示异步,不能保证脚本的执行顺序(异步下载,立即执行)

  • 函数柯里化:把接收多个参数的函数变成接收一个参数的函数并且返回一个新的可以接收剩余参数的函数

  • 什么是纯函数:相同输入,输出相同,函数执行不影响外部, 不受外部因素影响

  • 垃圾回收机制:当变量不再被引用时,浏览器v8引擎会将变量置为null,从而释放内存空间,这是浏览器的垃圾回收机制,内存分为堆和栈,栈存放的是变量的名字和对值的引用,堆存放的是变量的值,当变量设为null后,对应值在堆中的空间被释放。 如何避免内存泄漏:少创建全局变量,手动清除定时器,手动清除事件监听器,手动清除订阅发布事件监听器,少用闭包。

  • 事件循环机制:主线程,宏任务,微任务;js的任务队列分为同步任务和异步任务,所有的同步任务都是在主线程里执行的,异步任务可能会在macrotask或者microtask里面; 宏队列:setTimeout、setInterval、setImmediate、I/O、UI rendering;postMessage,MessageChannel; 微队列:promise.then、process.nextTick,Object.observe,MutationObserver; 先执行主线程,然后微队列先于宏队列执行; process.nextTick先于promise.then; setImmediate()在事件轮询之前执行,Process.nextTick在轮询之后执行

  • promise:是一种异步编程的解决方案,解决异步任务,防止回调地狱,常用方法:promise.resolve;promise.reject; promise.race:类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise 任务的结果,不管这个 Promise 结果是成功还是失败; promise.all:如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果;

  • generator:是一个普通函数,2个特点:function和函数名间有个星号,函数体内部使用yield语句定义内部状态,然后使用next()输出函数内容;

  • 防抖:高频次事件只能触发一次,再次触发需要重新计算时间, 多次执行变为最后一次执行;

  • 截流:事件几秒内只能执行一次,稀释频率;

  • es6: symbol 新的基本类型; set集合,可以去重, =》 arr = [1,2,1]; Array.from(new Set(arr)); Array.from => 类数组转换为数组,类数组有length,但是没有方法。 一些数组和对象的拓展方法Object.assign, Array.find/findIndex/fill/entries/keys/values; String.includes/startsWith...; set/map, weakSet/weakMap => weak是弱引用,没有内存泄漏,垃圾回收机制不考虑,无法遍历,只有加减查方法,没有属性; 元编程 proxy, Reflect

typescript:

  • 定义:是一种类型化的语言,允许你指定变量,函数参数,返回的值和对象属性的类型
  • 交叉类型(interp types):type a; type b; type c = a & b;
  • 联合类型(union types): type a = string | number
  • 泛型(generic types):给复用定类型的一种方式,捕获参数传递类型
  • Partial: 允许将T类型所有属性设置为可选
  • Required: 将类型里的所有属性设置为必选
  • Readonly: 转换类型里的所有属性,使不能被修改, 还可以在单独某个属性前加上关键字readonly
  • Pick<T,K>: 允许从一个已经存在的类型T中选择一些属性作为K,从而创建一个新的类型,对K的约束,属性必须来自T
  • Omit<T,K>: 与Pick正好相反,从T中删除K
  • Extract<T,U>:提取T中可以赋值给U的类型
  • Exclude<T,U>: 从T中剔除可以赋值给U的类型
  • Record<K,T>: 用来构建属性为T的, key为K的另外一种类型
  • NonNullable: 从T中剔除null和undefined
  • Mapped:映射类型允许你从一个旧的类型,生成一个新的类型
  • ts的类型保护: 可以使用 typeof, instanceof ,in 返回类型
  • ts的判断: 可以使用三元运算符:T extends U ? X : Y
  • keyof: 将一个类型映射为它所有成员名称的联合类型 => keyof {a:string,b:string} ==> 'a' | 'b'
  • any:任何类型
  • void: 无任何类型,没有类型,如果是函数则应没有返回值或者返回 undefined
  • unknown:未知类型:相当于 any,可以理解为官网指定的替代 any 类型的安全版本,只能 赋值给any和unknown
  • never:永远不存在的值,定义的时候代表永远不赋值,算是一种保护, never是所有类型的子类,可以赋值给任何类型。
  • type和interface的区别:相同=》 都能描述对象 或者函数;都允许拓展,可以互相继承。不同:type可以声明基本类型,联合类型,元祖;type语句可以使用typeof获取实例类型进行赋值;interface同名字定义多次可以合并
  • ts定义有2种模式:module模式和script模式, module模式必须明确的import到相应的文件中,而script模式默认全局生效,要注意定义类型声明模式

js代码

设计模式: 单例模式:

普通:
class User {
  constructor(){
    this.name = ''
  }
  static getInstance() {
    if(!User._instance) {
      User._instance = new User;
    }
    return User._instance
  }
}

闭包生成:
var Peopel = (function() {
  let _instance;
  function init(name) {
    return {
      name: name;
    }
  }
  return {
    createPeople: function(name) {
      if(!_instance) {
        _instance = init(name)
      }
      return _instance;
    }
  }
})

工厂模式:

function User {
  name: '1'
}

User.factory = function() {
  return new User();
}

观察者模式实现: proxy,reflect

class Stu {
  constructor(name, age) {
    this.name = 1;
    this.age = 1;
  }
}

var stu1 = new Stu();

var stu2 = new Proxy(a, {
  set: (target, key, value, receiver) => {
    Reflect.set(target, key, value, receiver);
  },
  get: (target, key, receiver) => {
    return Reflect.get(target, key, value);
  }
})
	
II:
class Student{
  constructor() {
    this.obsevers = [];
  }
  add(obsever) {
    this.obsevers.push(obsever)
  }
  remove(obsever){
    this.obsevers.filter(item => item == obsever)
  }
  notify() {
    this.obsevers.forEach(item => {
      item.update();
    })
  }
}
class obsever {
  constructor(name) {
    this.name = name;
  }
  update() {
    console.log('xx')
  }
}
let sub = new Student();
let obs1 = new obsever('1');
let obs2 = new obsever('2');
sub.notify();

Promise自己实现all的方法:

Promise.prototype.constructor.newAll = (promiseList) => {
  return new Promise((resolve, reject) => {
    var newPromiseList = promiseList.map(item => {
      return {
        pro: item,
        data: null,
      }
    });
    let promiseCount = 0;
    newPromiseList.forEach(val =>{
      Promise.resolve(val.pro).then(res =>{
        promiseCount++;
        val.data = res;
        if(promiseCount === newPromiseList.length) {
          let arr = newPromiseList.map(res3 => res3.data);
          resolve(arr);
        }
      }).catch(err=>{
        reject(err)
      })
    })

  })
};

var pro1 = new Promise((resolve, reject)=>{return resolve('1')});
var pro2 = new Promise((resolve, reject)=>{return resolve('2')});
var pro3 = new Promise((resolve, reject)=>{return reject('3')});
Promise.newAll([pro1, pro2, pro3]).then(res => console.log(res,'res')).catch(err=> console.log(err,'err'));
	
	
实现race:
Promise.prototype.constructor.newRace = (promiseList) => {
  return new Promise((resolve, reject) => {
    promiseList.forEach(val => {
      val.then(res => {
        resolve(res,'gg');
      }).catch(err => {
        reject(err);
      })
    })
  })
}
var pro1 = new Promise((resolve, reject)=>{return reject('1')});
var pro2 = new Promise((resolve, reject)=>{setTimeout(()=>{return resolve('2')},1000)});
var pro3 = new Promise((resolve, reject)=>{return reject('3')});
Promise.newRace([pro1, pro2, pro3]).then(res => console.log(res,'res')).catch(err=> console.log(err,'err'));

函数柯里化
function newAdd(){
  var args = Array.prototype.slice.call(arguments);
  const fn = function() {
    const sub_arg =Array.prototype.slice.call(arguments);
    return newAdd.apply(null, args.concat(sub_arg));  // 这里apply(null)代表this为全局作用域
  }
  fn.toString = function () {
    return args.reduce((a,b)=>{return a+b})
  }
  return fn;
}
	
防抖:
function debounce(fun, timeout) {
  let time;
  return function() {
    if(time) {
      clearTimeout(time)
    } else {
      time = setTimeout(() => {
        fun.apply(this,arguments);
      }, time)
    }
  }
}

截流:
function throttle(func, timeout) {
  let lastTime = new Date();
  return () => {
    let t = new Date();
    if(t-lastTime < timeout) {
      return false;
    } else {
      func.apply(this, arguments);
      lastTime = t;
    }
  }
}

浏览器

  • 缓存策略:强制缓存; expires(1.0)加个时间, cache-control(1.1)一般:public,private和max-age。 浏览器缓存一般放在内存和磁盘中,图片脚本字体等常常和页面交互的部分放内存,css等不常变的放磁盘,协商缓存:客户端在没有匹配到强缓存的前提下,向服务端发起了请求,请求的资源是否在上一次请求和这一次请求之间发生过变化,发生了变化则正常发起200响应,反之则发起304响应,直接触发协商缓存,两种方式,1.Last Modified 与 If-Modified-Since, 2.Etag与If-None-Match
  • history和hash两种路由:利用h5 history interface中新增的pushState和replaceState,在当前有的back,go等基础上提供的对历史记录进行修改的功能,执行修改时浏览器不会立即向后端发起请求,hash虽然出现在url中,但是不被包含在http请求中,对后端没有影响,改变hash不会加载页面,hash 本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了,hashchange监听变化;
  • 事件模型:捕获阶段,目标完成,冒泡阶段, 如何制止冒泡:event.stopPropagation(); 阻止默认事件:event.preventDefault()
  • 浏览器工作原理:用户界面,浏览器引擎,渲染引擎 ,网络, JS解释器,数据存储; 过程: 下载html,css,js资源,解析html生成dom树,构建渲染树,对渲染树进行排版布局,绘制渲染树,
  • 跨标签页通信:localstorage的onstorage监听, websocket,webworker
  • requestAnimationFrame: 动画帧,浏览器重绘前执行,第一个参数是回调
  • 如何提高性能:减少http请求次数,减少dns查询次数,避免页面跳转,延迟/提前加载,减少dom数量,减少iframe,使用cdn,压缩文件传输,开启浏览器缓存,减少cookie,样式表置顶, 优化图片,css sprite等
  • 浏览器工作原理:用户界面,浏览器引擎,渲染引擎 ,网络, JS解释器,数据存储; 过程: 下载html,css,js资源,解析html生成dom树,构建渲染树,对渲染树进行排版布局,绘制渲染树,

框架

  • spa:web页面初始加载html,css,js后不会因为用户操作继续进行页面加载和跳转,而是用路由机制实现html内容的变换, ui与用户的交互,避免页面的重新加载: 优点: 用户体验好,渲染快,相对服务器压力小,前后端分离; 缺点:初次加载慢, seo难度大,路由切换需要自定管理

  • v-show/v-if: show 页面还会渲染,适用频繁切换条件的场景;但是if是条件渲染,为true才渲染

  • 怎样理解vue单向数据流:props父组建更新传到子组件,但是子组件需要$emit才能反向触发父组件变量修改

  • computed和watch的区别和运用场景:computed计算属性,有缓存,只有它依赖的属性值发生变化,下一次获取computed的值的时候才会重新计算;watch更多的是观察作用,类似监听回调,当数据发生变化时进行后续操作; 计算属性有依赖用cxxx,监听变化之行异步或开销大操作时用 wxxx

  • 直接给数组赋值,vue能检测到吗:浏览器限制导致问题1. 利用数组索引修改数组项不能检测,2.修改数组长度; 解决1: Vue.set 主动触发,调用更新函数,解决2: 用Array.prototype.splice 替换

  • 生命周期:vue实例的一个完整生命周期=》开始创建,初始化数据,编译模版,挂载dom,渲染/更新,渲染/卸载

  • 异步调用放在哪一步合适: created,beforeMount,beforeMounted中,这个时候data已经创建,可以将数据赋值给data

  • 父组件监听子组件生命周期可以用@hooks 传递方法实现子组件生命周期函数的监听;

  • keep-alive是vue内置的一个组件,可以使被包含组建保留状态; 组件中data是一个函数是为了做作用域隔离

  • proxy可以直接监听对象而不是属性,可以直接监听数组变化,有很多方法,返回新对象,

  • 虚拟dom实现原理:用js对象模拟真实dom树,对真实dom进行抽象,diff算法比较虚拟dom树差异,然后将差异应用到真实dom树

  • vue key的作用:vnode唯一标记,可以是diff操作更准确更快速

  • vue和js区别:vue是一个框架,js是一种浏览器识别的语言,vue需要经过编译,转变为js后才能在浏览器执行,vue使用虚拟dom,优化速度。

  • vue的nextTick实现原理: 使用浏览器事件循环机制,将函数执行放到下一个函数调用栈,具体顺序:promise.then, MutationObserver, setTimeout

  • vue组件间通信:props/$emit, ref的parent/children, eventBus,vuex

  • vuex:响应式的转为vue开发的状态管理工具,如果状态改变,组件会相应更新,改变store需要显示提交commit,包括:state,getter,mutation,action,module

  • vue的ssr:客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染, 优点: 更快的seo,首屏加载更快,

  • mvvm:是一种软件架构模式, m指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,View 是视图层,也就是用户界面,ViewModel 是由前端开发人员组织生成和维护的视图数据层,从后端获取model,数据转换,生成view层使用的试图数据模型;

  • vue响应式原理 发布订阅模式: 定义一个dep的目标对象;目的是收集发布者,并且在该对象中实现一个更新方法; vue2.0使用的是Object.definePropert的getter,而vue3.0使用Proxy的get和Reflect对数据进行监听,并添加为发布者到目标对象 vue解析器解析vue语法为html过程中,为每个变量对应节点添加了订阅者,当响应对象执行set的时候,这些节点调用update更新方法

  • vue nextTick怎么实现的: 启用异步队列, 将nextTick方法添加到事件队列的末尾,具体实现方法: Promise.then > mutationObserver > setTimeout

  • react setState发生了什么:在代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

  • react fiber原理: 16之前react版本,react渲染dom元素是同步的,如果组件比较大,层级比较深,那么渲染它可能会花数百秒,而react组件渲染一直占用主线程,导致其他任务事件不能相应,基于此设计了fiber架构, 将组件更新分为2个时期:render前和render后,render前是可以被打断的,每隔一段时间跳出当前渲染,去查询是否有其他重要任务,这个过程中react一直复用Fiber数据结构构建新的tree,标记更新新的节点 放入队列中, render后期生命周期不能被打断,一次将所有变更更新到dom上;

  • vue和react的diff区别: 相同点:1.都忽略跨层级比较, 2.同层级比较不同类型组件生成不同点结构,3.同层级相同类型通过key识别,优化效率; 不同的:1.vue对className不同的同类型节点元素,认为是不同的,而react认为是相同的,只改属性值, 2.vue同级比较是从两边向中间比较,而react是从前往后。

  • redux原理: redux是一个状态管理器,将数据统一放到一个store里管理,组件中通过despatch触发action,将值传入reducer,返回一个新的state去更新store中对应的state,组件中使用subscribe为store订阅监听函数,数据改变后修改组件state,重新渲染页面

  • mobx原理: (Object.defineProperty,Proxy) 观察者模式, 定义observable,observer,Reaction, 定义数据变量在get的时候添加为被观察者observable,在使用mobx状态的时候添加观察者,当数据变化的时候, 添加依赖函数,当值变化的时候通过set依次触发observable,执行依赖函数, 对react组件的render方法进行跟踪,并让focusUpdate函数更新组件,底层还是对数据的观察

工程化

- 实现一个脚手架:依据node commandar包里的program 开发命令行,用inquirer包做命令行和用户的交互,最后开启child_process去执行一个clone命令下载项目模版, vue3.0实现是脚手架内嵌入了webpack
- webpack: 项目编译打包压缩工具,只能处理cjs文件,其他类型文件需要通过loader加载和转化后才能完成编译打包和压缩,plugin是为了拓展webpack没有的功能,在loader之后的整个生命周期生效。性能优化:优化loader文件搜索范围,按需加载。
- webpack插件编写:webpack插件是构造函数实例化实现的,构造函数有一个apply方法,可以接收compiler对象的引用,并在回调中使用;安装插件的时候,webpack compiler会执行apply方法;
webpack tree shaking:是一种通过清除多余代码来优化项目打包体积的技术,es6以前,使用cjs规范,引入模块require()是动态的,可以根据条件导入需要的代码,es6后,引入静态导入语法,具备tree shaking 的条件, 实现: es6 module引入静态分析,所以在编译的时候能够确定到底加载了哪些文件,通过静态分析程序流,判断哪些模块和变量没有使用或者引用,进而删除对应的代码
- cjs和esm区别:cjs是一种模块规范,最初应用在nodejs,运行在浏览器缺少对应规范,然后前端实现了amd(Requirejs),从es6开始,引入一套新的规范,在语言标准上实现了模块功能,但是兼容性不太好,webpack经过babel转换为cjs规范,使用上的差别:cjs是运行时加载, esm时编译时输出; cjs的this是当前模块,esm的this是undefined
roll
- cjs和esm区别:cjs是一种模块规范,最初应用在nodejs,运行在浏览器缺少对应规范,然后前端实现了amd(Requirejs),从es6开始,引入一套新的规范,在语言标准上实现了模块功能,但是兼容性不太好,webpack经过babel转换为cjs规范,使用上的差别:cjs是运行时加载, esm时编译时输出; cjs的this是当前模块,esm的this是undefined
- rollupjs:  应用创建使用webpack, 类库使用rollup, 代码拆分,静态资源多,或者依赖有很多cjs,那选择webpack,如果代码库基于es6,并且代码可对外提供,那可以rollupjs; rollup配置:input/output/plugin/…. 支持导出cjs,amd, umd, iife, esm包
- babel配置,作用,与插件编写:babel配置preset预设集合,配置plugins配置插件,还可以配置envirment,插件执行在预设前,插件按顺序执行,预设倒序执行,  插件编写:vistor模式,通过访问到内容,通过增删改修改内容;

协作工具

commit规范: google推荐的方式 fix/feat/refactor/style
git stash /git stash pop ;  把没有记录的新文件也放到暂存区 git stash -k -u
git cherry-pick 检出
git reset --hard ; git log; git --set-upstream origin master

网络

- xss跨站脚本攻击:恶意攻击者往web里插入恶意html代码,当用户浏览该页面时候嵌入其中web里面的html代码会被执行,从而达到恶意攻击用户的目的, 防御:注意标签与数据转义
- sql注入, 把sql命令插入到web表单递交或页面查询字符串,导致sql语义改变,泄漏数据,防御:验证输入,sql语句参数化
- csrf:跨站请求伪造,防御:增加验证码,token或refer判断

- 7层网络模型:物理层,数据链路层(mac),网络层(ip协议),传输层(tcp/udp),会话层,表示层,应用层(http/ftp/stmp)
- tcp/udp的区别:tcp是一对一面向连接的,udp是广播流,不面向连接;
- http/https的区别:http是明文传输,80端口,https是加密传输,443端口,
- https如何实现的加密传输?
- 三次握手四次挥手?

node

  • 事件驱动;nodejs中是事件驱动的,有一个循环线程一直从事件队列中取任务执行或者I/O的操作转给后台线程池来操作,把这个循环线程的每次执行的过程算是一次轮询.
  • setImmediate:即时计时器立即执行工作,它是在事件轮询之后执行,为了防止轮询阻塞,每次只会调用一个
  • process.nextTick:它和setImmediate()执行的顺序不一样,它是在事件轮询之前执行,为了防止I/O饥饿,所以有一个默认process.maxTickDepth=1000来限制事件队列的每次循环可执行的nextTick()事件的数目。
  • 特点:单线程,事件驱动,非阻塞i/o
  • 核心模块:eventEmitter,stream,fs,net
实现一个事件总线:
class EventBus {
  constructor() {
    this._eventList = new Map();
  }
  on(event, observer) {
     let curEvent = this._eventList.get(event);
     if(!curEvent) {
      this._eventList.set(event, [obsever])
     }
     curEvent.push(obsever);
  }
  off(event, observer) {
    let curEvent = this._eventList.get(event);
    if(curEvent) {
      let idx = curEvent.indexOf(obsever);
      curEvent.splice(idx, 1);
    }
  }
  emit(event, ...arg) {
    let curEvent = this._eventList.get(event);
    if(curEvent) {
      curEvent.forEach(item => {
        item.call(this, arg);
      })
    }
  }
}

算法(数据结构/排序/搜索)刷leetcode吧

@yzfgithub
Copy link
Author

yzfgithub commented Mar 30, 2022

eventsource和websocket的差别, websocket是怎么保持长链接的?eventsource是单向通信,而websocket是双向通信的,websocket使用的tcp三次握手创建连接,使用tcp的keepalive保持长链接

webRTC: 音视频技术,没怎么用过

tcp三次握手四次挥手:

  1. 第一次握手, 客户端发起请求,携带连接请求报文SYN=1和序列号seq=x,等待服务器响应,客户端状态调整为SYN_SENT
  2. 第二次握手,服务端返回响应,携带ACK=1,SYN=1,ack=x+1,seq=y,此时服务器状态变为SYN_RECV
  3. 第三次握手,客户端回复响应,发送报文携带ACK=1, ack=y+1, seq=x+1, 此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

第一次:客户端主动发起FIN=1,seq=u,客户端进入FIN-WAIT-1(终止等待1)状态
第二次:服务端返回报文携带 ACK=1,ack=u+1,并且带上自己的序列号seq=v, 服务端就进入了CLOSE-WAIT(关闭等待)状态
第三次:FIN=1 ACK=1,seq=w,ack=u+1
第四次:ACK=1 seq=u+1,ack=w+1

@yzfgithub
Copy link
Author

yzfgithub commented Mar 30, 2022

node特征:单线程,事件驱动,非阻塞I/O
判断node环境:判断global是否是window
同步和异步方法区别:同步方法一旦开始,调用者必须等到方法调用返回后才能继续执行,而异步方法会立即执行,进行后续操作
异步方式:promise,async/await, generator, 事件发布/监听模式
常见模块化规范:cmd,amd,cjs,esm
app.use和app.get区别:app.use(path,callback)中callback可以是路由对象或函数,而app.get(path, callback)中callback只能是函数
说一下事件循环:1.所有同步任务都在主线程上执行,形成一个执行栈,2.当主线程上执行栈为空时,检查事件队列是否为空,如果为空,则继续检查,如果不为空,就取出任务队列的首部事件,加入执行栈,执行任务,完成后继续检查执行栈执行任务

node的架构师什么样子的:三层。应用app 》v8以及node内置架构 》操作系统。 v8是node运行的环境,可以理解为node虚拟机,node内置架构可以分为三层:核心模块》c++绑定》libuv+CAes+http

核心模块:eventEmitter,stream,fs,net
全局对象:process,console,Buffer 和exports

node事件循环执行顺序: process.nextTick > setImmediate > setTimeout/setInterval

什么是EventEmitter ? EventEmitter是node中一个实现观察者模式的类,主要功能是监听和发射消息,用于处理多模块交互问题.

@yzfgithub
Copy link
Author

yzfgithub commented Mar 31, 2022

react,react-dom: react 负责描述特性,提供react API,包括 类组件,函数组件,hooks,refs等,都是react的特性,只负责特性长什么样,怎么用,不负责具体实现

react-dom 被称为渲染器, 负责在不同的宿主载体实现特性, 负责渲染 ,调用dom api 将操作指令实施到dom树上,可以看成react-reconciler和dom之间的翻译器
react-reconciler作用: 负责协调,生成fiber树, 协调和调度,产生操作指令

只调用了一次reactDom.render,setState和hooks如何实现的响应更新?
在创建组件实例阶段,react-dom设置了updater方法,setState时候调用的是updater的enqueueSetState的方法,在创建函数组件前,react-dom覆盖了ReactCurrentDispatcher的current,创建函数组件时,调用的是react-dom中定义 的hooks实现。

useMemo和useCallback的区别: useMemo和useCallback接收的参数是一样的,第一个参数是回调,第二个参数是要依赖的数据, useMemo用来缓存数据,当组件内部某一个渲染的数据需要通过计算得出的时候,这个计算依赖于特定的state,props数据,我们就用useMemo; useEffect是缓存函数,这个函数如果是由父组件传递给子组件的或者自定义hooks里面的函数,这个时候我们考虑缓存这个函数,不用每次都重新声明新的函数,避免释放内存后再分配内存的计算资源消耗, 子组件也不会因为这个函数的变动重新渲染;不要滥用会造成性能浪费,react中减少render就能提高性能,所以这个仅仅只针对缓存能减少重复渲染时使用和缓存计算结果。

typeof null == ‘object’ 为什么? 历史问题,跟最初存储有关, 没细研究

连续多次setState会渲染几次? react可以将多次setState调用合并为一个调用来提高性能,这里注意: react只是将react的合成事件和生命周期中直接调用setState做了合并处理, 而添加setTimeout等原生事件中,不受React管理, 会调用多次

高阶组件: 是把一个组件转换为另一个组件的函数, 应用: 权限控制 , 组件复用

@yzfgithub
Copy link
Author

yzfgithub commented Apr 10, 2022

ts抽象类: 抽象类是用abstract关键字声明的类, 里边的方法也要用abstract声明, 抽象类不能实例化, 需要子类去继承并且子类中需要重新实现抽象类中的方法
ts的特性: 跨平台; 静态类型检查; 有class, interface概念,面向对象; 支持es6
ts内置的数据类型: number, boolean, string, null, void, unknow, never, nonenullable, any
ts稳定版本是4.x;
ts中接口是什么? 定义了使用该接口的对象的框架或者规范, 关键字是interface, 定义了包括属性,函数和箭头函数的声明
ts中module是啥? 是包括函数,变量,对象, 接口和类的集合
ts中类型断言: 类似 类型转换, as
ts如何子类调用基类构造函数? 使用super(this)
jsx: 可嵌入的类似xml的语法, ts支持嵌入,类型检查和将jsx转换为js;
什么是三斜杠指令:
三斜线指令是单行注释,包含用作编译器指令的 XML 标记。每个指令都表示在编译过程中要加载的内容。三斜杠指令仅在其文件的顶部工作,并且将被视为文件中其他任何地方的普通注释。

/// 是最常见的指令,定义文件之间的依赖关系。

/// 类似于path但定义了包的依赖项。

/// 允许您显式包含内置lib文件。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment