最近的题(持续更新)

588 阅读25分钟

React

1.react 高效的原因

  • 虚拟(virtual)DOM,不是直接操作DOM
  • DOM diff算法,最小化页面重排重绘

2.如何在子组件中改变父组件状态

在父组件定义函数,传递给子组件,子组件调用。

3.diff算法原理

官方文档:zh-hans.reactjs.org/docs/reconc…

4.React 中 setState 什么时候是同步的,什么时候是异步的?

5.为什么虚拟dom会提高性能?

React Native

1.React Native如何实现跨平台

react Native是通过虚拟DOM实现跨平台,运行时将虚拟DOM转换为相应的web编码、android编号、ios编码进行运行的。

2.props和state相同点和不同点

相同点

  • 不管是props还是state的改变,都会引发render的重新渲染。
  • 都能由自身组件的相应初始化函数设定初始值。

不同点

  • 初始值来源:state的初始值来自于自身的getInitalState(constructor)函数;props来自于父组件或者自身getDefaultProps(若key相同前者可覆盖后者)。
  • 修改方式:state只能在自身组件中setState,不能由父组件修改;props只能由父组件修改,不能在自身组件修改。
  • 对子组件:props是一个父组件传递给子组件的数据流,这个数据流可以一直传递到子孙组件;state代表的是一个组件内部自身的状态,只能在自身组件中存在。

Vue

1.vue中的data为什么使用return返回数据?

不使用return包裹的数据会在项目的全局可见,会造成变量污染。使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件。

组件是一个可复用的实例,当你引用一个组件的时候,如果组件里的data是一个对象,那么所有用到这个组件的都引用的同一个data,就会造成数据污染。将data封装成函数后,在实例化组件的时候,我们只是调用了data函数生成的数据副本,避免了数据污染。

2.当数据变化时,vue时怎么更新节点的?

数据变化时,如果直接渲染真实DOM开销会很大,会造成整个DOM树的重排和重绘。diff算法帮助我们实现只更新修改的一小块DOM,而不是更新整个DOM。减小修改区域。

先根据真实的DOM生成一颗Virtual Dom,当Virtual Dom上某个节点的数据改变后,生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode。

先修改virtual DOM,此时不会更新界面,将修改批量映射到真实DOM上。减少修改真实DOM的次数。

3.Virtual DOM和真实DOM的区别?

Virtual DOM是将真实DOM的数据抽取出来,以对象的形式模拟一个树形结构

比如dom是这样的:

<div>
    <p>123</p>
</div>

对应的virtual DOM(伪代码):

var Vnode = {
    tag: 'div',
    children: [
        { tag: 'p', text: '123' }
    ]
};

PS:VNode和oldVNode都是对象

4.Vue中v-for的:key的作用?

vue的dom渲染是虚拟dom,diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。数据发生变化时,diff算法会只比较更改的部分,如果数据项的顺序被改变,vue是不会去移动DOM元素来匹配数据项的改变,而是简单的复用此处的每个元素,“就地复用”策略。比如说,我们在一个列表里插入了一个元素,diff算法会默认复用之前的列表,并在末尾追加一个。如果列表有选中一类的状态,则会因为复用而出现绑定错误的情况,而不是跟着原来的元素,key的作用就是给他一个标识,让状态跟着数据渲染。

比如一下这个情况: 在B和C之间加一个F,Diff算法默认执行起来是这样的:

要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

所以一句话,key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

5.为什么不能用index作为:key?

  • 更新DOM时会出影响性能
  • 会发生一些状态bug

比如一个长度为4的列表,用index作为key,删除最后一个元素是没问题的。如果删除第二个元素,那么此时除了第一个元素以外,第三个和第四个的index值发生变化,同时与其相应的key的绑定关系也发生改变,所以被重新渲染,影响性能。

如果列表当中的元素是select,假设第三个元素被选中,也就是key=2的被选中,删掉第二个元素后,原第三个元素的key变成1,原第四个元素的key变成2,也就导致了第四个元素被选中了,就产生了bug。

如果使用唯一id作为key,删除第二个元素后,剩下的元素因为与 key 的关系没有发生变化,都不会被重新渲染,从而达到提升性能的目的。

