Web 前端 中高难度问题(但愿看完以后的你能够拿到Offer^v^)

1. 解释 event loop

  • Javascript是单线程的,全部的同步任务都会在主线程中执行。
  • 主线程以外,还有一个任务队列。每当一个异步任务有结果了,就往任务队列里塞一个事件。
  • 当主线程中的任务,都执行完以后,系统会 “依次” 读取任务队列里的事件。与之相对应的异步任务进入主线程,开始执行。
  • 异步任务之间,会存在差别,因此它们执行的优先级也会有区别。大体分为 微任务(micro task,如:Promise、MutaionObserver等)和宏任务(macro task,如:setTimeout、setInterval、I/O等)。同一次事件循环中,微任务永远在宏任务以前执行。
  • 主线程会不断重复上面的步骤,直到执行完全部任务。

注意

  1. Promise 内部的语句是当即执行的,以上所说的微任务 Promise 指的是 Promise.then
    • macro-task(宏任务):包括总体代码script,setTimeout,setInterval
    • micro-task(微任务):Promise,process.nextTick(nodejs)

2. 写出输出结果

function p(){ return new Promise(resolve => { console.log('resolve') resolve() }) } p().then(() => { console.log('hello') }) console.log('hi') // 'resolve' 'hi' 'hello'

3. 写出输出结果

let a = 0 let b = async () => { a = a + await 10 console.log('2', a) } b() a++ console.log('1', a) // -> '1' 1 // -> '2' 10

4. setTimeinterval 和 setTimeout 准确吗,缘由?

因为 javascript 的 event loop 机制,setTimeinterval 和 setTimeout 须要在主线程任务和微任务结束后执行,这就意味着若是主线程的处理时间超出了设置的时间时这两种方法确定是不许确的javascript

5. setTimeout、setInterval、requestAnimationFrame 各有什么特色?

setTimeoutsetInterval在 event loop 的宏任务中,当主线程结束时才会按照任务队列加载css

requestAnimationFrame 在主线程中执行,因此更加准确,以1秒钟60次(大约每16.7毫秒一次)的频率执行html

6. setTimeout、setInterval、requestAnimationFrame 各有什么特色?

function setInterval(callback, interval) { let timer const now = Date.now let startTime = now() let endTime = startTime const loop = () => { timer = window.requestAnimationFrame(loop) endTime = now() if (endTime - startTime >= interval) { startTime = endTime = now() callback(timer) } } timer = window.requestAnimationFrame(loop) return timer } let a = 0 setInterval(timer => { console.log(1) a++ if (a === 3) cancelAnimationFrame(timer) }, 1000)

7. 函数节流与防抖

函数 & 防抖前端

8. 解释原型链

在 javascript 中,一切皆对象,而每一个对象都会有一个 __proto__ 属性, __proto__ 指向实例化该对象的构造函数的 prototype,而该构造函数的 __proto__ 又指向它的构造函数的 __proto__ 如此往复向下,直到底层为 null 时中止,当调用一个对象的方法时,javascript 会顺着这条线寻找该方法。vue

9. 继承实现的方式

prototype,classjava

10. 为何要使用模块化?都有哪几种方式能够实现模块化,各有什么特色?

  • 解决命名冲突
  • 提供复用性
  • 提升代码可维护性

特色node

11. == 和 === 区别

使用 == 时,若是两边值的类型不一样会触发类型转换,因此会出现 Boolean('1' == 1) === true ,使用 === 时则不会webpack

12. 什么是闭包,做用?

what

函数 A 内部有一个函数 B,函数 B 能够访问到函数 A 中的变量,那么函数 B 就是闭包git

13. 写出输出结果

for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) } // 6个6

14. 浅拷贝?深拷贝?分别如何实现?

深拷贝 & 浅拷贝github

15. javascript 中有哪些数据类型

  • 原始数据类型: number, boolean, string, null, undefinded, symbol
  • 引用数据类型: object

16. 箭头函数中的 this 指向

17. call,apply,bind 用法(如何改变 this 的指向)

18. 实现 call,apply,bind

