基础知识点--前端

317 阅读14分钟

一 基础

1.HTMLCollection

HTMLCollection 接口表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合(generic collection),还提供了用来从该集合中选择元素的方法和属性。

  • HTMLCollection.item():根据给定的索引(从0开始),返回具体的节点。如果索引超出了范围,则返回 null
  • HTMLCollection.namedItem():根据 Id 返回指定节点,或者作为备用,根据字符串所表示的 name 属性来匹配。根据 name 匹配只能作为最后的依赖,并且只有当被引用的元素支持 name 属性时才能被匹配。如果不存在符合给定 name 的节点,则返回 null

2.querySelector方法

  • querySelector() 方法返回匹配指定 CSS 选择器元素的第一个子元素
  • 注意: querySelector()方法只返回匹配指定选择器的第一个元素。如果你要返回所有匹配元素,请使用 querySelectorAll()方法替代

3.generator函数

  • generatorfunction*定义(注意多出的*号),并且,除了return语句,还可以用yield返回多次(yield的作用就是暂停
  • 调用generator对象有两个方法,
    • 一是不断地调用generator对象的next()方法:
      • next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果donetrue,则value就是return的返回值。
      • 当执行到donetrue时,这个generator对象就已经全部执行完毕,不要再继续调用next()了。
    • 直接for ... of循环迭代generator对象这种方式不需要我们自己判断done

问题:generator和普通函数相比,有什么用?

  • 因为generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能
  • 把异步回调代码变成“同步”代码
    //看上去是同步的代码,实际执行是异步的
    try {
        r1 = yield ajax('http://url-1', data1);
        r2 = yield ajax('http://url-2', data2);
        r3 = yield ajax('http://url-3', data3);
        success(r3);
    }
    catch (err) {
        handle(err);
    }
    
  • **next方法可以有参数 **:next方法参数的作用,是为上一个yield语句赋值。由于yield永远返回undefined,这时候,如果有了next方法的参数,yield就被赋了值

引申:应用场景:

  • 状态机
  • 异步操作的同步化写法

可参考链接:理解 ES6 Generator 函数

4.prefetch预渲染

  • prefetch一种期望,预测会加载指定的资源,以备下一个导航或者下一屏页面使用,但对当前的页面并没有什么帮助。如果prefetch使用不得当,还会造成资源重复加载的问题。页面不一定会使用prefetch指定的资源。
  • preload一种肯定,确认会加载指定资源,在页面加载的生命周期的早期阶段就开始获取,不区分下一屏。页面一定会使用preload指定的资源(不使用将会报警告)

参考链接:性能优化小册 - 让页面更早的渲染:使用 preload 提升资源加载优先级

5.symbol

由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。可以用来消除魔术字符串

  • 注意,Symbol 值作为对象属性名时,不能用点运算符
  • 在对象的内部,使用 Symbol 值定义属性时,Symbol必须放在方括号之中
  • Symbol 值作为属性名时,该属性还是公开属性,不是私有属性
  • 属性名的遍历:
    • 该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名
    • Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名
  • Symbol.for(),Symbol.keyFor()
    • Symbol.for:有时,我们希望重新使用同一个 Symbol,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol
    • Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key
       let s1 = Symbol.for("foo");
       Symbol.keyFor(s1) // "foo"
      
       let s2 = Symbol("foo");
       Symbol.keyFor(s2) // undefined
      

6.XMLHttpRequest对象

  • XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 在 AJAX 编程中被大量使用
  • XMLHttpRequest 可以用于获取任何类型的数据,而不仅仅是 XML。它甚至支持 HTTP 以外的协议(包括 file://FTP),尽管可能受到更多出于安全等原因的限制
  • XMLHttpRequest():该构造函数用于初始化一个 XMLHttpRequest 实例对象。在调用任何其他方法之前,必须先调用该构造函数,或通过其他方式,得到一个实例对象。 属性

7.rest和arguments

  • rest:剩余参数语法允许我们将一个不定数量的参数表示为一个数组。rest参数是一个真正的数组。注意:rest 参数之后不能再有其他参数(即,只能是最后一个参数),否则会报错,函数的 length 属性,不包括rest参数

  • 剩余参数和 arguments对象之间的区别主要有三个

    1. 剩余参数只包含那些没有对应形参的实参,而 arguments对象包含了传给函数的所有实参。
    2. arguments对象不是一个真正的数组,而剩余参数是真正的 Array实例,也就是说你能够在它上面直接使用所有的数组方法,比如 sort,map,forEach或pop
    3. arguments对象还有一些附加的属性(如callee属性)。arguments.callee是指向正在执行的函数的指针,可以用它来实现对函数的递归调用(注意:arguments.callee只能用于非严格模式下,在严格模式下,不能通过脚本访问arguments.callee。可以使用命名函数表达式来解决。) //求函数阶乘 function factorial (num){ if(num<=1){ return 1; }else{ return arguments.callee(num-1)*num; } }
  • arguments转换成数组的方法:

    • Array.prototype.slice.call(arguments)
    • [].slice.call(arguments)
    • Array.from(arguments)
    • bind方法
   function foo() {
        var TempSlice = Array.prototype.slice;
        // 把函数的call方法绑定在数组slice方法上,之后再给call方法传递参数
        var slice = Function.prototype.call.bind(TempSlice);
        return slice(arguments);
    }
    console.log(foo(1,2,3))   //[1,2,3]

引申:

  • Array.fromArray.from()方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。 Array.from()
  • [].slice.call(arguments)
    • 因为slice内部实现是使用的this代表调用对象。
    • 那么当[].slice.call() 传入 arguments对象的时候,通过call函数改变原来slice方法的this指向,使其指向arguments,并对arguments进行复制操作
    • 而后返回一个新数组。至此便是完成了arguments类数组转为数组的目的
  • [].shift.call(arguments):则是删除并拿到arguments的第一项

8. V8

  • 在运行JavaScript之前,相比其它的JavaScript的引擎转换成字节码(ByteCode)或解释执行,V8将其编译成原生机器码(MachineCode)(IA-32, x86-64, ARM, or MIPS CPUs),并且使用了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序
  • 为了让V8优化代码,我们应该尽可能保证传入的参数类型一致

9.顶级域名、一级域、二级域名、三级域名什么区别?

因特网采用层次树状结构命名方法。域是名字空间中一个可被管理的划分(按机构组织划分),域可被划分为子域,子域可再被划分,即形成了顶级域名、二级域名、三级域名等。从右向左为顶级域名、二级域名、三级域名等,用点隔开。最左边的部分是单台计算机的名字(不能再进行划分了)。

  • URL的构成——http://主机名 . 域名(端口号、参数、查询等可选)
  • Internet上,每台计算机都有一个唯一的IP地址,计算机之间也只能通过IP地址来 通讯,IP地址通常是由4个字节来表示。例如:218.4.56.194,用户不可能记住这么长的IP地址。为了方便人们的记忆——Internet采用【域名】来标识计算机,通过DNS(域名系统)将域名翻译成IP地址,每一级的域名都有对应的DNS域名服务器。
  • 域名是给人用的,而IP地址是给机器用的,由DNS来做翻译。

10.cookie

Cookie 是一些数据, 存储于你电脑上的文本文件中。当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息。Cookie 的作用就是用于解决 "如何记录客户端的用户信息":当用户访问 web 页面时,他的名字可以记录在 cookie 中。在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录。Cookie 以名/值对形式存储,如下所示:username=John Doe当浏览器从服务器上请求 web 页面时, 属于该页面的 cookie会被添加到该请求中。服务端通过这种方式来获取用户的信息。

  • cookiehttp only属性用法: 如果在cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie信息,这样能有效的防止XSS攻击
  • samsite:用来防止CSRF攻击和用户追踪。用来限制第三方Cookie,从而减少安全风险
    • 可以设置三个值。
      • Strict:最为严格,完全禁止第三方Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie
        //这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态
        Set-Cookie: CookieName=CookieValue; SameSite=Strict;
        
      • LaxLax规则稍稍放宽,大多数情况也是不发送第三方Cookie,但是导航到目标网址的 Get 请求除外。设置了StrictLax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性
      • NoneChrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

11.MessageChannel

  • MessageChannel 接口允许我们创建一个新的消息通道,并通过它的两个MessagePort 属性发送数据。
  • 此特性在 Web Worker中可用。
  • 构造函数:MessageChannel()返回一个带有两个MessagePort属性的MessageChannel新对象。
  • MessagePort
    • MessagePort.postMessage
    • MessagePort.onmessage:Inherits event handlers from its parent, EventTarget

12.MutationObserver

构造函数MutationObserver():创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。

  • disconnect():阻止 MutationObserver实例继续接收通知,直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用。
  • observe():配置MutationObserverDOM更改匹配给定选项时,通过其回调函数开始接收通知。
  • takeRecords():从MutationObserver的通知队列中删除所有待处理的通知,并将它们返回到MutationRecord对象的新Array中。

13.constructor属性

constructor 是一种用于创建和初始化class创建的对象的特殊方法

constructor([arguments]) { ... }
  • 在一个类中只能有一个名为 “constructor” 的特殊方法。 一个类中出现多次构造函数 (constructor)方法将会抛出一个 SyntaxError 错误。
  • 在一个构造方法中可以使用super关键字来调用一个父类的构造方法
  • 如果没有显式指定构造方法,则会添加默认的 constructor 方法。
  • 如果不指定一个构造函数(constructor)方法,则使用一个默认的构造函数(constructor)。对于基类,默认构造函数是:constructor() {}。对于派生类,默认构造函数是: constructor(...args) { super(...args); }

14.window和document对象区别

  • window:代表浏览器中一个打开的窗口。成员对象:
    • window.event
    • window.document
    • Window.history
  • document对象:代表整个HTML文档,可用来访问页面中的所有元素。

16.encodeURIComponent

  • 如果你需要编码整个URL,然后需要使用这个URL,那么用encodeURI
  • 当你需要编码URL中的参数的时候,那么encodeURIComponent是最好方法。(encodeURIComponentencodeURI编码的范围更大,encodeURIComponent会对/进行编码)

17.扩展运算符...的应用

  • 合并数组
  • 与解构赋值结合
  • 函数的返回值
  • 字符串,将字符串转数组,识别32位的unicode字符
  • 实现了 Iterator 接口的对象
  • Map 和 Set 结构, Generator 函数

18.while、do while、for的区别

  • while:先判断再执行。
  • do...while:先执行再判断。(无论如何会执行一次循环体里面的代码,而while循环有可能一次都不会执行)
  • while、for用法:
    • 循环次数不限制、不确定用while
    • 循环次数有限制、已确定用for

19.localStorage

localStorage 中的键值对总是以字符串的形式存储。 (需要注意, 和js对象相比, 键值对总是以字符串的形式存储意味着数值类型会自动转化为字符串类型). 存储对象时需要使用到JSON.StringifyJSON.parse进行深拷贝

20.虚拟DOM到真实DOM的过程

参考链接:虚拟DOM到真实DOM的过程

引申:vue和react的diff算法事件复杂度

  • React 和 Vue 做的假设是: 检测VDOM的变化只发生在同一层 检测VDOM的变化依赖于用户指定的key

如果变化发生在不同层或者同样的元素用户指定了不同的key或者不同元素用户指定同样的key, React 和 Vue都不会检测到,就会发生莫名其妙的问题。 但是React 认为, 前端碰到上面的第一种情况概率很小,第二种情况又可以通过提示用户,让用户去解决,因此这个取舍是值得的。 没有牺牲空间复杂度,却换来了在大多数情况下时间上的巨大提升。 明智的选择!

  • 由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。 树的最小距离编辑算法的时间复杂度是O(n^2m(1+logmn)), 我们假设m 等于 n, 就变成 O(n^3)。

参考链接:React 和 Vue 的 diff 时间复杂度

21.微信小程序

  • AppID:它相当于你的小程序在微信中的 ‘身份证’ ,有了它,微信客户端才能确定你的 ‘小程序’ 的身份,并使用微信提供的高级接口。

  • openId:是表示用户在你的当前应用中的唯一标示,比如小程序,微信公众号等,这些都算是一个应用,如果你有多个应用,同一个用户的openId可能并不相同

  • unionid:借用微信官方文档中的话来说,如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的

同一个用户,在你的多个应用中,openid可能都不相同;但是,unionid一定会相同的

22.requestIdleCallback

window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。

你可以在空闲回调函数中调用requestIdleCallback(),以便在下一次通过事件循环之前调度另一个回调。

引申:setTimeout(f,0)应用

  1. 调整事件的发生顺序
  2. 分割耗时事件

如果一段程序过大,我们可以拆分成若干细小的块。由于setTimeout(f,0)实际上意味着,将任务放到浏览器最早可得的空闲时段执行,所以那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f,0)里面执行(分片塞入队列),这样即使在复杂程序没有处理完时,我们操作页面,也是能得到即时响应的。其实就是将交互插入到了复杂程序中执行

setTimeoutsetInterval的运行机制是,将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着,setTimeout指定的代码,必须等到本次执行的所有代码都执行完,才会执行

每一轮Event Loop时,都会将“任务队列”中需要执行的任务,一次执行完。setTimeoutsetInterval都是把任务添加到“任务队列”的尾部。因此,它们实际上要等到当前脚本的所有同步任务执行完,然后再等到本次Event Loop的“任务队列”的所有任务执行完,才会开始执行。由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeoutsetInterval指定的任务,一定会按照预定时间执行

23.HTTP协议的Keep-Alive 模式

请求头部和响应头部都有一个key-valueConnection: Keep-Alive,这个键值对的作用是让HTTP保持连接状态,因为HTTP 协议采用“请求-应答”模式,当使用普通模式,即非 Keep-Alive 模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP 协议为无连接的协议);当使用 Keep-Alive 模式时,Keep-Alive 功能使客户端到服务器端的连接持续有效

