2019前台面试题汇总(一年经验)

  • 时间:2019-06-11 05:51 作者:舒店长 来源:舒店长 阅读:349
  • 扫一扫,手机访问
摘要:写在开头跳槽是为了得到更高的薪酬,或者者为了寻觅更具发展空间的公司一种常见的行为。我也于今年完成了一次跳槽,自然也有很多的感悟,作成本文供大家参考。本文主要分成两个部分,一部分是我自己整理的面试大纲,另外一部分是面试官考察的问题。面试大纲关于面试大纲,我认为每个人都是千差万别的。由于面试官都是对简历所

写在开头

跳槽是为了得到更高的薪酬,或者者为了寻觅更具发展空间的公司一种常见的行为。我也于今年完成了一次跳槽,自然也有很多的感悟,作成本文供大家参考。
本文主要分成两个部分,一部分是我自己整理的面试大纲,另外一部分是面试官考察的问题。

面试大纲

关于面试大纲,我认为每个人都是千差万别的。由于面试官都是对简历所写的项目经验进行深挖或者者对你所掌握的基本技能进行考察。
我在简历上写的技能点包含了HTML/CSS/JavaScript/ES6/HTTP协议/Vue/微信小程序/Node/Express/MySQL/ 面向对象/设计模式
根据以上,整理出面试大纲

一、HTML

<li>标签之间有空格怎样解决?

起因:行框的排列会受到中间空白(回车空格)等的影响,由于空格也属于字符,这些空白也会被应用样式,占据空间,所以会有间隔,把字符大小设为0或者者将<li>标签写在一排,就没有空格了。

二、CSS

CSS3新特性
  1. 支持rgba和透明度
  2. 支持媒体查询
  3. 支持自己设置字体
  4. 对长的不可分割单词换行word-wrap:break-word;
  5. 文字阴影text-shadow: 5px 5px 5px #ff0000;
  6. 盒阴影box-shadow: 10px 10px 5px #888888
  7. 支持圆角border-radius: 50%;
  8. 边框图片border-image: url(border.png) 30 30 round
CSS优先级如何计算?
  • 元素和伪元素:1
  • 类选择器、属性选择器或者伪类:10
  • id选择符:100
  • 内联样式:1000
  1. !important公告的样式优先级最高,假如冲突再进行计算。
  2. 继承得到的样式的优先级最低。
  3. 假如优先级相同,则选择最后出现的样式。
清理浮动的几种方式和各自的优缺点
  1. 增加空div,使用clear: both;缺点:进行清理浮动;会增加很多无意义的空标签,有违结构与体现的分离,在后期维护中将是噩梦;
  2. 父元素使用overflow: hidden;缺点:不能配合position一起使用,超出的部分会被隐藏;
  3. 父元素使用overflow: auto;缺点:超出部分会出现滚动条;
  4. 父级定义高度
  5. 推荐使用的方法:父级div定义伪类:after和zoom
<style>.clearfloat {    *zoom: 1;}.clearfloat:after {    content: "";    height: 0;    display: block;    clear: both;    visibility: hidden;}</style><!-- *是为了兼容低版本IE7浏览器 zoom是ie属性,设置缩放比例 --><div class="box clearfloat">  <div class="left">  <div class="right"></div>
如何水平居中一个浮动元素?
#test {  position: absolute;  width: 100px;  height: 100px;  background-color: green;  <!-- 三行代码缺一不可 -->  margin: 0 auto;  left: 0;  right: 0;}
<div id="test"></div>
如何水平居中一个浮动元素?
#test {  position: absolute;  width: 100px;  height: 100px;  background-color: green;  <!-- 三行代码缺一不可 -->  margin: 0 auto;  left: 0;  right: 0;}
<div id="test"></div>
利用CSS画三角形
  #triangle {    width: 0;    height: 0;    border-top: 40px solid transparent;    border-left: 40px solid transparent;    border-right: 40px solid transparent;    border-bottom: 40px solid greenyellow;  }