6.$nextTick用过吗,有什么作用?为什么要用nextTick?它解决了什么问题?

在下次DOM更新循环结束后执行延迟回调。在数据修改之后,立即使用这个方法,获取更新后的DOM。

解决的问题:有些时候在改变数据后要立即对DOM进行操作,此时获取到的还是数据修改前的DOM,无法满足需求,这个时候就用到了nextTick。

//修改数据
vm.msg = "haha";
// DOM 还没有更新
vm.$nextTick(function () {
    // DOM 更新了
})

// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })

7.为什么不应该使用箭头函数来定义 method 函数?watch呢?

理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

例如:

plus: () => this.a++

正确示例:

var vm = new Vue({
  data: { a: 1 },
  methods: {
    plus: function () {
      this.a++
    }
  }
})
vm.plus()
vm.a // 2

watch也同上。

8.Vue组件间的参数传递

1.父组件与子组件传值
父组件传给子组件:子组件通过props方法接受数据;
子组件传给父组件:$emit方法传递参数;

2.非父子组件间的数据传递,兄弟组件传值
eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。(虽然也有不少人推荐直接用VUEX,具体来说看需求咯。技术只是手段,目的达到才是王道。)

9.Vue中 route 和 router 的区别?

$route 是路由信息对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数。

$router是路由实例对象,包括了路由的跳转方法,钩子函数等。

10.对keep-alive 的了解?

当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。

  • include - 字符串或正则表达式,只有名称匹配的组件会被缓存
  • exclude - 字符串或正则表达式,任何名称匹配的组件都不会被缓存

include 和 exclude 的属性允许组件有条件地缓存。二者都可以用“,”分隔字符串、正则表达式、数组。当使用正则或者是数组时,要记得使用v-bind 。

11.Vuex中mutations和actions的区别

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

Mutations

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
mutations 必须是同步函数。为什么?

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

任何回调函数中进行的状态改变都是无法追踪的,devtools会对mutations的每一条提交做记录,记录上一次提交之前和提交之后的状态,在上面的例子中无法实现捕捉状态,因为在执行mutations时,内部回调函数还没有执行。

Action

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

action 的产生就是因为 mutation 不能进行异步操作,如果有异步操作那么就用action 来提交mutation。
让我们来注册一个简单的 action:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候):

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

12.如何解决首屏加载慢的问题?

首屏加载慢的原因:因为 vue 是一个单页面应用,加载首页的时候会加载所有的组件,并向服务器发送请求。

解决方法:

  • 去掉.map文件

在 config/index.js 文件中将productionSourceMap 的值设置为false

原因:打包后的dist文件中的.map文件起到一个辅助调试的作用,帮助我们线上调试代码,查看样式。所以为了避免部署包过大,通常都不生成这些文件。

  • vue-router 路由懒加载

懒加载即组件的延迟加载,通常vue的页面在运行后进入都会有一个默认的页面,而其他页面只有在点击后才需要加载出来。
使用懒加载可以将页面中的资源划分为多份,从而减少第一次加载的时候耗时。

  • 使用CDN引入插件

在Vue项目中,引入到工程中的所有js、css文件,编译时都会被打包进vendor.js,它是项目所有依赖的集成。浏览器在加载该文件之后才能开始显示首屏。若是引入的库众多,那么vendor.js文件体积将会相当的大,影响首开的体验。

解决方法是,将引用的外部js、css文件剥离开来,不编译到vendor.js中,而是用资源的形式引用,这样浏览器可以使用多个线程异步将vendor.js、外部的js等加载下来,达到加速首开的目的。

  1. 我们把import导入的方式删掉
  2. 通过cdn方式去引入import需要导入的库(我们不是不用import,而是换了另一种方式去引入,这样可以减轻vendor.js的负担)
  3. 在目录build/webpack.base.conf.js文件中配置externals。目的是在项目中通过import引入的依赖在打包的时候不会打包到bundle包中去,而是通过script(CDN)的方式去访问这些依赖。
  • 压缩文件(开启gzip压缩)