但是如果开启了Keep-Alive模式,那么客户端如何知道某一次的响应结束了呢?

以下有两个方法

  1. 如果是静态的响应数据,可以通过判断响应头部中的Content-Length 字段,判断数据达到这个大小就知道数据传输结束了。
  2. 但是返回的数据是动态变化的,服务器不能第一时间知道数据长度,这样就没有 Content-Length关键字了。这种情况下,服务器是分块传输数据的,Transfer-Encoding:chunk,这时候就要根据**传输的数据块chunk**来判断,数据传输结束的时候,最后的一个数据块chunk的长度是0。

24.forEach和map区别

  • forEach可以做到的东西,map()也同样可以。反过来也是如此。
  • map()会分配内存空间存储新数组并返回,forEach()不会返回数据
  • forEach()允许callback更改原始数组的元素。map()返回新的数组
  • forEach()的执行速度 < map()的执行速度

25.h5新增标签

section、article、aside、header、footer、nav、figure

新增属性:H5新增标签、属性

26. DOCTYPE的作用

<!DOCTYPE>标签是一种标准通用标记语言的文档类型声明,它的目的是要告诉标准通用标记语言解析器,它应该使用什么样的文档类型定义(DTD)来解析文档:声明文档的解析类型(document.compatMode),避免浏览器的怪异模式(默认就是浏览器的BackCompat怪异模式渲染页面)