<div id="triangle"><div>
display: none;visibility: hidden; 的区别?
  1. display:none; 不显示对应的元素,在文档布局中不再分配空间(回流+重绘)
  2. visibility:hidden; 隐藏对应元素,在文档布局中仍保留原来的空间(重绘)
positionfloat 值相互叠加会发生什么?

position:absolute/fixed; 优先级最高,有他们在时,float不起作用,display值需要调整。

对BFC规范(块级格式化上下文:block formatting context)的了解?

BFC规定了内部的Block Box如何布局。
定位方案:

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

满足下列条件之一即可触发BFC

  1. 根元素,即html
  2. float的值不为none(默认)
  3. overflow的值不为visible(默认)
  4. display的值为inline-block、table-cell、table-caption
  5. position的值为absolute或者fixed
为什么会出现浮动和什么时候需要清理浮动?清理浮动的方式?

浮动元素碰到包含它的边框或者者浮动元素的边框停留。因为浮动元素不在文档流中,所以文档流的块框体现得就像浮动框不存在一样。浮动元素会漂浮在文档流的块框上。
浮动带来的问题:

  1. 父元素的高度无法被撑开,影响与父元素同级的元素
  2. 与浮动元素同级的非浮动元素(内联元素)会跟随其后
  3. 若非第一个元素浮动,则该元素之前的元素也需要浮动,否则会影响页面显示的结构。

清理浮动的方式:

  1. 父级div定义height
  2. 最后一个浮动元素后加空div标签 并增加样式clear:both。
  3. 包含浮动元素的父标签增加样式overflow为hidden或者auto。
  4. 父级div定义zoom
:after::after的区别是什么?
  1. 在CSS3的规范里:表示伪类,::表示伪元素
  2. :after在CSS2.1的规范中,表示伪元素,随着WEB的发展,在CSS3的规范中,伪元素的语法被修改成使用双冒号,成为::after
实现一个高度自适应的div,里面有两个div,一个高度100px,希望另一个填满剩下的高度
  html, body {    padding: 0;    margin: 0;    width: 100%;    height: 100%;  }  #box {    position: relative;    height: 100%;    width: 200px;    background-color: beige;  }  #box .block-1 {    width: 200px;    height: 100px;    background-color: rosybrown;  }  #box .block-2 {    position: absolute;    top: 100px;    bottom: 0;    width: 200px;    background-color: red;  }
  <div id="box">    <div class="block-1"></div>    <div class="block-2"></div>  </div>

三、JavaScript