这个优化是两方面的,前端将文件打包成.gz文件,然后通过nginx的配置,让浏览器直接解析.gz文件。

webpack的配置

config/index.js

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: true,
    productionGzipExtensions: ['js', 'css'],

build/webpack.prod.conf.js

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

nginx的配置

修改服务器的nginx 配置,找到conf目录下的nginx.conf ,开启gzip,并设置gzip的类型,如下:

gzip  on;
gzip_types text/plain application/x-javascript application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;

重启nginx:

nginx -s reload
  • 首页单独做服务端渲染

如果首页真的有瓶颈,兼考虑seo问题,那还是用node的mvc框架(比如express)做首页渲染,而下面的子页面仍用spa单页(将vue打包的dist目录放在public目录下,当静态资源来访问)。

13.说说slot-scopes

slot-scope是vue2.10新增的一个作用域插槽。只负责对父组件的数据操作,至于样式什么的,都是由他的父组件来决定。现已废弃,推荐使用v-slot。

14.img标签动态设置src属性不生效,是为什么?

因为动态添加src被当做静态资源处理了,没有进行编译,所以要加上require。

15.如何深度监听

handler方法和immediate属性

watch的一个特点是,最初绑定的时候是不会执行的,要等到监听的属性改变时才执行监听。
解决办法:
监听属性函数内,定义一个handler函数,immediate设为true。 注意到handler了吗,我们给 firstName 绑定了一个handler方法,之前我们写的 watch 方法其实默认写的就是这个handler,Vue.js会去处理这个逻辑,最终编译出来其实就是这个handler。

watch: {
  firstName: {
    handler(newName, oldName) {
      this.fullName = newName + ' ' + this.lastName;
    },
    // 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
    immediate: true
  }
}

16.mixin与mixins

全局混入,影响创建的每个Vue实例,向组件注入自定义的行为。扩展组件。

如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。

mixins 混入的钩子函数会先于组件内的钩子函数执行,并且在遇到同名选项的时候也会有选择性的进行合并。

与vuex的区别

vuex:用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之改变。

mixins:可以定义公用的变量,在每个组件中使用,引入组件后,各个变量是相互独立的,值的修改在组件中不会相互影响。

与组件的区别

组件:在父组件中引入组件,相当于在父组件中给出了一篇独立空间供子组件使用,然后根据props来传值,但本质上两者是相对独立的。

mixins:则是在引入组件后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件。

17.props类型检查

18.vue数据为什么是异步加载

网络相关

1.http2

  • 多路复用:只通过一个 TCP 连接就可以传输所有的请求数据。解决了浏览器限制同一个域名下的请求数量的问题。
  • 二进制传输:引入了新的编码机制,所有传输的数据都会被分割,并采用二进制格式编码。
  • Header 压缩:对传输的 header 进行编码,减少了 header 的大小。并在两端维护了索引表,用于记录出现过的 header ,后面在传输过程中就可以传输已经记录过的 header 的键名,对端收到数据后就可以通过键名找到对应的值。
  • 服务端 Push:服务端可以在客户端某个请求后,主动推送其他资源

2.https原理

原理:

  1. 客户端请求 HTTPS 网址,然后连接到 server 的 443 端口 (HTTPS 默认端口,类似于 HTTP 的80端口)。
  2. 服务器响应客户端请求,CA证书会生成一对公钥A和私钥B,私钥留在服务器,传给客户端的信息包含公钥A和大量CA证书信息,比如证书颁发机构信息,公司信息和证书有效期等。
  3. 客户端解析证书并对其进行验证。
  4. 验证通过后,客户端生成一个随机码 KEY,并取出服务器的公钥A对KEY加密
  5. 服务器在收到后使用私钥B将KEY解密出来(完成非对称加密过程)。
  6. 服务器用KEY对数据进行加密,返给客户端。
  7. 客户端收到数据后再用KEY解密(完成对称加密过程)。