<!DOCTYPE> 声明必须是 HTML 文档的第一行,位于 <html> 标签之前

27.获取元素位置

getBoundingClientRect()方法。它返回一个对象,其中包含了left、right、top、bottom四个属性,分别对应了该元素的左上角和右下角相对于浏览器窗口(viewport)左上角的距离

所以,网页元素的相对位置就是

  var X= this.getBoundingClientRect().left;
  var Y =this.getBoundingClientRect().top;

参考:用Javascript获取页面元素的位置

28.git hooks

(1)Hook 就是在执行某个事件之前或之后进行一些其他额外的操作

Git 也是如此,在 Git 中也有许多的事件(commitpush 等等),每个事件也是对应了有不同的钩子的(如commit前,commit后)

Git Hooks 的实现其实非常简单,就是在.git/hooks文件下,保存了一些 shell 脚本,然后在对应的钩子中执行这些脚本就行了

pre-commit这个钩子是在git commit命令执行之前触发

(2)Husky 配置

是一个让配置Git钩子变得更简单的工具

它的原理是让我们在项目根目录中写一个配置文件,然后在安装Husky的时候把配置文件和Git Hook关联起来,这样我们就能在团队中使用Git Hook

29.koa洋葱模型:中间件

zhuanlan.zhihu.com/p/279391637

