2019年,盘点一些我出过的前端面试题以及对求职者的建议

10,436 阅读28分钟

笔者虽然曾经也面试过很多求职者,但是对于前端的笔试和面试,我觉得并不能体现一个人的真实能力,所以建议大家多修炼前端真正的技术.对于前端面试题,之前也承诺过读者要出一篇,笔者大致总结一下曾今面试的题目.后续不会再出面试题,而是聚焦于一些真正的,有利于成长性的技术文章和思维方式,来帮助大家提高解决问题的能力.

前言

首先总结一下笔者对于前端领域学习的一些成长模型和学习路线,来给大家提供一些参考.

下面推荐一些不同技术选型的优秀组件和库,也是笔者曾今使用过的.

如果公司的前端是以vue为主的,那么你可能需要熟悉如下技能和知识点

  • vue
  • vue-router 路由管理
  • vuex 状态管理
  • vue-cli3 vue项目优秀的脚手架,可以配置单页,多页,ssr, 预渲染技术的页面
  • vue-server-renderer 服务端渲染方案
  • vue-devtools vue开发调试工具,方便调试大型复杂项目
  • antd-desigin-vue / element UI 基于vue的第三方ui组件库
  • vxe-table 优秀的vue表格解决方案
  • vue-multiselect vue强大的多选下拉组件
  • Vuelidate 基于vue强大且轻量的校验库
  • vuex-router-sync 保持vue-router和vuex store同步

如果公司的前端是以React为主的,那么你可能需要熟悉如下技能和知识点

  • react
  • react-router
  • react-thunk 异步redux解决方案
  • ant design 优秀且强大的react组件库
  • react-json-view JSON 显示器
  • react-quill 富文本编辑器
  • rc-color-picker 拾色器
  • qrcode.react 二维码生成器
  • antV 数据可视化解决方案
  • react-beautiful-dnd 基于react的拖拽库
  • emoji-mart 表情库
  • react-image-crop 图片裁切
  • Ant Motion 动画
  • react-codemirror2 代码编辑器
  • react-copy-to-clipboard 复制到剪切板

接下来言归正传,开始进入正文.

正文

1. 介绍一下ES6的新特性

[参考答案]

  • const和let
  • 模板字符串
  • 箭头函数
  • 函数的参数默认值
  • Spread / Rest 操作符
  • 二进制和八进制字面量(通过在数字前面添加0o或0O即可将其转为八进制值,二进制使用0b或者0B)
  • 对象和数组解构
  • ES6中的类(class)
  • Promise
  • Set()和Map()数据结构
  • Modules(模块, 如import, export)
  • for..of 循环

2. 介绍Promise以及Promise的几种状态

[参考答案]

介绍: Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。 状态: pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

3. 谈谈你对闭包的理解及其优缺点

[参考答案]

闭包就是能够读取其他函数内部变量的函数. 本质上,闭包是将函数内部和函数外部连接起来的桥梁.

优点

  • 逻辑连续,当闭包作为另一个函数调用参数时,避免脱离当前逻辑而单独编写额外逻辑。
  • 延长局部变量的生命周期, 更具有封装性, 保护局部变量。

缺点

  • 容易造成内存溢出
  • 闭包会在父函数外部,改变父函数内部变量的值,所以可能会导致改变父函数的变量

4. React的生命周期

[参考答案]

  • 初始化阶段 defaultProps -> constructor -> componentWillMount() -> render() -> componentDidMount()
  • 运行中阶段 componentWillReceiveProps() -> shouldComponentUpdate() -> componentWillUpdate() -> componentDidUpdate()
  • 销毁阶段 componentWillUnmount()

5. componentWillReceiveProps的触发条件是什么

[参考答案] componentWillReceiveProps会在接收到新的props的时候调用

6. vue中v-if和v-show的区别

[参考答案]

  • v-show不管条件是真还是假,第一次渲染的时候标签都会添加到DOM中。切换的时候,通过display: none;样式来显示隐藏元素,对性能影响不是很大。
  • v-if在首次渲染的时候,如果条件为假,不会在页面渲染该元素。当条件为真时,开始局部编译,动态的向DOM元素里面添加元素。当条件从真变为假的时候,开始局部编译,卸载这些元素,也就是删除。对性能有一定影响

7. 小程序里面开页面最多多少

[参考答案] 小程序原生页面存在层级限制,超过一定层数就会无法打开新页面。一开始这个限制为不超过5层,目前是不超过10层。

8. 取数组的最大值(ES5、ES6)