HTTPS 和 HTTP 的区别:

  • 最最重要的区别就是安全性,HTTP 明文传输,不对数据进行加密安全性较差。HTTPS (HTTP + SSL / TLS)的数据传输过程是加密的,安全性较好。
  • 使用 HTTPS 协议需要申请 CA 证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、DigiCert 和 GlobalSign 等。
  • HTTP 页面响应速度比 HTTPS 快,这个很好理解,由于加了一层安全层,建立连接的过程更复杂,也要交换更多的数据,难免影响速度。
  • 由于 HTTPS 是建构在 SSL / TLS 之上的 HTTP 协议,所以,要比 HTTP 更耗费服务器资源。
  • HTTPS 和 HTTP 使用的是完全不同的连接方式,用的端口也不一样,前者是 443,后者是 80。

HTTPS 的缺点:

  • 在相同网络环境中,HTTPS 相比 HTTP 无论是响应时间还是耗电量都有大幅度上升。
  • HTTPS 的安全是有范围的,在黑客攻击、服务器劫持等情况下几乎起不到作用。
  • 在现有的证书机制下,中间人攻击依然有可能发生。
  • HTTPS 需要更多的服务器资源,也会导致成本的升高。

3.了解Service Worker嘛?

是运行在浏览器背后的独立线程,可用于实现缓存功能,传输协议必须是HTTPS。使用Service-Worker实现缓存功能一般分为三个步骤:首先注册,然后监听install事件缓存需要的文件,最后拦截请求事件,如果缓存中已经有请求的数据就直接使用。

4.HTTPS 握手过程中,客户端如何验证证书的合法性

5.img iframe script 来发送跨域请求有什么优缺点?

HTML

1.H5上传图片如何预览?

2.canvas与svg

CANVAS

SVG

3.你怎么看待模板引擎?

严格的模板引擎的定义,输入模板字符串 + 数据,得到渲染过的字符串。
字符串渲染的性能其实也就在后端比较有意义,毕竟每一次渲染都是在消耗服务器资源,但在前端,用户只有一个,几十毫秒的渲染时间跟请求延迟比起来根本不算瓶颈。倒是前端的后续更新是字符串模板引擎的软肋,因为用渲染出来的字符串整个替换 innerHTML 是一个效率很低的更新方式。所以这样的模板引擎如今在纯前端情境下已经不再是好的选择,意义更多是在于方便前后端共用模板。

4.说说BFC

BFC是 块格式化上下文(Block Formatting Context)

BFC的布局规则

  1. 内部的Box会在垂直方向一个接着一个地放置。
  2. Box垂直方向上的距离由margin决定。属于同一个BFC的两个相邻的Box的margin会发生重叠。
  3. 每个盒子的左外边框紧挨着包含块的左边框,即使浮动元素也是如此。
  4. BFC的区域不会与float box重叠。
  5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然。
  6. 计算BFC的高度时,浮动子元素也参与计算。

如何创建BFC

  1. float的值不是none。
  2. position的值不是static或者relative。
  3. display的值是inline-block、table-cell、flex、table-caption或者inline-flex
  4. overflow的值不是visible

BFC的作用

  1. 解决浮动元素令父元素高度坍塌的问题
    方法:给父元素开启BFC,设置样式 overflow:hidden
    原理:计算BFC的高度时,浮动子元素也参与计算
  2. 非浮动元素被浮动元素覆盖题
    方法:给非浮动元素开启BFC,设置样式 overflow:hidden
    原理:BFC的区域不会与float box重叠
  3. 两栏自适应布局
    方法:给固定栏设置固定宽度,给不固定栏开启BFC,设置样式 overflow:hidden
    原理:BFC的区域不会与float box重叠
  4. 外边距垂直方向重合的问题
    方法:给上box或者下box任意一个包裹新的box并开启BFC,设置样式 overflow:hidden
    原理:属于同一个BFC的两个相邻的Box的margin会发生重叠

5.什么是Bom?有哪些常用的Bom属性?

6.你了解的浏览器的重绘和回流导致的性能问题

JS

1.函数柯里化

2.列举出集中创建实例的方法

3.简述一下前端事件流

4.简述一下原型 / 构造函数 / 实例

5.简述一下JS继承,并举例

6.Function.proto(getPrototypeOf)是什么?

7.模块规范CommonJS、 AMD、 CMD、ES6

CommonJS(同步模块定义)