二、项目相关

1.权限控制怎么做:

递归返回的权限值,并判断当前按钮是否包含在里边

const getRouterName = function(allArray, obj) {
  if (obj.children && obj.children.length > 0) {
    for (let i = 0; i < obj.children.length; i++) {
      allArray.push(obj.children[i].name)
      getRouterName(allArray, obj.children[i])
    }
  } else {
    return
  }
}

Vue.prototype.$targetPathAuth = function(targetPath) {
  let userRouter = []
  getRouterName(userRouter, store.getters.addRouters[0])
  return userRouter.indexOf(targetPath) === -1 #根据返回的true或false去判断按钮显示还是隐藏
}

2.前端工程化

  • 模块化:在文件层面上,对代码或资源的拆分
    • JS模块化
    • CSS模块化:从命名BEM上考虑,CSSmodulesscoped
    • 资源模块化
  • 组件化:在设计层面上,对UI(用户界面)的拆分。
    • 从UI拆分下来的每个包含模板(HTML)+样式(CSS)+逻辑(JS)功能完备的结构单元,我们称之为组件。
  • 规范化
    • 目录结构的制定
    • 编码规范
      • HTML规范
      • CSS规范
      • JS规范
      • 图片规范
      • 命名规范
    • 前后端接口规范
    • 文档规范
    • 组件管理
    • git分支管理
    • commit描述规范
    • 视觉图标规范
    • ...等
  • 自动化
    • 图标合并
    • 持续继承
    • 自动化构建
    • 自动化部署
    • 自动化测试