Function.prototype.myCall = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window context.fn = this const args = [...arguments].slice(1) const result = context.fn(...args) delete context.fn return result } Function.prototype.myApply = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window context.fn = this let result if (arguments[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn return result } Function.prototype.myBind = function (context) { if (typeof this !== 'function') { throw new TypeError('Error') } const _this = this const args = [...arguments].slice(1) // 返回一个函数 return function F() { if (this instanceof F) { return new _this(...args, ...arguments) } return _this.apply(context, args.concat(...arguments)) } }

20. 使用 new 关键字后发生了什么

  1. 新生成了一个对象
  2. 连接到原型
  3. 绑定 this
  4. 返回新对象

实现

function create() { let obj = {} let Con = [].shift.call(arguments) obj.__proto__ = Con.prototype let result = Con.apply(obj, arguments) return result instanceof Object ? result : obj }

21. intanceof 原理?

instanceof 能够正确的判断对象的类型,由于内部机制是经过判断对象的原型链中是否是能找到类型的 prototype

function myInstanceof(left, right) { let prototype = right.prototype left = left.__proto__ while (true) { if (left === null || left === undefined) return false if (prototype === left) return true left = left.__proto__ } }

22. 垃圾回收机制?

23. 解释冒泡,捕获事件

冒泡:由小及大,从子元素事件发出,向父元素,父元素的父元素...直至 html 为止

捕获:由大及小,从父元素发出,向其下的子元素...直至最小的元素为止

使用 element.addEventListener(type,listener,options) ,在 options.capture 设置使用冒泡仍是捕获,默认冒泡

24. 解释事件代理

通常使用在有大量或者是动态渲染的html元素须要绑定事件时,以达到提升性能或动态绑定的目的。

将事件绑定在 html 元素的父元素上,经过事件流的冒泡属性,在父元素中获取到点击的子元素,加以判断后实行相应的事件。

25. 什么是跨域?为何浏览器要使用同源策略?你有几种方式能够解决跨域问题?了解预检请求嘛?

what

当协议、域名或者端口有一个不一样便是跨域,浏览器会拦截 ajax 请求,目的是为了防止 CSRF 攻击。简单点说,CSRF 攻击是利用用户的登陆态发起恶意请求。

浏览器拦截的是读取内容的请求,因此经过表单等方式的请求是不会被拦截的

仅在同域名和同域名不一样文件夹下两种状况时不存在跨域,其他皆为跨域

解决

  1. JSONP
  • JSONP 的原理很简单,就是利用 <script> 标签没有跨域限制的漏洞。经过 <script> 标签指向一个须要访问的地址并提供一个回调函数来接收数据当须要通信时。
function jsonp(url, jsonpCallback, success) { let script = document.createElement('script') script.src = url script.async = true script.type = 'text/javascript' window[jsonpCallback] = function(data) { success && success(data) } document.body.appendChild(script) } jsonp('http://xxx', 'callback', function(value) { console.log(value) })
  1. CORS
  • 须要浏览器和后端同时支持。IE 8 和 9 须要经过 XDomainRequest 来实现。
  1. document.domain
  • 该方式只能用于二级域名相同的状况下,好比 a.test.com 和 b.test.com 适用于该方式。

  • 只须要给页面添加 document.domain = 'test.com' 表示二级域名都相同就能够实现跨域

  1. MessageChannel
  • MessageChannel

  • 主要用于页面和其下的 iframe 之间的通信

26. 什么状况会形成阻塞渲染

  1. 在 HTML 和 CSS 生成渲染树的过程当中确定会形成阻塞渲染
  • 解决方案:文件大小,而且扁平层级,优化选择器
  1. 在浏览器解析到 script 标签时,会加载并执行 script 的内容,直到结束后才会继续渲染,也会形成渲染阻塞
  • 解决方案:将 script 放在 body 底部,或者设置 async 属性为 defer

27. 重绘(Repaint)和回流(Reflow)

重绘仅改变节点的外观,不影响布局,如改变节点的 color 属性

回流指节点的大小或页面的布局发生改变

回流一定会发生重绘,重绘不必定会引起回流

如何减小

  1. 使用 transform 替代 top
  2. 使用 visibility 替换 display: none ,由于前者只会引发重绘,后者会引起回流(改变了布局)
  3. 不要把节点的属性值放在一个循环里当成循环里的变量
  4. 不要使用 table 布局,可能很小的一个小改动会形成整个 table 的从新布局

28. 从用户输入URL到浏览器呈现页面通过了哪些过程

参考

DNS 解析

  1. 浏览器根据地址去自己缓存中查找dns解析记录,若是有,则直接返回IP地址,不然浏览器会查找操做系统中(hosts文件)是否有该域名的dns解析记录,若是有则返回。
  2. 若是浏览器缓存和操做系统hosts中均无该域名的dns解析记录,或者已通过期,此时就会向域名服务器发起请求来解析这个域名。
  3. 请求会先到LDNS(本地域名服务器),让它来尝试解析这个域名,若是LDNS也解析不了,则直接到根域名解析器请求解析
  4. 根域名服务器给LDNS返回一个所查询余的主域名服务器(gTLDServer)地址。
  5. 此时LDNS再向上一步返回的gTLD服务器发起解析请求。
  6. gTLD服务器接收到解析请求后查找并返回此域名对应的Name Server域名服务器的地址,这个Name Server一般就是你注册的域名服务器(好比阿里dns、腾讯dns等)
  7. Name Server域名服务器会查询存储的域名和IP的映射关系表,正常状况下都根据域名获得目标IP记录,连同一个TTL值返回给DNS Server域名服务器
  8. 返回该域名对应的IP和TTL值,Local DNS Server会缓存这个域名和IP的对应关系,缓存的时间有TTL值控制。
  9. 把解析的结果返回给用户,用户根据TTL值缓存在本地系统缓存中,域名解析过程结束。

HTTP请求发起和响应

  1. 用户输入URL,浏览器获取到URL
  2. 浏览器(应用层)进行DNS解析(若是输入的是IP地址,此步骤省略)
  3. 根据解析出的IP地址+端口,浏览器(应用层)发起HTTP请求,请求中携带(请求头header(也可细分为请求行和请求头)、请求体body),

header包含:

请求的方法(get、post、put..) 协议(http、https、ftp、sftp…) 目标url(具体的请求路径已经文件名) 一些必要信息(缓存、cookie之类)

body包含:

请求的内容

  1. 请求到达传输层,tcp协议为传输报文提供可靠的字节流传输服务,它经过三次握手等手段来保证传输过程当中的安全可靠。经过对大块数据的分割成一个个报文段的方式提供给大量数据的便携传输。
  2. 到网络层, 网络层经过ARP寻址获得接收方的Mac地址,IP协议把在传输层被分割成一个个数据包传送接收方。
  3. 数据到达数据链路层,请求阶段完成
  4. 接收方在数据链路层收到数据包以后,层层传递到应用层,接收方应用程序就得到到请求报文。
  5. 接收方收到发送方的HTTP请求以后,进行请求文件资源(如HTML页面)的寻找并响应报文
  6. 发送方收到响应报文后,若是报文中的状态码表示请求成功,则接受返回的资源(如HTML文件),进行页面渲染。

网页渲染

  1. 浏览器经过HTMLParser根据深度遍历的原则把HTML解析成DOM Tree。
  2. 将CSS解析成CSS Rule Tree(CSSOM Tree)。
  3. 根据DOM树和CSSOM树来构造render Tree。
  4. layout:根据获得的render tree来计算全部节点在屏幕的位置。
  5. paint:遍历render树,并调用硬件图形API来绘制每一个节点。
  6. 当遇到 script 标签时会等待其中 js 代码执行完成后继续执行上述步骤(会形成阻塞)

29. 前端性能优化

CSS

  1. 优化选择器路径:健全的css选择器当然是能让开发看起来更清晰,而后对于css的解析来讲倒是个很大的性能问题,所以相比于 .a .b .c{} ,更倾向于你们写.c{}。
  2. 压缩文件:尽量的压缩你的css文件大小,减小资源下载的负担。
  3. 选择器合并:把有共同的属性内容的一系列选择器组合到一块儿,能压缩空间和资源开销
  4. 精准样式:尽量减小没必要要的属性设置,好比你只要设置{padding-left:10px}的值,那就避免{padding:0 0 0 10px}这样的写法
  5. 雪碧图:在合理的地方把一些小的图标合并到一张图中,这样全部的图片只须要一次请求,而后经过定位的方式获取相应的图标,这样能避免一个图标一次请求的资源浪费。
  6. 避免通配符:.a .b {} 像这样的选择器,根据从右到左的解析顺序在解析过程当中遇到通配符()回去遍历整个dom的,这样性能问题就大大的了。
  7. 少用Float:Float在渲染时计算量比较大,尽可能减小使用。
  8. 0值去单位:对于为0的值,尽可能不要加单位,增长兼容性

HTML

  1. 避免再HTML中直接写css代码。
  2. 使用Viewport加速页面的渲染。
  3. 使用语义化标签,减小css的代码,增长可读性和SEO。
  4. 减小标签的使用,dom解析是一个大量遍历的过程,减小无必要的标签,能下降遍历的次数。
  5. 避免src、href等的值为空。
  6. 减小dns查询的次数。

JS

  1. 尽量把script标签放到body以后,避免页面须要等待js执行完成以后dom才能继续执行,最大程度保证页面尽快的展现出来。
  2. 尽量合并script代码,
  3. css能干的事情,尽可能不要用JavaScript来干。毕竟JavaScript的解析执行过于直接和粗暴,而css效率更高。
  4. 尽量压缩的js文件,减小资源下载的负担
  5. 尽量避免在js中逐条操做dom样式,尽量预约义好css样式,而后经过改变样式名来修改dom样式,这样集中式的操做能减小reflow或repaint的次数。
  6. 尽量少的在js中建立dom,而是预先埋到HTML中用display:none来隐藏,在js中按需调用,减小js对dom的暴力操做。

30. 强制缓存,跳过垃圾回收机制

31. Vue 实例中的 data 为何使用函数

32. 实现 v-modal

33. --

34. 公司技术(组件)沉淀举例

35. get 和 post 区别(从报文角度)

36. ES5 写原型拓展(实现 extends)

// ES5 function Animal() { this.type = 'animal' this.eat = function(){} } function Cat() { Animal.call(this) this.name = 'cat' } function inherits(Child, Parent) { var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; } // ES6 class Fruit{ constructor(){} } class Apple extends Fruit{ constructor(){ super() } }

36. 虚拟 dom 相比 原生 dom 好处

先明确:虚拟 dom (框架封装的)不必定比 原生 dom 快 参考

好处:

简化dom操做,让数据与dom之间的关系更直观更简单

37. webpack 中 plugin 和 loader 有什么区别

loader

用于加载某些资源文件。 由于webpack 自己只能打包commonjs规范的js文件,对于其余资源例如 css,图片,或者其余的语法集,好比 jsx, coffee,是没有办法加载的。 这就须要对应的loader将资源转化,加载进来。从字面意思也能看出,loader是用于加载的,它做用于一个个文件上。

plugin

用于扩展webpack的功能。它直接做用于 webpack,扩展了它的功能。固然loader也时变相的扩展了 webpack ,可是它只专一于转化文件(transform)这一个领域。而plugin的功能更加的丰富,而不只局限于资源的加载。

38. 浏览器缓存策略

一般浏览器缓存策略分为两种:强缓存和协商缓存,而且缓存策略都是经过设置 HTTP Header 来实现的。

强缓存

强缓存能够经过设置两种 HTTP Header 实现:Expires 和 Cache-Control 。强缓存表示在缓存期间不须要请求,state code 为 200。

协商缓存

若是缓存过时了,就须要发起请求验证资源是否有更新。协商缓存能够经过设置两种 HTTP Header 实现:Last-Modified 和 ETag 。

39. 手写原生 ajax

相关文章
相关标签/搜索