CommonJS是服务器端模块的规范,Node.js采用了这个规范。CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。

CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}

AMD(异步模块定义)

AMD(异步模块定义),它就主要为前端JS的表现制定规范。
AMD就只有一个接口:define(id?,dependencies?,factory);
它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中,像这样:

define(['dep1','dep2'],function(dep1,dep2){...});

要是没什么依赖,就定义简单的模块,下面这样就可以啦:

define(function(){
    var exports = {};
    exports.method = function(){...};
    return exports;
});

RequireJS就是实现了AMD规范。

CMD(同步模块定义)

通过define()定义,没有依赖前置,通过require加载jQuery插件,CMD是依赖就近,在什么地方使用到插件就在什么地方require该插件,即用即返,这是一个同步的概念。

define(function(require,exports,module){...});

SeaJS就是实现了AMD规范

ES6模块化

export/import对模块进行导出导入的

8.call,apply,bind

call和apply共同点是,都能够改变函数执行时的上下文,将对象的函数交给另一个对象来执行,并且是立即执行。

bind() 方法创建一个新的函数,在调用时设置 this 关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。

bind 方法 与 apply 和 call 比较类似,也能改变函数体内的 this 指向。不同的是,bind 方法的返回值是函数,并且需要稍后调用,才会执行。而 apply 和 call 则是立即调用。

详见:segmentfault.com/a/119000001…

9.什么是类数组?

数组特征:可以通过角标调用,如 array[0];具有长度属性length;可以通过 for 循环或forEach方法,进行遍历。

类数组:具备与数组特征类似的对象。可以通过角标进行调用,具有length属性,同时也可以通过 for 循环进行遍历。如:

let arrayLike = {
    0: 1,
    1: 2,
    2: 3,
    length: 3
};

注意:类数组无法使用 forEach、splice、push 等数组原型链上的方法,毕竟它不是真正的数组。

如果它想使用 Array 原型链上的方法,可以使用Array.prototype.slice.call(arrayLike)

10.类数组转为数组的方式有哪些?

[].slice.call(arguments):借用了数组原型中的slice方法,返回一个数组。 Array.from(arguments):从一个类数组或可迭代对象创建一个新的,浅拷贝的数组实例。 [...arguments]:使用扩展运算符

11.数组去重

  • [...new Set(arr)]
  • 循环遍历
  • 利用indexOf
  • 利用arr.includes()检测数组是否有某个值
  • 利用filter:检测数值元素,并返回符合条件所有元素的数组。
function unique(arr) {
    return arr.filter((item, index, arr) => {
        return arr.indexOf(item) === index;
    })
}

12.slice 和 splice

slice:选取一部分元素,并返回一个新数组。 splice:删除一部分元素,并返回一个原数组

13.字符串的test、match、search它们之间的区别?

`test`是检测字符串是否匹配某个正则,返回布尔值;
/[a-z]/.test(1);  // false

`match`是返回检测字符匹配正则的数组结果集合,没有返回`null`'1AbC2d'.match(/[a-z]/ig);  // ['A', 'b', 'C', 'd']

`search`是返回正则匹配到的下标,没有返回`-1`'1AbC2d'.search(/[a-z]/);  // 2

14.字符串的slice、substring、substr它们之间的区别?

`slice`是返回字符串开始至结束下标减去开始下标个数的新字符串,下标是负数为倒数;
`slice(开始下标,结束下标+1))`
`slice(开始下标,开始下标+截取个数(结束下标-开始下标+1))`

'abcdefg'.slice(2,3);  // c  // 3 - 2
'abcdefg'.slice(3,2);  // ''  // 2 - 3
'abcdefg'.slice(-2,-1);  // f  // -1 - -2

`substring``slice`正常截取字符串时相同,负数为0,且下标值小的为开始下标;
'abcdefg'.substring(2,3);  //c  // 3 - 2
'abcdefg'.substring(3,2);  // c  // 3 - 2 
'abcdefg'.substring(3,-3);  // abc  // 3 - 0