3.Istanbul:代码覆盖率工具

  • 安装$ npm install -g istanbul
  • $ istanbul check-coverage --statement -5 --branch -3 --function 100:该命令设置了3个覆盖率门槛:5个语句、3个 if 代码块、100%的函数。注意,这三个门槛是"与"(and)的关系,只要有一个没有达标,就会报错。
  • 可与测试框架结合使用
  • 可忽略某些代码。比如/* istanbul ignore if */在计算覆盖率的时候会被忽略

参考链接:代码覆盖率工具 Istanbul 入门教程

4.Web Worker

  1. 同源限制:分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源
  2. DOM 限制Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象
  3. 通信联系Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
  4. 脚本限制Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出AJAX请求。
  5. 文件限制:Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络

主线程: Worker()构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。Worker 线程对象的属性和方法如下:

Worker.onerror:指定 error 事件的监听函数。
Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
Worker.postMessage():向 Worker 线程发送消息。
Worker.terminate():立即终止 Worker 线程。

Worker 线程Web Worker 有自己的全局对象,不是主线程的window,而是一个专门为 Worker 定制的全局对象。因此定义在window上面的对象和方法不是全部都可以使用

self.name: Worker 的名字。该属性只读,由构造函数指定。
self.onmessage:指定message事件的监听函数。
self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
self.close():关闭 Worker 线程。
self.postMessage():向产生这个 Worker 线程发送消息。
self.importScripts():加载 JS 脚本。

参考链接: Web Worker 使用教程

5.单元测试有哪些

  • mochamocha是一个功能丰富的前端测试框架。所谓"测试框架",就是运行测试的工具。通过它,可以为JavaScript应用添加测试,从而保证代码的质量。mocha 既可以基于 Node.js 环境运行 也可以在浏览器环境运行
  • Karma:一个基于Node.jsJavaScript测试执行过程管理工具(Test Runner)。该工具可用于测试所有主流Web浏览器,也可集成到CI(Continuous integration)工具,也可和其他代码编辑器一起使用。
  • Travis.CI: 提供的是持续集成服务(Continuous Integration,简称 CI)。它绑定 Github上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。
  • jestJest 是 Facebook 开发的一款 JavaScript 测试框架。在 Facebook 内部广泛用来测试各种 JavaScript 代码

关于测试点击这里

6.前端构建流程是怎么样的?对于构建工具了解么?

grunt,gulp,webpack,parcel,fis等等

7.Gzip压缩

  • gzip命令用来压缩文件。gzip是个使用广泛的压缩程序,文件经它压缩过后,其名称后面会多处“.gz”扩展名。

  • gzip是在Linux系统中经常使用的一个对文件进行压缩和解压缩的命令,既方便又好用。gzip不仅可以用来压缩大的、较少使用的文件以节省磁盘空间,还可以和tar命令一起构成Linux操作系统中比较流行的压缩文件格式。据统计,gzip命令对文本文件有60%~70%的压缩率。减少文件大小有两个明显的好处,一是可以减少存储空间二是通过网络传输文件时,可以减少传输的时间

9.WebSocket

  • 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
  • 建立在 TCP 协议之上,服务器端的实现比较容易。
  • HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  • 数据格式比较轻量,性能开销小,通信高效。
  • 可以发送文本,也可以发送二进制数据。
  • 没有同源限制,客户端可以与任意服务器通信。
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
var ws = new WebSocket('ws://localhost:8080');//执行这个语句之后,客户端就会与服务器进行连接。

10.观察者模式和发布订阅模式的区别

  • 观察者模式中观察者和目标直接进行交,
  • 而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。