简单说说继承的方式和优缺点?
原型链继承
function Parent(name) {     this.name = name;}Parent.prototype.sayName = function() {    console.log('parent name:', this.name);}function Child(name) {    this.name = name;}Child.prototype = new Parent('father');Child.prototype.constructor = Child;Child.prototype.sayName = function() {    console.log('child name:', this.name);}var child = new Child('son');child.sayName();    // child name: son// 这种方法存在两个缺陷:// 1.子类型无法给超类型传递参数,在面向对象的继承中,我们总希望通过 var child = new Child('son', 'father'); 让子类去调用父类的构造器来完成继承。而不是通过像这样 new Parent('father') 去调用父类。// 2.Child.prototype.sayName 必需写在 Child.prototype = new Parent('father') 之后,不然就会被覆盖掉。
类式继承
function Parent(name) {     this.name = name;}Parent.prototype.sayName = function() {    console.log('parent name:', this.name);}Parent.prototype.doSomthing = function() {    console.log('parent do something!');}function Child(name, parentName) {    Parent.call(this, parentName);    this.name = name;}Child.prototype.sayName = function() {    console.log('child name:', this.name);}var child = new Child('son');child.sayName();      // child name: sonchild.doSomthing();   // TypeError: child.doSomthing is not a function// 处理了原型链继承带来的问题// 但存在缺陷:每次创立一个 Child 实例对象时候都需要执行一遍 Parent 函数,无法复用少量公用函数。
组合式继承
function Parent(name) {     this.name = name;}Parent.prototype.sayName = function() {    console.log('parent name:', this.name);}Parent.prototype.doSomething = function() {    console.log('parent do something!');}function Child(name, parentName) {    Parent.call(this, parentName);      // 第二次调用    this.name = name;}Child.prototype.sayName = function() {    console.log('child name:', this.name);}Child.prototype = new Parent();         // 第一次调用Child.prototype.construtor = Child;var child = new Child('son');child.sayName();      child.doSomething(); // 第一次调用构造函数显然是没有必要的,由于第一次调用构造函数时候不需要函数内部的那些实例属性,这么写只是想取得其原型上的方法罢了// 下面的寄生组合式继承处理了这个问题
寄生组合式继承
function Parent(name) {    this.name = name;}Parent.prototype.sayName = function() {    console.log('parent name:', this.name);}function Child(name, parentName) {    Parent.call(this, parentName);      this.name = name;    }Child.prototype = Object.create(Parent.prototype);   //修改Child.prototype.construtor = Child;Child.prototype.sayName = function() {    console.log('child name:', this.name);}var parent = new Parent('father');parent.sayName();      // parent name: fathervar child = new Child('son', 'father');child.sayName();       // child name: son
// tips:/*  Object.create(proto, [propertiesObject])  Object.create()方法创立一个新对象,使用现有的对象来提供新创立的对象的__proto__    proto    新创立对象的原型对象。  propertiesObject 可选。    假如没有指定为undefined,则是要增加到新创立对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性形容符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。  */
谈谈JS中的this
  1. this是在执行上下文创立时确定的一个在执行过程中不可更改的变量
  2. this只在函数调用阶段确定,也就是执行上下文创立的阶段进行赋值,保存在变量对象中。

执行上下文,就是JavaScript引擎在执行一段代码之前将代码内部会用到的少量变量函数this提前公告而后保存在变量对象中的过程。

运算符优先级
  1. ()括号 .成员访问
  2. ()函数调用 new
  3. ++ -- !
  4. * %
  5. + -
  6. >= <= > <
  7. == !==
  8. & ^ | 位运算符
  9. && || 逻辑运算符
  10. a ? x : y 条件运算符
  11. = op = 运算赋值
// 本题的涉及考点主要还是运算符的优先级function Foo() {  getName = function() {    console.log(1)  }  return this}Foo.getName = function() {  console.log(2)}Foo.prototype.getName = function() {  console.log(3)}var getName = function() {  console.log(4)}function getName() {  console.log(5)}// 写出以下函数的执行结果Foo.getName()            // 2getName()                // 4Foo().getName()          // 1  getName()                // 1new Foo.getName()        // 2// new (Foo.getName)()new Foo().getName()      // 3// (new Foo()).getName()new new Foo().getName()  // 3// new((new Foo()).getName)()
简单聊一聊包装对象?

引用类型包装对象的区别在于生存期
引用类型所创立的对象会一直存在于堆内存中,而基本包装对象只存在于一瞬间

var str = 'hello';str.number = 10;   //假设我们想给字符串增加一个属性number ,后端会有如下步骤/*  var str = new String('hello'); // 1 找到对应的包装对象类型,而后通过包装对象创立出一个和基本类型值相同的对象  str.number = 10; // 2 通过这个对象调用包装对象下的方法 但结果并没有被任何东西保存  str =null; // 3 这个对象又被销毁*/alert(str.number); //undefined  当执行到这一句的时候,由于基本类型原本没有属性,后端又会重新重复上面的步骤/* var str = new String('hello'); // 1 找到基本包装对象,而后又新开拓一个内存,创立一个值为hello对象 str.number = undefined   // 2 由于包装对象下面没有number这个属性,所以又会重新增,由于没有值,所以值是未定 ;而后弹出结果 str =null; // 3 这个对象又被销毁*/
instanceof 和 typeof

instanceof运算符用于测试构造函数的prototype属性能否出现在对象的原型链中的任何位置(Instanceof 的使用规则是: A instanceof B A沿着proto这条线来找,同时B沿着prototype这条线来找,假如两条线能找到同一个引用,即同一个对象,那么就返回true。假如找到终点还未重合,则返回false。)

函数/变量提升
// 1、函数提升优先级高于变量提升// 2、表达式可以修改提升后变量的值test() // 2var test = function() {  console.log(1)}function test() {  console.log(2)}test() // 1  alert(a);var a=1;alert(a); function a(){alert(2)};alert(a);var a=3;alert(a);function a(){alert(4)};alert(a);a();// function a() {alert (4)}; // 1// 1// 3// 3// 报错
new 操作符都干了些什么?
// new共经历了四个阶段// 1、创立一个空对象var obj = new Object()// 2、设置原型链obj.__proto__ = Func.prototype// 3、让Func中的this指向obj,并执行Func的函数体var result = Func.call(obj)// 4、判断Func的返回值类型// 假如是值类型,返回obj。假如是引用类型,就返回这个引用类型的对象// (tip: 构造函数默认 return this,不用写)if(typeof result === 'object') {    return result} eles {    return obj}

四、Vue

对MVVM的了解

MVVM分为Model、View、ViewModel三者

Model 代表数据模型,数据和业务逻辑都在Model层中定义;
View 代表UI视图,负责数据的展现;
ViewModel 负责监听 Model 中数据的改变并且控制视图的升级,解决客户交互操作;

Model 和 View 并无直接关联,而是通过 ViewModel 来进行联络的,Model 和 ViewModel 之间有着双向数据绑定的联络。因而当 Model 中的数据改变时会触发 View 层的刷新,View 中因为客户交互操作而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步,因而开发者只要要专注对数据的维护操作就可,而不需要自己操作 dom

Vue的双向数据绑定是如何实现的?

简单来说就是使用数据劫持发布订阅的设计模式实现的

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <meta http-equiv="X-UA-Compatible" content="ie=edge">  <title>MVVM双向数据绑定</title></head><body>  <script>    class Vue {      constructor(options) {        this.options = options        this.$data = options.data        this.$el = document.querySelector(options.el)        this._directives = {};        // 数据劫持        this.Observer(this.$data)        // 解析指令        this.Compile(this.$el)      }      Observer(data) {        for(let key in data) {          this._directives[key] = []          let val = data[key]          let _obj = this._directives[key]          Object.defineProperty(this.$data, key, {            get: function() {              return val            },            set: function(newVal) {              if(val !== newVal) {                val = newVal                _obj.forEach((item) => {                  item.update()                })              }            }          })        }      }      Compile(el) {        let nodes = el.children        for(let i=0; i<nodes.length; i++) {          let node = nodes[i]          if(node.children.length) {            this.Compile(node)          }          if(node.hasAttribute('v-text')) {            let attrValue = node.getAttribute("v-text");            this._directives[attrValue].push(new Watch(node, this, attrValue, "innerHTML"))          }          if(node.hasAttribute('v-model')) {            let _this = this            let attrValue = node.getAttribute("v-model");            this._directives[attrValue].push(new Watch(node, this, attrValue, "value"))            node.addEventListener('input', (function(){              return function() {                _this.$data[attrValue] = node.value              }            })())          }        }      }    }    class Watch {      constructor(el, vm, exp, attr) {        this.el = el        this.vm = vm        this.exp = exp        this.attr = attr        this.update()      }      update() {        this.el[this.attr] = this.vm.$data[this.exp]      }    }  </script>    <div id="app">    <h1>数据双向绑定</h1>    <div>      <div v-text="myText"></div>      <input type="text" v-model="myText">    </div>  </div>  <script>    const app = new Vue({      el: '#app',      data: {        myText: '今晚吃鸡呐,大吉大利!'      }    });  </script></body></html>
Vue如何监听某个属性值的变化
watch: {  'obj.a': {    handler (newName, oldName) {      console.log('obj.a changed')    }  }}computed: {  a1 () {    return this.obj.a  }}
Vue中给data中的对象属性增加一个新的属性时会发生什么,如何处理?

数据已经增加,但是视图并没有刷新;

起因在于在Vue实例创立时,obj.b并未公告,因而就没有被Vue转换为响应式的属性,自然就不会触发视图的升级,这时就需要使用Vue的全局api Vue.$set()

addObjB () {  // this.obj.b = 'obj.b'  this.$set(this.obj, 'b', 'obj.b')  console.log(this.obj)}
Vue组件通讯的方式
  1. 父子组件通讯:父->子:使用props,子->父:$emit方法传递参数
  2. 使用eventBus,就是创立一个事件中心,相当于中转站,可以用它来传递事件和接收事件
  3. Vuex

可以去掘金找这类文章看一下,Vue组件的通讯方式达6种之多

Vue常用的修饰符

.prevent: 提交事件不再重载页面;
.stop: 阻止单击事件冒泡;
.self: 当事件发生在该元素本身而不是子元素的时候会触发;
.capture: 事件侦听,事件发生的时候会调用;

$route 和 $router 的区别

$route 是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数
$router 是“路由实例”对象包括了路由的跳转方法,钩子函数等

Vue的路由实现:hash模式 和 history模式

hash模式: 在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash尽管在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因而对于后台来说,即便没有做到对路由的全覆盖,也不会返回 404 错误。

history模式: history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前台的 URL 必需和实际向后台发起请求的 URL 一致,如 http://www.xxx.com/items/id。后台假如缺少对 /items/id 的路由解决,将返回 404 错误。Vue-Router 官网里如此形容:“不过这种模式要玩好,还需要后端配置支持……所以呢,你要在服务端添加一个覆盖所有情况的候选资源:假如 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

对keep-alive的理解?

keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或者避免重新渲染
在vue 2.1.0 版本之后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。

delete和Vue.delete删除数组的区别?

delete只是被删除的元素变成了 empty/undefined 其余的元素的键值还是不变;
Vue.delete直接删除了数组 改变了数组的键值。

如何优化SPA应用的首屏加载速度慢的问题?
  1. 将公用的JS库通过script标签外部引入,减小app.bundel的大小,让浏览器并行下载资源文件,提高下载速度;
  2. 在配置 路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundel 的体积,在调用某个组件时再加载对应的js文件;
  3. 加一个首屏 loading 图,提升客户体验;
Vuex是什么?如何使用?哪种场景使用它?

将需要读取的状态集中放在store
改变状态的方式是提交mutations,这是一个同步的事务
异步逻辑应该封装在actions

state Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和板块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations 定义的方法动态修改Vuexstore中的状态或者数据。
getters 相似vue的计算属性,主要用来过滤少量数据。
action 可以了解为通过将mutations里面处里数据的方法变成可异步的解决数据的方法,简单的说就是异步操作数据。view层通过store.dispath来分发action
modules 项目特别复杂的时候,可以让每一个板块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理

Vue等单页应用的优缺点

优点:Vue的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件,核心是响应式系统
缺点:不支持低版本浏览器,最低仅支持ie9;不利于SEO的优化,首页加载耗时相对偏长少量。

四、ES6

letvar的区别
  1. 不存在变量提升
  2. 暂时性的死区
if (true) {  // --- 死区开始 ---  tmp = 'abc'; // ReferenceError  console.log(tmp); // ReferenceError  let tmp;  // --- 死区结束 ---  console.log(tmp); // undefined  tmp = 123;  console.log(tmp); // 123}
  1. let为JavaScript新添加了块级作用域
var a = [];for (var i = 0; i < 10; i++) {  a[i] = function () {    console.log(i);  };}a[6](); // 10// 变量i是var命令公告的,在全局范围内都有效,所以全局只有一个变量i,每一次循环,变量i的值都会发生改变var a = [];for (let i = 0; i < 10; i++) {  a[i] = function () {    console.log(i);  };}a[6](); // 6// 变量i是let公告的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。
解构赋值

定义:从数组或者对象中提取值,对变量进行赋值

  1. 数组的解构赋值
let [a, b, c] = [1, 2, 3];let [ , , third] = ["foo", "bar", "baz"];third // "baz"let [x, , y] = [1, 2, 3];x // 1y // 3let [head, ...tail] = [1, 2, 3, 4];head // 1tail // [2, 3, 4]let [x, y, ...z] = ['a'];x // "a"y // undefinedz // []let [x, y = 10] = [7]
  1. 对象的解构赋值

    (tip: 数组的解构赋值对顺序有要求,而对象则没有要求,变量必需与属性同名,才能取到正确的值)
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };foo // "aaa"bar // "bbb"let { baz } = { foo: 'aaa', bar: 'bbb' };baz // undefinedvar {x, y = 5} = {x: 1};// 假如变量名与属性名不一致,必需写成下面这样。// foo 是模式, baz 才是变量,特别要注意区分模式和变量let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
  1. 嵌套解构赋值
let obj = {  p: [    'Hello',    { y: 'World' }  ]};let { p: [x, { y }] } = obj;x // "Hello"y // "World"// p 是模式,不会被复制,若想被赋值,可以写成如下形式let { p, p: [x, { y }] } = obj;x // "Hello"y // "World"p // ["Hello", {y: "World"}]
箭头函数和非箭头函数的区别
  1. 箭头函数的this指向在定义的时候继承自外层的第一个普通函数的this,若外层没有普通函数,指向的是window
  2. 不能直接修改箭头函数的this指向
  3. 箭头函数的this指向全局,使用arguments会报未公告的错误。
  4. 箭头函数的this指向普通函数时,它的argumens继承于该普通函数
  5. 使用new调用箭头函数会报错,由于箭头函数没有constructor
简单讲讲Promise

Promise的特点
状态一旦改变就再也不会发生改变了
Promise的缺点

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消;
  2. 假如不设置回调函数,Promise内部抛出的错误,不会反应到外部;
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
如何创立Promise实例?
const promise = new Promise(function(resolve, reject) {  if (/* 异步操作成功 */){    resolve(value);  } else {    reject(error);  }});
简单讲讲Promise API
  • Promise.resolve();
// 1. 假如传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。function fn(resolve){    setTimeout(function(){        resolve(123);    },3000);}let p0 = new Promise(fn);let p1 = Promise.resolve(p0);// 返回为true,返回的 Promise 即是 入参的 Promise 对象。console.log(p0 === p1);//2. 假如这个值是个 thenable(thenable对象指的是具备then方法的对象),返回的 Promise 对象会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled)let promise = Promise.resolve($.ajax('/test/test.json'));// => promise对象promise.then(function(value){   console.log(value);});
  • Promise.reject();
  • Promise.race
// 类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise// 任务的结果,不论这个 Promise 结果是成功还是失败。 。
  • Promise.all
// 类方法,多个 Promise 任务同时执行。// 假如一律成功执行,则以数组的方式返回所有 Promise 任务的执行结果。// 假如有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。const promises = [2, 3, 5, 7, 11, 13].map(function (id) {  return getJSON('/post/' + id + ".json");});Promise.all(promises).then(function (posts) {  // ...}).catch(function(reason){  // ...});
  • Promise.prototype.then()
  • Promise.prototype.catch() 该方法可以捕获到then链上发生的任何一个错误
  • Promise.prototype.finally() 该方法用于指定不论 Promise 对象最后状态如何,都会执行的操作
promise.then(result => {···}).catch(error => {···}).finally(() => {    // finally方法的回调函数不接受任何参数,这意味着没有办法知道,    //前面的 Promise 状态究竟是fulfilled还是rejected。这表明,    // finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。});

关于Promise,不同面试官考察的深度因人而异,反正做好手写实现Promise的准备就可

五、微信小程序

简述微信小程序原理
  1. 小程序本质就是一个单页应用,所有的页面渲染和事件解决,都在一个页面内进行,但又可以通过微信用户端调用原生的各种接口;
  2. 功能可分为渲染层webview和逻辑层appService两个部分;
    webview用来展示UI,appService有来解决业务逻辑、数据及接口调用;
    两个部分在两个进程中运行,通过系统层JSBridge实现通信,实现UI的渲染、事件的解决等。
为什么微信小程序的DOM API不完整?

由于微信小程序的渲染线程和脚本线程是分开的,脚本线程独立在 JSCore 中,没有一个完整的浏览器对象。

简单谈谈WXS?

WXS(weixin script),结合HTML,构建页面结构。

<wxs module="m1">var msg = "hello world"; module.exports.message = msg;</wxs><view>{{m1.message}}</view>
为什么setData操作会很昂贵?

频繁客户交互的效果在小程序上体现是比较卡慢的, 例如页面有 2 个元素 A 和 B,客户在 A 上做 touchmove 手势,要求 B 也跟随移动。一次 touchmove 事件的响应过程为:

a、touchmove 事件从视图层(Webview)抛到逻辑层(App Service)
b、逻辑层(App Service)解决 touchmove 事件,再通过 setData 来改变 B 的位置

一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信(JSBridge)的耗时比较大。此外 setData 渲染也会阻塞其它脚本执行,导致了整个客户交互的动画过程会有推迟。

谈谈小程序的生命周期函数

onLoad() 页面加载时触发,只会调用一次,可获取当前页面路径中的参数;
onShow() 页面显示/切入前端时触发,一般用来发送数据请求(或者者 set to foreground);
onReady() 页面首次渲染完成时触发 只会调用一次,代表页面已可和视图层进行交互(当逻辑层通知渲染层 Send Initial Data,且渲染层 First Render 之后调用);
onHide() 页面隐藏/切入后端时触发(set to background), 如底部tab切换到其余页面或者小程序切入后端等;
onUnload() 页面卸载时触发(destroy),如redirectTo或者navigateBack到其余页面时。

小程序页面有哪些传递数据的方法
  1. 使用全局变量实现数据传递
  2. 页面跳转或者重定向时,使用url带参数传递数据
  3. 使用组件模板 template传递参数
  4. 使用缓存传递参数
  5. 使用数据库传递数据
请谈谈微信小程序主要目录和文件的作用?
  • project.config.json 项目配置文件,用得最多的就是配置能否开启https校验;
  • App.js 设置少量全局的基础数据等;
  • App.json 底部tab, 标题栏, 小程序的window背景色和路由等设置;
  • App.wxss 公共样式,引入iconfont等;
  • pages 里面包含一个个具体的页面;
    • index.json (配置当前页面标题和引入组件等);
    • index.wxml (页面结构);
    • index.wxss (页面样式表);
    • index.js (页面的逻辑,请求和数据解决等);
谈谈微信小程序.wxss样式
  1. 新添加加了尺寸单位rpx(tip: rpx换算px规则是屏幕宽度/750, iphone6下,1rpx = 0.5px)
  2. 提供了全局样式(app.wxss)和局部样式(page.wxss)
  3. WXSS 仅支持部分 CSS 选择器( > :first-of-type :nth-child 均不支持)

六、HTTP协议

HTTP请求中的内容
  • 请求行 GET /images/logo.gif HTTP/1.1
  • 首部
    • 通用首部
    • 请求首部
    • 响应首部
    • 实体首部
  • 实体
HTTP状态码
  • 1**: 信息,服务器收到请求,需要请求者继续执行操作
  • 2**: 成功,操作被成功接收并解决
  • 3**: 重定向,需要进一步的操作以完成请求
    • 304 Not Modified 所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。
  • 4**: 用户端错误,请求包含语法错误或者无法完成请求
    • 400 用户端请求的语法错误,服务端无法了解
    • 403 Forbidden 服务器了解请求用户端的请求,但是拒绝执行此请求
    • 404 Not Found 服务器无法根据用户端的请求找到资源(网页)
  • 5**: 服务器错误,服务器在解决请求的过程中发生了错误
    • 500 服务器内部错误
    • 502 Bad Gateway 作为网关或者者代理商工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
RTT

往返时延,在计算机网络中它也是一个重要的性能指标,它表示从发送端发送数据开始,到发送端收到来自接收端确实认(接收端收到数据后便立即发送确认),总共经历的时延;

UDP协议

只是数据报文的搬运工,不会对报文进行拆分和拼接操作,不保证有序且不丢失的传递到对端

  • 面向无连接
  • 不可靠 -> 高效

在直播行业和即时对战游戏等实时性要求高的行业,UDP协议使用广泛

TCP协议

TCP 基本是和 UDP 反着来,建立连接断开连接都需要先需要进行握手。在传输数据的过程中,通过各种算法保证数据的可靠性,当然带来的问题就是相比 UDP 来说不那么的高效。是全双工协议。

标识符
  • URG=1:该字段为一表示本数据报的数据部分包含紧急信息,是一个高优先级数据报文,此时紧急指针有效。紧急数据肯定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部。
  • ACK=1:代表确认接受,确认号字段有效。此外,TCP 还规定在连接建立后传送的所有报文段都必需把 ACK 置为一。
  • PSH=1:该字段为一表示接收端应该立即将数据 push 给应用层,而不是等到缓冲区满后再提交。
  • RST=1:该字段为一表示当前 TCP 连接出现严重问题,可能需要重新建立 TCP 连接,也可以用于拒绝非法的报文段和拒绝连接请求。
  • SYN=1:代表请求创建连接,当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文。当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文。
  • FIN=1:代表请求释放连接。
  • seq:序列号,什么意思呢?当发送一个数据时,数据是被拆成多个数据包来发送,序列号就是对每个数据包进行编号,这样接受方才能对数据包进行再次拼接。
  • ack:这个代表下一个数据包的编号,这也就是为什么第二请求时,ack是seq + 1(tip: ack和ACK代表的意思不同)
三次握手

用户端 ------ SYN = 1, ACK = 0, seq = x ------- > 服务端

用户端 < ----- SYN = 1, ACK = 1, seq = y, ack = x + 1 -------- 服务端

用户端 ------ ACK = 1, seq = x + 1, ack = y + 1------- > 服务端

四次挥手

用户端 ------ FIN ------- > 服务端

用户端 < ----- ACK -------- 服务端

用户端 < ----- FIN -------- 服务端

用户端 ------ ACK ------- > 服务端

ARQ协议

ARQ协议其实就是超时重传机制。通过确认和超时重传机制保证数据的正确送达。

  • 中止等待ARQ协议
  • 连续ARQ协议
连续ARQ协议 - > 滑动窗口

发送端窗口包含已发送但未收到应答的数据待发送的数据

发送端窗口是由接收窗口剩余大小决定的。接收方会把当前接收窗口的剩余大小写入应答报文,发送端收到应答后根据该值和当前网络拥塞情况设置发送窗口的大小,所以发送窗口的大小是不断变化的。

滑动窗口处理了数据的丢包、顺序不对和流量控制问题

拥塞解决
  • 慢开始算法
  • 拥塞避免算法
  • 快速重传
  • 快速恢复

七、面向对象

面向对象的基本要素

继承 封装多态

JS适合用来面向对象吗?
你在工作中是如何使用面向对象思想的?

明天需要上班,后续跟进补充!

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
【系统环境|】ZORRO佐罗软件安装教程及一键新机使用方法详细简介(2023-02-10 21:56)
【系统环境|】补单系统搭建补单源码搭建(2022-05-18 11:35)
【系统环境|服务器应用】高端显卡再度登上热搜,竟然是因为“断崖式”的降价(2022-04-12 19:47)
【系统环境|软件环境】一步步教你开发、部署第一个去中心化应用 - 宠物商店(2022-03-15 15:13)
【系统环境|软件环境】循序渐进!一文学会高性能开发十大必需掌握的核心技术。(2022-03-15 15:13)
【系统环境|软件环境】Python游戏开发,pygame模块,Python实现贪吃蛇小游戏(2022-03-15 15:13)
【系统环境|软件环境】Spring Cloud Feign 记录单个服务耗时并处理 Hystrix 线程隔离模式!(2022-03-15 15:13)
【系统环境|软件环境】js数组方法全解(2022-03-15 15:12)
【系统环境|软件环境】字节二面:小伙子你来说下什么是伪共享?如何避免?(2022-03-15 15:12)
血鸟云
手机二维码手机访问领取大礼包
返回顶部