`substr`返回开始下标开始加第二个参数(不能为负数)个数的新字符串。
`substr(开始下标,截取个数))`
'abcdefg'.substr(2, 3);  // cde
'abcdefg'.substr(3, 2);  // de
'abcdefg'.substr(-3, 2); // ef

15.在类型转换中哪些值会被转为true?

除了undefined、null、false、NaN、''、0、-0以外的值都会被转为true,包括所有引用类型,即使是空的。

16.什么是基本包装类型?

  • Boolean
  • Number
  • String

后台对基本类型进行了包装,首先会使用各自的构造函数创建对应的实例,这样调用这些方法时就可以正常使用,不过再方法调用结束后,就会将实例给销毁掉,从而又是基本类型。

自动创建的基本包装类型对象,只存在于一行代码执行的瞬间,然后立即被销毁。

let s1 = 'hello'
let s2 = s1.substring(2)
↓ 后台包装
let s1 = new String('hello') // 包装
let s2 = s1.substring(2) // 可以调用方法
s1 = null // 销毁

17.toString()和valueOf的区别?

tostring:值类型时返回自身的字符串形式;当是引用类型时,无论是一维或多维数组,将他们拍平成一个字符串,里面的null和undefined转为空字符串'',对象转为[object Object],函数的原样返回字符串形式。

valueOf:返回指定对象的原始值(布尔,数字,字符串)

在进行字符串强转的时候,toString会优先于valueOf;在进行数值运算时,valueOf会优先于toString。

数组 对象 Date类型时,返回时间戳

18.原型继承的方式有哪些?

原型链继承,借用构造函数继承,组合继承,原型式继承,寄生组合继承等。最优的继承方式是寄生组合继承。

function Parent(name) {
  this.name = name;
}
function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child

19.垃圾回收机制

JavaScript程度每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体,执行环境会负责管理代码执行过程中使用到的内存,什么时候划分新的内存和把占用的内存释放出来,这一套自动管理机制就是垃圾回收机制。

垃圾回收的方式有两种:

  • 标记清除:垃圾收集器会给内存中每个变量做上标记排除环境中的变量以及被环境中变量引用的变量的标记在此之后加上标记的变量会被视为准备删除的变量最后垃圾收集器完成内存清除工作。
  • 引用计数:追踪记录每个值被引用的次数,当声明了一个变量并将一个引用类型赋给该变量时,这个变量的引用次数就是1。相反如果包含这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当为0时,这说明没有办法再访问这个值了,因此垃圾收集器下次运行时,就会释放该值占用的内存。

20.axios和fetch的区别

axios

  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 客户端支持防止CSRF
  • 提供了一些并发请求的接口(重要,方便了很多的操作)
  • 拦截请求和响应
  • 自动转换JSON数据

fetch

优点:

  • 符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里
  • 更好更方便的写法
  • 更加底层,提供的API丰富(request, response)
  • 脱离了XHR,是ES规范里新的实现方式

缺点:

  • fetchtch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
  • fetch默认不会带cookie,需要添加配置项
  • fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
  • fetch没有办法原生监测请求的进度,而XHR可以

CSS

1.sass和less

Sass和Less都属于CSS预处理器。

Sass

  • 基于Ruby环境
  • 有变量和作用域
  • 有函数的概念:@function和@return以及函数参数(还有不定参)
  • 进程控制:条件:@if @else;循环遍历:@for @each @while;继承:@extend;引用:@import
  • 数据结构:list类型=数组;list类型=数组;map类型=object;其余的也有string、number、function等类型
  • 通过服务端处理
  • 变量以 $ 开始
  • 可以遍历任何类型的数据

Less

  • 基于JavaScript环境
  • 没有裁剪 CSS 原有的特性,而是在现有 CSS 语法的基础上,为 CSS 加入程序式语言的特性
  • 通过客户端处理(解析相对较慢)
  • 变量以 @ 开始
  • 只能使用递归函数循环数值

相同之处

  • 混入(Mixins)——class中的class;
  • 参数混入——可以传递参数的class,就像函数一样;
  • 嵌套规则——Class中嵌套class,从而减少重复的代码;
  • 运算——CSS中用上数学;
  • 颜色功能——可以编辑颜色;
  • 名字空间(namespace)——分组样式,从而可以被调用;
  • 作用域——局部修改样式;
  • JavaScript 赋值——在CSS中使用JavaScript表达式赋值。