11.Https

  • HTTPS通过了HTTP来传输信息,但是信息通过TLS协议进行加密
  • TLS中使用了两种加密技术,分别为对称加密和非对称加密

12.找出文件所在的位置

find ./ -name 'vue.config.js' -print

13.让别人看到自己本地的代码

命令行输入ifconfig,找到自己电脑的ip地址 然后把项目运行的地址改为ip

14.docker文档

官网:docs.docker.com/docker-for-…

中文文档:www.docker.org.cn/book/docker…

Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、bare metal、OpenStack 集群和其他的基础应用平台。 Docker通常用于如下场景:

  • web应用的自动化打包和发布
  • 自动化测试和持续集成、发布
  • 在服务型环境中部署和调整数据库或其他的后台应用
  • 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境

Docker 部署 vue 项目

#!/bin/bash
export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"
yarn --frozen-lockfile
VERSION=$(date "+%Y%m%d-%H%M%S")
GIT_SHA_SHORT=`git rev-parse --short=8 ${GIT_COMMIT}`

docker login -u 100006645773 -p !QAZ2wsx ccr.ccs.tencentyun.com
sh ./deploy/build_docker.sh ${env} jenkins-${env}-${VERSION}-${GIT_SHA_SHORT}

15.GraphQL核心概念

segmentfault.com/a/119000001…

16.架构

  • JQuery1.8,提供了基本的操作能力,但是基本的JQuery都被Web Engin接管,所以不可以直接使用选择器(少用id和class调用元素) $(".testclass").show(),因为会产生强耦合

  • 项目开始前,首先设计整体架构,页面组件。写前端框架时,不要有太多的全局变量

  • 使用传参避免全局耦合,然后在js里面通过控制class,减少和css的耦合,和耦合相对的是内聚,出发点是重复代码,减少拷贝代码会有一个抽象和封装的过程:function -> 模块 -> 插件/框架,封装常用的还有封装成一个类,方便控制私有数据。这样可实现高内聚

17.微前端

www.thoughtworks.com/radar/techn… www.infoq.cn/article/CaX…

18.小程序生命周期

developers.weixin.qq.com/miniprogram…

19.获取用户本地文件路径

  • 由于浏览器的安全机制,当我们获取input file的路径时被fakepath代替,隐藏了真实物理路径。 当然,调整浏览器的浏览器安全设置可以解决这个问题,但是这种解决办法显然不是我们想要的,不可能让每个用于都去设置浏览器安全选项。
    • URL.createObjectURL()方法会根据传入的参数创建一个指向该参数对象的URL,这个URL的生命仅存在于它被创建的这个文档里,新的对象URL指向执行的File对象或Blob对象。
  • 语法:objcetURL = window.URL.createObjectURL(file || blob);
  • 参数:File对象和Blob对象;File对象就是一个文件,比如我用file type="file"标签来上传文件,那么里面的每个文件都是一个file对象。Blob对象就是二进制数据,比如在XMLHttpRequest里,如果指定requestType为blob,那么得到的返回值也是一个blob对象。
  • 每次调用createObjectURL的时候,一个新的URL对象就被创建了。即使你已经为同一个文件创建过一个URL.,如果你不再需要这个对象,要释放它,需要使用URL.revokeObjectURL()方法.。当页面被关闭,浏览器会自动释放它,但是为了最佳性能和内存使用,当确保不再用得到它的时候,就应该释放它。
$(document).on('change', '#PictureUrl', function () { //PictureUrl为input file 的id
        //console.log(this.files[0]);
        function getObjectURL(file) {
            var url = null;
            if (window.createObjcectURL != undefined) {
                url = window.createOjcectURL(file);
            } else if (window.URL != undefined) {
                url = window.URL.createObjectURL(file);
            } else if (window.webkitURL != undefined) {
                url = window.webkitURL.createObjectURL(file);
            }
            return url;
        }
        var objURL = getObjectURL(this.files[0]);//这里的objURL就是input file的真实路径
        $('#imgContainer').html("<img src='" + objURL + "' alt='Alternate Text' width='640px' height='350px' id='target' />");
        cutImg();//自定义的裁剪图片函数
    });

20.PWA