[参考答案]

// es5
Math.max.apply(null, arr);
// es6
Math.max(...arr);

9. http并发请求资源数上限

[参考答案] HTTP客户端一般对同一个服务器的并发连接个数都是有限制的, 最大为6条

10. 如何优化网站的SEO

[参考答案]

  1. 网站结构布局优化:尽量简单, 提倡扁平化结构. 一般而言,建立的网站结构层次越少,越容易被“蜘蛛”抓取,也就容易被收录。
  2. img标签必须添加“alt”和“title”属性,告诉搜索引擎导航的定位,做到即使图片未能正常显示时,用户也能看到提示文字。
  3. 把重要内容HTML代码放在最前搜索引擎抓取HTML内容是从上到下,利用这一特点,可以让主要代码优先读取,广告等不重要代码放在下边。
  4. 控制页面的大小,减少http请求,提高网站的加载速度。
  5. 合理的设计title、description和keywords
  • title标题:只强调重点即可,尽量把重要的关键词放在前面,关键词不要重复出现,尽量做到每个页面的title标题中不要设置相同的内容。
  • meta keywords页面/网站的关键字。
  • meta description网页描述,需要高度概括网页内容,切记不能太长,过分堆砌关键词,每个页面也要有所不同。
  1. 语义化书写HTML代码,符合W3C标准尽量让代码语义化,在适当的位置使用适当的标签,用正确的标签做正确的事。让阅读源码者和“蜘蛛”都一目了然。
  2. a标签:页面链接,要加 “title” 属性说明,链接到其他网站则需要加上 el="nofollow" 属性, 告诉 “蜘蛛” 不要爬,因为一旦“蜘蛛”爬了外部链接之后,就不会再回来了。
  3. 图标使用IconFont替换
  4. 使用CDN网络缓存,加快用户访问速度,减轻服务器压力
  5. 启用GZIP压缩,浏览速度变快,搜索引擎的蜘蛛抓取信息量也会增大
  6. SSR技术
  7. 预渲染技术

11. 介绍下HTTP状态码, 403、301、302分别代表什么

[参考答案]

当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。

  • 301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置
  • 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求
  • 403 (禁止) 服务器拒绝请求

12. RESTful常用的方法和介绍

[参考答案] rest请求方法有4种,包括get,post,put,delete.分别对应获取资源,添加资源,更新资源及删除资源.

12. React16.3对生命周期的改变

[参考答案] React16.3+废弃的三个生命周期函数

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

取而代之的是两个新的生命周期函数

  • static getDerivedStateFromProps(nextProps, prevState) 取代componentWillMount、componentWillReceiveProps和componentWillUpdate
  • getSnapshotBeforeUpdate(prevProps, prevState) 取代componentWillUpdate
class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 返回的数据将作为componentDidUpdate第三个参数
    return {
      stateA: 'xxx'
    };
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot !== null) {
      console.log(snapshot.stateA)
    }
  }

  render() {}
}

13. 介绍React高阶组件

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数.其本身是纯函数,没有副作用。

function logProps(WrappedComponent) {
  return class extends React.Component {
    componentWillReceiveProps(nextProps) {
      console.log('Current props: ', this.props);
      console.log('Next props: ', nextProps);
    }
    render() {
      // 将 input 组件包装在容器中,而不对其进行修改。Good!
      return <WrappedComponent {...this.props} />;
    }
  }
}

14. 缓存相关的HTTP请求头

[参考答案]

headers字段 解释 案例
Expires 服务端告诉浏览器,先把这个文件缓存起来,在这个过期时间之前,该文件都不会变化 Expires: Mon, 1 Aug 2016 22:43:02 GMT
Cache-Control 由于Expires给定的是绝对时间,而客户端的系统时间可以由用户任意修改, Cache-Control为相对时间 Cache-Control: max-age=80
Last-Modified(response header) / If-Modified-Since (request header) 服务端收到请求后会对比目前文件的最后修改时间和该请求头的信息,如果没有修改,那就直接返回304给浏览器,而不返回实际资源。如果有变化了,就返回200,并且带上新的资源内容 If-Modified-Since:Mon, 01 Aug 2016 13:48:44 GMT
Etag / If-None-Match 第一次请求后响应头中包含了Etag,作为时间标签,下一次请求时会把原来的Etag标签带上(在请求头中变成了If-None-Match)作为校验标准,若这个文件如果发生了改变,则Etag也会改变。服务器对比浏览器请求头中的的If-None-Match:如果相同就返回304,而不返回实际资源如果不同,就返回200和新的资源。