2.em、rem的区别?

em:如果父级有设置字体大小,1em就是父级的大小,没有1em等于自身默认的字体大小。 rem:相对于html标签的字体大小。

3.移动端 1px 像素

在做移动端开发时,设计师提供的视觉稿一般是750px,当你定义 border-width:1px 时,在iphone6手机上却发现,边框变粗了。

4.分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景

5.link与@import的区别

6.display: block;和display: inline;的区别

7.容器包含若干浮动元素时如何清理浮动

8.PNG,GIF,JPG 的区别及如何选

Webpack相关

1.webpack配置删掉console.log

开放性问题

1.谈下vue和react的差异

详见:caibaojian.com/vue-vs-reac…

2.谈下对react hook的理解

3.谈下对typescript的理解

4.谈下对前端微服务的理解,有什么好处,有什么坏处

微服务架构是一种架构理念,是指将功能分解到离散的各个服务当中,从而降低系统的耦合性,并提供更加灵活的服务支持。把一个大型的单体应用程序和服务拆分为数个或数十个的微小型服务,它可扩展单个组件而不是整个的应用程序堆栈,从而满足服务等级协议。

5.谈下对serverless架构的理解

6.谈下react fiber的理解

7.前端适配方案

详见 blog.csdn.net/chenjuan199…

8.谈下mobx和redux的差异和选择

9.如果让你从零主导一个项目,描述下整体思路,前端后端,开发到部署

10.如果让你搭建一套前端监控方案,具体思路

11.如何定位内存泄露

使用工具Heap Profiling,详见 blog.csdn.net/qq_34309704…

12.谈谈设计模式

13.异步编程,函数式编程

异步编程

  1. 回调函数
function f1(callback) {
    setTimeout(function() {
        // f1的任务代码
        callback();
    }, 1000);
}
f1(console.log("love you"));
  1. promise

每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。

var promise = new Promise(function(resolve,reject) {
    setTimeout(function() {
        resolve();
    }, 1000);
})

promise.then(function() {
    console.log("love you");
},function(error) {
    console.log(error);
})
  1. async/await
function f1() {
    return new Promise(function(resolve, reject) {
        setTimeout(() => {
            resolve()
        }, 1000)
    })
}

async function asyncPrint(){
    await f1();
    console.log("love you");
}

asyncPrint();

语法注意:

1.async 函数返回 一个Promise 对象。async 函数内部return语句返回的值,会成为then回调函数的参数

async function f() {
    return 'hello'
}

f().then((data) => {
    console.log(data) // ‘hello’
})

2.async函数内部抛出错误,会导致返回的 Promise 对象编程reject状态,抛出的错误对象会被catch回调函数接收到。

async function f() {
    throw new Error('出错了')
}

f().then(
    data => console.log(data),
    error => console.log(error)
)

3.防止出错的方法,也是将其放在try...catch代码块之中。

async function f() {
    try {
        await f1();
        console.log("love you");
    }
    catch(error) {
        console.log(error);
    }
}

函数式编程

利用高阶函数进行函数变换的思路,就是函数式编程的思路。

函数式编程思想,不修改组件的情况下对这个组件的方法做一些额外的修饰。

14.对前端工程化的理解

一切能提升前端开发效率,提高前端应用质量的手段和工具都是前端工程化。

  1. 极大提升开发效率
  2. 降低大型项目的开发难度

详见:blog.csdn.net/valada/arti…

15.说说负载均衡

16.fetch发送2次请求的原因

17.ES5的继承和ES6的继承有什么区别?

18.定时器的执行顺序或机制?

19.[‘1’,‘2’,‘3’].map(parseInt) 输出什么,为什么?

20.get请求传参长度的误区、get和post请求在缓存方面的区别

  • HTTP 协议 未规定 GET 和POST的长度限制
  • GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度
  • 不同的浏览器和WEB服务器,限制的最大长度不一样
  • 要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度 8182byte

get和post在缓存方面的区别:

  • get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
  • post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。

21.监控和埋点