有一些关键的原则来辨别一个web应用是否是一个PWA应用。它应该具有以下特点:

  • Discoverable, 内容可以通过搜索引擎发现。
  • Installable, 可以出现在设备的主屏幕。
  • Linkable, 你可以简单地通过一个URL来分享它。
  • Network independent, 它可以在离线状态或者是在网速很差的情况下运行。
  • Progressive, 它在老版本的浏览器仍旧可以使用,在新版本的浏览器上可以使用全部功能。
  • Re-engageable, 无论何时有新的内容它都可以发送通知。
  • Responsive, 它在任何具有屏幕和浏览器的设备上可以正常使用
  • Safe, 在你和应用之间的连接是安全的,可以阻止第三方访问你的敏感数据。

21.原生FetchAPI

Fetch API是新的ajax解决方案,用于解决古老的XHR对象不能实现的问题。

  • 基于标准的Promises实现,支持async/await
  • fetch()本身就返回一个Promise对象。所以,它就可以用Promises的所有方法。

fetch的参数:

  • 第一个参数:要获取资源的URL
  • 第二个参数:options对象,包括:
    • method(String): HTTP请求方法,默认为GET

    • body(String): HTTP的请求参数,注意 GET 或 HEAD 方法的请求不能包含 body 信息

    • headers(Object): HTTP的请求头,默认为{}

    • credentials(String): 默认为omit,忽略的意思,也就是不带cookie;还有两个参数,same-origin,意思就是同源请求带cookieinclude,表示无论跨域还是同源请求都会带cookie

    • mode: 请求的模式,如 corsno-cors 或者 same-origin

用法:

fetch(url, options).then(function(response) { 
// handle HTTP response
}, function(error) {
 // handle network error
})

response方法

  • Response.clone():创建一个Response对象的克隆
  • Response.error():返回一个绑定了网络错误的新的Response对象
  • Response.redirect():用另一个URL创建一个新的 response
  • Response.arrayBuffer():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为ArrayBuffer格式的promise对象
  • Response.blob():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为Blob格式的promise对象
  • Response.formData():读取Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为FormData格式的promise对象
  • Response.json():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为JSON格式的promise对象
  • Response.text():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为USVString格式的promise对象

引申:fetch需要注意的问题

  1. cookie传递
  • 必须在header参数里面加上credientials: ‘include’,才会如xhr一样将当前cookies带到请求中去
  1. fetchxhr的不同
  • fetch虽然底层,但是还是缺少一些常用xhr有的方法,比如能够取消请求(abort)方法
  • fetch在服务器返回4xx、5xx时是不会抛出错误的,这里需要手动通过,通过response中的ok字段和status字段来判断

22. axios怎么实现的,与ajax及fetch的区别?

  • axios一个基于Promise 用于浏览器和 nodejsHTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征:
    1. 从浏览器中创建 XMLHttpRequest
    2. 支持 Promise API
    3. 客户端支持防止CSRF
    4. 提供了一些并发请求的接口(重要,方便了很多的操作)
    5. node.js 创建 http 请求
    6. 拦截请求和响应
    7. 转换请求和响应数据
    8. 取消请求
    9. 自动转换JSON数据
  • Fetch基于promise设计的fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象
  • JQuery ajax 是对原生XHR的封装,除此以外还增添了对JSONP的支持。首先实例一个XMLHttpRequest对象,用其中的open方法建立连接;send方法传输数据(前端传到后台);然后再利用onreadystatechange 监听readyState的变化,当其为4时,代表请求完成。经过多年的更新维护,真的已经是非常的方便了,优点无需多言;如果是硬要举出几个缺点,那可能只有:
  1. 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
  2. 基于原生的XHR开发,XHR本身的架构不清晰。
  3. JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
  4. 不符合关注分离(Separation of Concerns)的原则
  5. 配置和调用方式非常混乱,而且基于事件的异步模型不友好。

总结:axios既提供了并发的封装,也没有fetch的各种问题,而且体积也较小,当之无愧现在最应该选用的请求的方式。

参考:ajax和axios、fetch的区别

学习:axios原理及面试题

23.Lodash

Lodash是一个著名的javascript原生库,不需要引入其他第三方依赖。是一个意在提高开发者效率,提高JS原生方法性能的JS库。简单的说就是,很多方法lodash已经帮你写好了,直接调用就行,不用自己费尽心思去写了,而且可以统一方法的一致性。Lodash使用了一个简单的 _ 符号,就像Jquery的 $ 一样,十分简洁。 类似的还有Underscore.jsLazy.js

24.怎么介绍自己做过的项目