15. 如何优化用户体验

[参考答案]

  • 页面渲染前使用骨架屏或者加载动画,避免大块白屏
  • 使用预渲染或者ssr技术提高首屏加载时间
  • 动画使用css3硬件加速,避免用户操作动画卡顿
  • 计算密集型业务使用web worker或者js分片处理,避免js线程阻塞
  • 页面状态监控,给用户提供反馈机制
  • 静态资源走CDN缓存或者oss服务,提高用户访问速度
  • 避免用户操作报错,提供404页面或则错误提示页面

16. 对PWA的了解

[参考答案]

progressive web app: 渐进式网页应用.可以将 Web 和 App 各自的优势融合在一起:渐进式、可响应、可离线、实现类似 App 的交互、即时更新、安全、可以被搜索引擎检索、可推送、可安装、可链接。其核心技术包括 App Manifest、Service Worker、Web Push,等等。

17. 介绍下跨域

[参考答案]

同源策略/SOP(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击, 同源策略要求两个通讯地址的协议、域名、端口号必须相同,否则两个地址的通讯将被浏览器视为不安全的,并被阻挡下来. 要突破SOP的限制,我们可以使用如下方式:

  • CORS 同域安全策略CORS是一种跨域资源请求机制,它要求当前域在响应报头添加Access-Control-Allow-Origin标签,从而允许指定域的站点访问当前域上的资源
res.setHeader("Access-Control-Allow-Origin","*"); 
// 不过CORS默认只支持GET/POST这两种http请求类型,如果要开启PUT/DELETE之类的方式,需要在服务端在添加一个"Access-Control-Allow-Methods"报头标签:
 res.setHeader(
    "Access-Control-Allow-Methods",
    "PUT, GET, POST, DELETE, HEAD, PATCH"
  )
  • HTML5 postMessage 可以使用 postMessage 方法和 onmessage 事件来实现不同域之间的通信,其中postMessage用于实时向接收信息的页面发送消息

  • HTML5 WebSocket WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很棒的实现

  • JSONP 是JSON的一种“使用模式”,主要是利用script标签不受同源策略限制的特性,向跨域的服务器请求并返回一段JSON数据

  • iframe形式 通过设置相同的document.domain, 或者不同域通过window.name传递数据

  • 服务器代理

18. Access-Control-Allow-Origin在服务端哪里配置

[参考答案] response header响应头

19. csrf跨站攻击怎么解决

[参考答案]

CSRF, 跨站请求伪造,它可以在用户毫不知情的情况下以用户名义伪造请求发送给受攻击站点,从而对用户或者网站造成攻击. 预防措施如下:

  • 服务器端验证HTTP Referer字段, Referer记录了该HTTP请求的来源地址
  • 在请求地址中添加token并验证
  • 在HTTP头中自定义属性并验证

20. 用js写一个数组扁平化函数

[参考答案]

// reduce
function flatten(arr = []) {  
    return arr.reduce((result, item) => {
        return result.concat(Array.isArray(item) ? flatten(item) : item)
    }, [])
}

// (toString | join) & split(利用数组的toString或者join,将数组转化为字符串)
function flatten(arr = []) {
  return arr.toString().split(',').map(item => Number(item))
}

21. Promise和Callback有什么区别

[参考答案]

相比于callback,Promise 具有更易读的代码组织形式(链式结构调用),更好的异常处理方式(在调用 Promise 的末尾添加上一个catch方法捕获异常即可),以及异步操作并行处理的能力(Promise.all()Promise.race()等)。callback最大的问题就是我们通常说的回调地狱,一旦业务逻辑复杂了,我们不得不使用大量的嵌套回调代码,可维护性很低.

22. 如何实现高度自适应

[参考答案]

  • 使用绝对定位, 设置top,bottom属性
  • 使用flex布局
  • float+ height:100%

23. cookie, session, storage的区别和联系

[参考答案]

  • cookie存储于浏览器端,而session存储于服务端
  • cookie的安全性相比于session较弱,cookie容易被第三方劫持,考虑到安全应当使用session
  • session保存在服务器上,当访问增多时,会占用服务器的资源
  • cookie存储容量有限制,单个cookie保存数据不能超过4k,且很多浏览器限制一个站点最多保存20个cookie。对于session,默认大小一般是1M
  • cookie、sessionStorage、localStorage,都保存在浏览器端,且受同源策略影响
  • cookie数据始终在同源的http请求中携带,而Storage不会再请求中携带,仅在本地存储
  • 存储大小上, cookie一般是4k,Storage可以达到5M-10M
  • 数据存储时间上:sessionStorage仅仅是会话级别的存储,它只在当前浏览器关闭前有效,不能持久保持;localStorage始终有效,即使窗口或浏览器关闭也一直有效,除非用户手动删除;cookie只在设定的 过期时间之前有效
  • 作用域上:sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面; localStorage和 cookie在所有同源窗口是共享的
  • Storage支持事件通知机制,可以将数据更新的通知发送给监听者。并且它提供增删查api使用更方便

24. 说说你做过的前端性能优化(JS原生,React, vue)

[参考答案] 参考<趣谈前端>周二打卡答案

25. 对web安全的理解

[参考答案]

  • CSRF 攻击和防范

跨站请求伪造,可以理解为攻击者盗用了用户的身份,以用户的名义发送恶意请求,造成用户隐私及财产损失 过程: 1.登录受信任网站并在本地生成cookie; 2.在不登出 网站 的情况下访问危险网站 防范: 关键操作只接受POST请求, 使用验证码, 检测Referer, 使用token(或者JWT)

  • XSS 攻击和防范

全称Cross-site script,跨站脚本攻击,是Web程序中常见的漏洞。原理是攻击者向有XSS漏洞的网站中输入恶意的脚本,当其它用户浏览该网站时候,该脚本会自动执行,从而达到攻击的目的(盗取Cookie,破坏页面结构,重定向到钓鱼网站等)。 区分: 分为持久型XSS和非持久性XSS. 持久型XSS是将攻击的脚本植入到服务器,从而导致每个访问的用户都会遭到此XSS脚本的攻击。非持久型XSS是将恶意脚本包装在页面的URL参数中,通过URL链接骗取用户访问,从而进行攻击. 防范: 对用户输入进行HTML转义, 对敏感信息进行过滤

  • SQL 注入与防范

通过把SQL命令插入到表单中并提交或页面请求的参数中,最终使得服务器执行恶意的SQL命令. 防范: 对用户的输入进行校验或限制长度;对特殊字符进行转换, 不要使用动态拼装SQL,为每个应用使用单独的权限有限的数据库连接。对隐私信息进行加密

  • DDOS 攻击

分布式拒绝服务(DDoS:Distributed Denial of Service)攻击指借助于客户/服务器技术,将多个计算机联合起来作为攻击平台,对一个或多个目标发动DDoS攻击,从而成倍地提高拒绝服务攻击的威力。

26. 用js实现数组随机取数,每次返回的值都不一样

[参考答案]

function getUniqueItems(arr, num) {
  let temp = [];
  for (let index in arr) {
      temp.push(arr[index]);
  }
  let res = [];
  for (let i = 0; i<num; i++) {
      if (temp.length>0) {
          let arrIndex = Math.floor(Math.random()*temp.length);
          res[i] = temp[arrIndex];
          temp.splice(arrIndex, 1);
      } else {
        break;
      }
  }
  return res;
}

27. 页面上有1万个button如何绑定事件

[参考答案] 事件委托, 冒泡触发

28. base64为什么能提升性能以及它的缺点是什么

[参考答案] 优点:

  • 无额外请求
  • 适用于很小或者很简单的图片
  • 可像单独图片一样使用,比如背景图片等
  • 没有跨域问题,不需要考虑缓存、文件头或者cookies问题 缺点:
  • CSS 文件体积的增大, 造成CRP(关键渲染路径)阻塞
  • 页面解析CSS生成的CSSOM时间增加

29. 介绍webp图片文件格式

[参考答案]

WebP是一种支持有损压缩和无损压缩的图片文件格式,根据Google的测试,无损压缩后的WebP比PNG 文件少了45%的文件大小,即使这些PNG文件经过其他压缩工具压缩之后,WebP 还是可以减少28%的文件大小。

  • 优点
    • 更小的文件尺寸
    • 更高的质量——与其他相同大小不同格式的压缩图像比较
  • 缺点
    • 编码和解码速度比较慢,存在一定兼容性

30. react-router里的Link标签和a标签有什么区别

[参考答案]

从渲染的DOM来看,这两者都是链接,都是a标签,区别是: Link是react-router里实现路由跳转的链接,配合Route使用,react-router拦截了其默认的链接跳转行为,区别于传统的页面跳转,Link 的“跳转”行为只会触发相匹配的Route对应的页面内容更新,而不会刷新整个页面。 a标签是html原生的超链接,用于跳转到href指向的另一个页面或者锚点元素,跳转新页面会刷新页面。

31. 介绍一下函数柯里化,并写一个柯里化函数

[参考答案]

柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。柯里化函数能够让我们:

  1. 在多个函数调用中依次收集参数,不用在一个函数调用中收集所有参数。
  2. 当收集到足够的参数时,返回函数执行结果。

32. 介绍一下从输入URL到页面加载全过程

[参考答案]

  • 浏览器的地址栏输入URL并按下回车。
  • 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
  • DNS解析URL对应的IP。
  • 根据IP建立TCP连接(三次握手)。
  • HTTP发起请求。
  • 服务器处理请求,浏览器接收HTTP响应。
  • 渲染页面,构建DOM树。
  • 关闭TCP连接(四次挥手)。

33. 说说bind、call、apply的区别

[参考答案]

call和apply的共同点
  • 都能够改变函数执行时的上下文,将一个对象的方法交给另一个对象来执行,并且是立即执行的
  • 调用call和apply的对象,必须是一个函数Function
call和apply的区别
  • apply的第二个参数,必须是数组或者类数组,它会被转换成类数组,传入函数中,并且会被映射到函数对应的参数上, 而call从第二个参数开始,可以接收任意个参数
bind
  • bind()方法创建一个新的函数,与apply和call比较类似,也能改变函数体内的this指向。不同的是,bind方法的返回值是函数,并且需要稍后调用,才会执行。而apply和call 则是立即调用

34. ES6中的map和原生的对象有什么区别

[参考答案]

object和Map存储的都是键值对组合。区别:object的键的类型是字符串;map的键的类型可以是任意类型;另外,object获取键值使用Object.keys(返回数组)Map获取键值使用map变量.keys() (返回迭代器)。

35. 说说H5手机端的适配的几种方案

[参考答案]

    1. js实现一
(function (doc, win) {
  var docEl = doc.documentElement,
      resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
      recalc = function () {
          var clientWidth = docEl.clientWidth;
          var fontSize = 20;
          docEl.style.fontSize = fontSize + 'px';
          var docStyles = getComputedStyle(docEl);
          var realFontSize = parseFloat(docStyles.fontSize);
          var scale = realFontSize / fontSize;
          console.log("realFontSize: " + realFontSize + ", scale: " + scale);
          fontSize = clientWidth / 750 * 20;
          if(isIphoneX()) fontSize = 19;
          fontSize = fontSize / scale;
          docEl.style.fontSize = fontSize + 'px';
      };
  // Abort if browser does not support addEventListener
  if (!doc.addEventListener) return;
  win.addEventListener(resizeEvt, recalc, false);
  doc.addEventListener('DOMContentLoaded', recalc, false);

  // iphoneX判断
  function isIphoneX(){
      return /iphone/gi.test(navigator.userAgent) && (screen.height == 812 && screen.width == 375)
  }

})(document, window);
    1. css @media媒介查询(苏宁易购实现方式)
    1. 手淘的lib-flexible实现方式

36. 用js如何去除url中的#号

[参考答案]

  • 情景一: 单纯将hash路由改变成history路由即可去除hash的#号,此时需要服务器做路由重定向,比如nginx, node重定向
  • 情景二: 单纯去除#
function dropHash(url) {
  let i = url.indexOf('#')
  return i > -1 ? url.replace(/#/g, '') : url
}

37. Redux状态管理器和变量挂载到window中有什么区别

[参考答案] redux通过制定一套严格的规范,提供一种规范式的api和使用方式来处理状态, 适合大型项目和多人协同开发的项目,虽然代码编写有些繁复,但整体数据流向清楚,便于问题跟踪和后期维护,并且支持状态回溯.而window的变量管理比较混乱,维护不当还会造成变量污染,不适合中大型项目的开发.

38. webpack和gulp的优缺点

[参考答案]

gulp webpack
定位 基于任务流的自动化打包工具 模块化打包工具
优点 易于学习和理解, 适合多页面应用开发 可以模块化的打包任何资源,适配任何模块系统,适合SPA单页应用的开发
缺点 不太适合单页或者自定义模块的开发 学习成本低,配置复杂,通过babel编译后的js代码打包后体积过大

39. 说说jsonp为什么不支持post方法

[参考答案]

浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互,jsonp跨域本质上是通过动态script标签, 本质上也是对静态资源的访问,所以只能是get请求

40. 说说栈和堆的区别, 垃圾回收时栈和堆的区别以及栈和堆具体怎么存储

[参考答案]

1.从定义和存取方式上说:

  • 栈stack为自动分配的内存空间, 它由系统自动释放, 特点是"LIFO,即后进先出(Last in, first out)"。数据存储时只能从顶部逐个存入,取出时也需从顶部逐个取出,js的基本数据类型(Undefined、Null、Boolean、Number和String). 基本类型在内存中占据空间小、大小固定 ,他们的值保存在栈空间,按值访问
  • 堆heap是动态分配的内存,大小不定也不会自动释放. 特点是"无序"的key-value"键值对"存储方式. 比如js的对象,数组. 引用类型占据空间大、大小不固定, 栈内存中存放地址指向堆(heap)内存中的对象。是按引用访问的

2.从js数据的存取过程上说:

栈内存中存放的是对象的访问地址,在堆内存中为这个值分配空间。这个值大小不固定,因此不能把它们保存到栈内存中。但内存地址大小的固定的,因此可以将内存地址保存在栈内存中。这样,当查询引用类型的变量时,先从栈中读取内存地址,然后再通过地址找到堆中的值。

3.栈内存和堆内存与垃圾回收机制的联系和清除方式:

  • 垃圾回收机制: JavaScript中有自动垃圾回收机制,会通过标记清除的算法识别哪些变量对象不再使用,对其进行销毁。开发者也可在代码中手动设置变量值为null(xxx = null)进行清除,让引用链断开,以便下一次垃圾回收时有效回收。其次, 函数执行完成后,函数局部环境声明的变量不再需要时,就会被垃圾回收销毁(理想的情况下,闭包会阻止这一过程)。全局环境只有页面退出时才会出栈,解除变量引用。所以工程师们应尽量避免在全局环境中创建全局变量,如需使用,一定要在不需要时手动标记清除,将内存释放。
  • 栈内存和堆内存通常与垃圾回收机制有关。之所以会区分栈内存和堆内存,目的是使程序运行时占用的内存最小。当某个方法执行时,都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;当我们创建一个对象时,对象会被保存到运行时数据区中,以便反复利用(因为对象的创建内存开销较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用,则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在循环收集的过程中回收。

41. ES6中let块作用域是怎么实现的

[参考答案]

可以通过闭包自执行函数实现块作用域

42. 介绍排序算法和快排原理

[参考答案]

排序算法有:冒泡排序, 希尔排序, 快速排序, 插入排序, 归并排序, 堆排序, 桶排序等.

快速排序原理:

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

43. 说说node文件查找的优先级

[参考答案] 从文件模块缓存中加载 > 从原生模块加载 > 从文件加载

44. WebView和原生是如何通信的

[参考答案]

  • 使用Android原生的JavascriptInterface来进行js和java的通信
  • UrlRouter(通过内部实现的框架去拦截前端写的url,如果符合UrlRouter的协议的话,就执行相应的逻辑)
  • WebView 中的 prompt/console/alert 拦截,通常使用 prompt避免副作用
  • API注入,通过Native获取JavaScript环境上下文,并直接在上面添加方法,使js可以直接调用

45. pm2怎么做进程管理,如何解决进程奔溃问题

[参考答案]

  • 通过pm2 start去开启一个进程, pm2 stop去停止某个进程, pm2 list去查看进程列表, pm2 monit查看每个进程的cpu使用情况, pm2 restart重启指定应用等
  • 进程奔溃可以用过设置自动重启或者限制应用运行内存max_memory_restart(最大内存限制数,超出自动重启)

46. 直接给一个数组项赋值,Vue 能检测到变化吗,为什么?

[参考答案] vue中的数组的监听不是通过Object.defineProperty来实现的,是通过对'push', 'pop','shift','unshift','splice', 'sort','reverse'等改变数组本身的方法来通知监听的,所以直接给数组某一项赋值无法监听到变化,解决方案如下:

  • 用vue的set方法改变数组或者对象
  • 用改变数组本身的方法如splice, pop, shift等
  • 用深拷贝,解构运算符

总结

面试只是进入公司的第一步,如何走好它,需要自身的不断努力和学习,所以不要沉迷于走捷径,踏踏实实的学技术吧. 年前笔者会总结一下笔者2019年的技术成长与规划, 希望能和大家继续分享真正的前端技术.

最后

如果想了解更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公众号《趣谈前端》加入我们一起学习讨论,共同探索前端的边界。

招聘

年后公司因扩招需要2-3名熟悉react的工程师,坐标上海, 有意者请联系我哈~

更多推荐