可参考链接:如何向面试官介绍你的项目

25.CDN

内容分发网络,CDN解决的是如何将数据快速可靠从源站传递到用户的问题。用户获取数据时,不需要直接从源站获取,通过CDN对于数据的分发,用户可以从一个较优的服务器获取数据,从而达到快速访问,并减少源站负载压力的目的。

  1. 用户在浏览器中输入要访问的域名
  2. 浏览器向DNS服务器请求对域名进行解析。由于CDN对域名解析进行了调整,DNS服务器会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。
  3. CDN的**DNS服务器将CDN的负载均衡设备IP地址返回给用户**。
  4. 用户向CDN的负载均衡设备发起内容URL访问请求
  5. CDN负载均衡设备会为用户选择一台合适的缓存服务器提供服务。 选择的依据包括:根据用户IP地址,判断哪一台服务器距离用户最近;根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器的负载情况,判断哪一台服务器的负载较小。 基于以上这些依据的综合分析之后,负载均衡设置会把缓存服务器的IP地址返回给用户。
  6. 用户向缓存服务器发出请求。
  7. 缓存服务器响应用户请求,将用户所需内容传送到用户。 如果这台缓存服务器上并没有用户想要的内容,而负载均衡设备依然将它分配给了用户,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉取到本地

使用CDN服务的网站,只需将其域名的解析权交给CDN的负载均衡设备,CDN负载均衡设备将为用户选择一台合适的缓存服务器,用户通过访问这台缓存服务器来获取自己所需的数据

26.热更新流程

  1. Webpack编译期,为需要热更新的 entry 注入热更新代码(EventSource通信)
  2. 页面首次打开后,服务端与客户端通过 EventSource 建立通信渠道,把下一次的 hash 返回前端
  3. 客户端获取到hash,这个hash将作为下一次请求服务端 hot-update.jshot-update.jsonhash
  4. 修改页面代码后,Webpack 监听到文件修改后,开始编译,编译完成后,发送 build 消息给客户端
  5. 客户端获取到hash,成功后客户端构造hot-update.js script链接,然后插入主文档
  6. hot-update.js 插入成功后,执行hotAPIcreateRecordreload方法,获取到 Vue 组件的 render方法,重新 render 组件, 继而实现 UI 无刷新更新。

参考链接:Webpack 热更新实现原理分析

轻松理解webpack热更新原理

27. 进程与线程

  1. 进程: 程序的一次执行,他占有一片独有的内存空间,是操作系统的基本单位。
  • 一个进程中至少有一个运行的线程:主线程。进程启动后自动创建
  • 一个进程中也可以同时运行多个线程,程序是多线程运行的。
  • 一个进程内的数据可以提供其中的多个线程共享,多个进程之间的数据是不能直接共享的。
  1. 线程: 是建成内一个独立执行的单位,是CPU调度的最小单元,程序运行的基本单位。
  • 线程池:保存多个线程对象的容器,实现线程对象的反复利用
  • 多线程的目的主要是保持用户界面的高度响应。

例如:为了不让Browser进程的UI线程被其他耗时的操作(本地文件的读写)所阻塞,那么我们就把这些操作放到分线程中去处理。在render进程中,为了不让其他操作阻止渲染线程的高速执行,我们通常会将渲染过程管道化,利用计算机的多核优势,让渲染的不同阶段在不同线程中执行

  1. 现代浏览器:多进程、多线程模型
  • 不堪回首的过去: 当你通过浏览器打开多个页面的时候,如果其中一个页面不响应了或者崩溃了,那么随之而来的将会是更不幸的事情,你打开的所有页面都而不到响应,最让人不能忍受的是,其中一些页面还包含了一些未发送、未保存的信息
  • 浏览器厂商如何解决: 采用多进程模型,该模型可以带来的好处是:当第三方插件崩溃时,也不会影响到整个浏览器的稳定性,安全。
  1. 浏览器有哪些进程
  • Browser进程: 浏览器的主进程,负责浏览器页面的显示,和各个页面的管理。浏览器中所有其他类型进程的祖先,负责其他进程的创建和销毁,有且仅只有一个。
  • Render进程: 网页渲染进程,负责页面的渲染,可以有多个,渲染进程的数量不一定等于你打开的网页的个数
  • 各种插件进程 GPU:移动设备的浏览器可能不太一样。安卓不支持插件所以没有插件进程,GPU演化成Browser进程的一个线程