前端面试题整理

参考文章

html

html5语义化和新特性

  • 语义化让网页结构更加清晰,便于浏览器、搜索引擎的解析,便于阅读维护。javascript

  • 语义化标签php

    • Header main footer nav article aside detailes dialog
  • 加强表单css

    • Color date email number range search url time tel month
  • 新增的表单属性html

    • Placehoder required min max step autofoucs
  • 视频和音频前端

    • audio video
  • canvas/svg绘图vue

    • canvas标签是图形容器,必须使用脚原本绘制图形
    • svg是可伸缩的矢量图
  • 地理位置html5

  • 拖放Apijava

  • Web Workernode

    • 运行在后台的js脚本,独立于其余脚本,不影响页面性能
  • Web Storagereact

    • localStorage: 没有时间限制的数据存储
    • sessionStorage: 针对一个session的数据存储,当用户关闭浏览器窗口后数据会被删除
  • webSocket

浏览器的标准模式和怪异模式/怪异盒模型

  • 现代浏览器通常都有两种渲染模式 标准模式怪异模式,
  • 在标准模式下文档按照w3c的规范进行解析和渲染
  • 在怪异模式下文档按照浏览器本身的方式进行解析和渲染
  • 好比, 标准模式下盒模型的总宽度等于左右margin + 左右padding + 左右的border + 内容的宽度
  • 而在怪异模式下内容的宽度包含了padding和border
  • 在标准模式下可使用box-sizing把盒模型变成怪异盒模型

html和xhtml的区别

  • html是超文本标记语言,是一种语法比较松散的标记语言,语法要求也不严格。好比说,标签名大小写混写,属性名单双引号均可以,标签能够不闭合
  • xhtml是可扩展的标记语言,能够说是html的严格模式,好比标签名必须小写,属性必须加引号,引号必须是双引号,标签要闭合等等。

使用data-*的好处

  • data属性是h5新增的一种用于保存自定义数据的属性,老是以data-出现。
  • 好处是能够储存些不须要在浏览器上展现但又重要的信息,好比文章的ID
  • 用js能够很方便的去读写这些属性,好比dataset getAttribute setAttribute data()

canvas

canavs是h5新增一种用户绘制图形的容器标签,经过js绘制,经常使用于动画,游戏页面,数据可视化。

定位的写法

position: absolute;/*绝对定位*/
position: relative; /*相对定位*/
position: flexd; /*固定定位* / 复制代码

CSS/js放置的位置和缘由

什么是渐进式渲染

指浏览器不用等待全部页面资源都渲染好以后再呈现给用户看,而是边下载边渲染,因此用户打开一个网页的时候每每不能第一时间看到全部的内容,可是可以看到一个大概的样子,后续的内容浏览器会慢慢补上造成一个完整的页面。

  • 解决什么问题: 为了解决js加载时间的问题。
  • 怎么实现 服务器端渲染SSR,流行的vue,react都有SSR解决方案。

模板引擎

是一种将业务逻辑层和表现层分离,将规定格式的模板代码转化为业务数据的实现

meta viewport原理

前端页面有哪三层构成,分别是什么?做用是什么?

  • 结构层
    • 由html/xhtml之类的标记语言来建立,对网页内容的语义作出描述
  • 表示层
    • 由css负责建立,对网页如何显示内容作出定义
  • 行为层
    • 有JavaScript负责。对网页内容应该如何对事件作出反应

网页验证码有什么做用。

  • 区分是计算机程序仍是人,防止恶意攻击
  • 防止黑客针对某个特定用户以特定程序进行暴力破解

渐进加强和优雅降级

  • 渐进加强一开始保证最基本的功能,再改进和追加功能
  • 优雅降级一开始就构建完整的功能,在针对低版本浏览器进行兼容

区别

优雅降级从复杂开始,渐进加强则从一个基础的版本开始,并不断扩充。

DOM和BOM

  • DOM是文档对象模型,是为了操做文档出现的API,document是其中的一个对象
  • DOM是浏览器对象模型,是为了操做浏览器出现的API,window是其中的一个对象

css

解释一下"::before"和":after"中的双冒号和单冒号的区别

  • 双冒号表示伪元素,单冒号表示伪类

页面导入样式时使用link和@import的区别?

  • Link 属于 html 标签,而@import 是 CSS 中提供的

  • 在页面加载的时候,link会同时被加载,而@import引用的CSS会在页面加载完成后才会加载引用的CSS

  • @import只有在ie5以上才能够被识别,而link是html标签,不存在浏览器兼容性问题

  • link引入样式的权重大于@import的引用(@import 是将引用的样式导入到当前的页面中)

CSS 清除浮动的几种方法: clear:both;

  • 在浮动的盒子下面再放一个标签,使用 clear:both;来清除浮动

  • 使用 overflow 清除浮动, 找到浮动盒子的父元素,给父元素添加overflow:hidden;属性

  • .clearfix:after {
        content:"";
        height:0;
        line-height:0;
        display:block;
        clear:both;
        visibility:hidden;
    }
    // 兼容IE6
    .clearfix {
      zoom: 1;
    }
    复制代码

CSS 选择符有哪些?哪些属性能够继承?优先级算法如何计算? CSS3 新增伪类有那些?

1.id选择器( # myid)
2.类选择器(.myclassname)
3.标签选择器(div, h1, p)
4.相邻选择器(h1 + p)
5.子选择器(ul < li)
6.后代选择器(li a)
7.通配符选择器( * )
8.属性选择器(a[rel = "external"])
9.伪类选择器(a: hover, li: nth-child)

*   可继承: font-size font-family color, UL LI DL DD DT;
*   不可继承 :border padding margin width height ;
*   优先级就近原则,样式定义最近者为准;
*   载入样式以最后载入的定位为准;

优先级为:
!important >  id > class > tag  important 比 内联优先级高

CSS3新增伪类举例:
p:first-of-type 选择属于其父元素的首个 <p> 元素的每一个 <p> 元素。
p:last-of-type  选择属于其父元素的最后 <p> 元素的每一个 <p> 元素。
p:only-of-type  选择属于其父元素惟一的 <p> 元素的每一个 <p> 元素。
p:only-child    选择属于其父元素的惟一子元素的每一个 <p> 元素。
p:nth-child(2)  选择属于其父元素的第二个子元素的每一个 <p> 元素。
:enabled、:disabled 控制表单控件的禁用状态。
:checked,单选框或复选框被选中。
复制代码

行内元素和块级元素的具体区别是什么?行内元素的padding和margin可设置吗?

  • 块级元素(block)特性:

    • 老是独占一行,表现为另起一行开始,并且其后的元素也必须另起一行显示;
    • 宽度(width)、高度(height)、内边距(padding)和外边距(margin)均可控制;
  • 内联元素(inline)特性:

    • 和相邻的内联元素在同一行;
    • 宽度(width)、高度(height)、内边距的 top/bottom(padding-top/padding-bottom)和外边距的 top/bottom(margin-top/margin-bottom)都不可改变(也就是 padding 和 margin 的 left 和 right 是能够设置的),就是里面文字或图片的大小。

inline-block元素有哪些?

  • <input> 、<img> 、<button> 、<texterea> 、<label>。

如何垂直居中一个元素?

  • 方法一:绝对定位居中(原始版之已知元素的高宽)
.content {
    width: 200px;
    height: 200px;
    background-color: #6699ff;
    position: absolute; /*父元素须要相对定位*/
    top: 50%;
    left: 50%;
    margin-top: -100px; /*设为高度的1/2*/
    margin-left: -100px; /*设为宽度的1/2*/
}
复制代码
  • 方法二:绝对定位居中(改进版之一未知元素的高宽)
.content {
    width: 200px;
    height: 200px;
    background-color: #6699ff;
    position: absolute; /*父元素须要相对定位*/
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%); /*在水平和垂直方向上各偏移-50%*/
}
复制代码
  • 方法三:绝对定位居中(改进版之二未知元素的高宽)
.content {
    width: 200px;
    height: 200px;
    background-color: #6699ff;
    margin: auto; /*很关键的一步*/
    position: absolute; /*父元素须要相对定位*/
    left: 0;
    top: 0;
    right: 0;
    bottom: 0; /*让四个定位属性都为0*/
}
复制代码
  • 方法四:flex 布局居中
body {
  display: flex; /*设置外层盒子display为flex*/
  align-items: center; /*设置内层盒子的垂直居中*/
  justify-content: center; /*设置内层盒子的水平居中*/
  .content {
    width: 200px;
    height: 200px;
    background-color: #6699ff;
  }
}


复制代码

垂直居中img

.content {
   /* img的容器设置以下 */
   display: table-cell; 
   text-align: center;
   vertical-align: middle;
}
复制代码

BFC

  • 什么是 BFC

    BFC(Block Formatting Context)格式化上下文,是 Web 页面中盒模型布局的 CSS 渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。

  • 造成 BFC 的条件

    • 浮动元素,float 除 none 之外的值
    • 定位元素,position(absolute,fixed)
    • display 为如下其中之一的值 inline-block,table-cell,table-caption
    • overflow 除了 visible 之外的值(hidden,auto,scroll)
  • BFC 的特性

    • 内部的 Box 会在垂直方向上一个接一个的放置。
    • 垂直方向上的距离由 margin 决定
    • bfc 的区域不会与 float 的元素区域重叠。
    • 计算 bfc 的高度时,浮动元素也参与计算
    • bfc 就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。

用纯CSS建立一个三角形的原理是什么?

span {
  width: 0;
  heigh: 0;
  border-top: 40px solid transparent;
  border-left: 40px solid transparent;
  border-right: 40px solid transparent;
  border-bottom: 40px solid #ff0000;
}
复制代码

Sass、LESS 是什么?为何要使用他们?

  • 他们是CSS预处理器。是 CSS 上的一种抽象层。他们是一种特殊的语法/语言编译成 CSS。

  • 为何要使用它们?

    • 结构清晰,便于扩展。
    • 能够方便地屏蔽浏览器私有语法差别。这个不用多说,封装对浏览器语法差别的重复处理,减小无心义的机械劳动。
    • 能够轻松实现多重继承。
    • 彻底兼容 CSS 代码,能够方便地应用到老项目中。LESS 只是在 CSS 语法上作了扩展,因此老的 CSS 代码也能够与 LESS 代码一同编译。

css sprite(精灵) 是什么,有什么优缺点

  • 概念:

    将多个小图片拼接到一个图片中。经过 background-position 和元素尺寸调节须要显示的背景图案。

  • 优势:

    • 减小 HTTP 请求数,极大地提升页面加载速度。
    • 增长图片信息重复度,提升压缩比,减小图片大小。
    • 更换风格方便,只需在一张或几张图片上修改颜色或样式便可实现。
  • 缺点:

    • 图片合并麻烦。
    • 维护麻烦,修改一个图片可能须要重新布局整个图片,样式

移动端 1px 像素问题及解决办法

  • 媒体查询利用设备像素比缩放,设置小数像素;
    • 优势:简单,好理解
    • 缺点:兼容性差,目前之余IOS8+才支持,在IOS7及其如下、安卓系统都是显示0px。

IOS8+下已经支持带小数的px值,media query 对应 devicePixelRatio 有个查询值 -webkit-min-device-pixel-ratio;

.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
  .border { border: 0.5px solid #999 }
}

@media screen and (-webkit-min-device-pixel-ratio: 3) {
  .border { border: 0.333333px solid #999 }
}
复制代码
<body><div id="main" style="border: 1px solid #000000;"></div></body>
<script type="text/javascript"> if (window.devicePixelRatio && devicePixelRatio >= 2) { var main = document.getElementById('main'); main.style.border = '.5px solid #000000'; } </script>
复制代码
  • viewport + rem
    • 利用viewport + rem + js 动态的修改页面的缩放比例
    • 适合新项目,老项目可能要涉及到较多的改动。
<meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
复制代码
*var* viewport = document.querySelector("meta[name=viewport]")
if (window.devicePixelRatio == 1) {
  viewport.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no')
} 
if (window.devicePixelRatio == 2) {
  viewport.setAttribute('content', 'width=device-width, initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no')
} 
if (window.devicePixelRatio == 3) {
  viewport.setAttribute('content', 'width=device-width, initial-scale=0.333333333, maximum-scale=0.333333333, minimum-scale=0.333333333, user-scalable=no')
} 
*var* docEl = document.documentElement;
*var* fontsize = 10 * (docEl.clientWidth / 320) + 'px';
docEl.style.fontSize = fontsize;
复制代码
  • box-shadow
    • 利用阴影也能够实现,优势是没有圆角问题,缺点是颜色很差控制
div {
	-webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);
}
复制代码
  • 媒体查询 + transfrom(推荐)
/* 2倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.5);
        transform: scaleY(0.5);
    }
}
/* 3倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.33);
        transform: scaleY(0.33);
    }
}
复制代码
  • transform: scale(0.5) 方案 - 推荐: 很灵活
div {
  height:1px;
  background:#000;
  -webkit-transform: scaleY(0.5);
  -webkit-transform-origin:0 0;
  overflow: hidden;
}
复制代码
  • 其余参考

移动端 1px 像素问题及解决办法

常见的CSS布局

  • 单列布局

  • 两列自适应布局

  • 圣飞布局和双飞翼布局

  • flxd布局

CSS3 弹性盒子模型

  • 弹性盒子是 CSS3 的一种新的布局模式。

  • CSS3 弹性盒( Flexible Box 或 flexbox),是一种当页面须要适应不一样的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。

  • 引入弹性盒布局模型的目的是提供一种更加有效的方式来对一个容器中的子元素进行排列、对齐和分配空白空间。

base64:

base64 是网络上最多见的用于传输 8Bit 字节代码的编码方式之一,要求把每三个 8Bit 的字节转换为四个 6Bit 的字节,Base64 是网络上最多见的用于传输 8Bit 字节代码的编码方式之一。
通俗点讲:将资源本来二进制形式转成以 64 个字符基本单位,所组成的一串字符串。
好比一张图片转成 base64 编码后就像这样,图片直接以 base64 形式嵌入文件中(很长没截完):

- 生成 base64 编码:
图片生成 base64 能够用一些工具,如在线工具,但在项目中这样一个图片这样生成是挺繁琐。
特别说下,webpack 中的 url-loader 能够完成这个工做,能够对限制大小的图片进行 base64 的转换,很是方便。

- 优势:
base64 的图片会随着 html 或者 css 一块儿下载到浏览器,减小了请求.
可避免跨域问题

- 缺点:
老东西(低版本)的 IE 浏览器不兼容。
体积会比原来的图片大一点。
css 中过多使用 base64 图片会使得 css 过大,不利于 css 的加载。

- 适用场景:
应用于小的图片几 k 的,太大的图片会转换后的大小太大,得不偿失。
用于一些 css sprites 不利处理的小图片,如一些能够经过 background-repeat 平铺来作成背景的图片
复制代码

对偏移、卷曲(卷起)、可视的理解

偏移

offsetWidth  width + padding + border
offsetHeight height + padding + border
offsetLeft
offsetTop
offsetParent
注意:没有offsetRight和offsetBottom
************************************************************************************************

卷曲
scrollWidth  width + padding
scrollHeight  当内部的内容溢出盒子的时候, 顶边框的底部,计算到内容的底部;若是内容没有溢出盒子,计算方式为盒子内部的真实高度(边框到边框)
scrollLeft   这个scroll系列属性不是只读的
scrollTop
scroll()

此函数能够获取卷曲的高度和卷曲的宽度
function myScroll() {
  return {
   top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0,
   left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0
  };
}
滚动滚动条的时候触发事件
box(window).onscroll = function () {}

************************************************************************************************

可视
clientWidth  获取的是元素内部的真实宽度 width + padding
clientHeight 边框之间的高度
clientLeft  至关于左边框的宽度 若是元素包含了滚动条,而且滚动条显示在元素的左侧。这时,clientLeft属性会包含滚动条的宽度17px
clientTop   至关于顶边框的宽度
client()

此函数能够获取浏览器可视区域的宽高
function myClient() {
  return {
    wid: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0,
    heit: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0
  };
}
\----------------------------------------------------------------------------------------------
@offsetHeight和style.height的区别
demo.style.height只能获取行内样式,若是样式写到了其余地方,甚至根本就没写,便没法获取
style.height是字符串(并且带单位),offsetHeight是数值
demo.style.height能够设置行内样式,offsetHeight是只读属性
所以,通常用demo.offsetHeight来获取某元素的真实宽度/高度,用style.height来设置宽度/高度
\----------------------------------------------------------------------------------------------
@offsetLeft和style.left的区别
1、style.left只能获取行内样式
2、offsetLeft只读,style.left可读可写
3、offsetLeft是数值,style.left是字符串而且有单位px
4、若是没有加定位,style.left获取的数值多是无效的
5、最大区别在于offsetLeft以border左上角为基准,style.left以margin左上角为基准
\----------------------------------------------------------------------------------------------
@scrollHeight和scrollWidth
标签内部实际内容的高度/宽度
不计算边框,若是内容不超出盒子,值为盒子的宽高(不带边框)
若是内容超出了盒子,就是从顶部或左部边框内侧一直到内容a的最外部分
\----------------------------------------------------------------------------------------------
@scrollTop和scrollLeft
被卷去部分的 顶部/左侧 到可视区域 顶部/左侧 的距离
复制代码

如何解决不一样浏览器的样式兼容性问题?

  • 在肯定问题缘由和有问题的浏览器后,使用单独的样式表,仅供出现问题的浏览器加载。这种方法须要使用服务器端渲染。

  • 使用已经处理好此类问题的库,好比 Bootstrap。

  • 使用 autoprefixer 自动生成 CSS 属性前缀。

  • 使用 Reset CSS 或 Normalize.css。

如何为功能受限的浏览器提供页面? 使用什么样的技术和流程?

  • 优雅的降级:为现代浏览器构建应用,同时确保它在旧版浏览器中正常运行。

  • 渐进式加强:构建基于用户体验的应用,但在浏览器支持时添加新增功能。

  • 利用 caniuse.com 检查特性支持。

  • 使用 autoprefixer 自动生成 CSS 属性前缀。

  • 使用 Modernizr进行特性检测。

{box-sizing: border-box;}会产生怎样的效果?

  • 元素默认应用了box-sizing: content-box,元素的宽高只会决定内容(content)的大小。

  • box-sizing: border-box改变计算元素widthheight的方式,borderpadding的大小也将计算在内。

  • 元素的height = 内容(content)的高度 + 垂直方向的padding + 垂直方向border的宽度

  • 元素的width = 内容(content)的宽度 + 水平方向的padding + 水平方向border的宽度

文本超出部分显示省略号

  • 单行
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
复制代码
  • 多行
display: -webkit-box;
-webkit-box-orient: vertical;
/* 最多显示几行*/
-webkit-line-clamp: 3; 
overflow: hidden;
复制代码

JavaScript

订阅发布

观察者

cmd commonjs amd

列举 3 种强制类型转换和 2 种隐式类型转换

  • 强制(parseInt,parseFloat,Number)、隐式(+ -)

js基本数据类型、引用数据类型,null、undefined的区别

  • 基本数据类型有:String、Number、Boolean、Symbol、Null、Undefined
  • 引用类型:Object、Array、Function
  • null是空对象,undefined是未定义的值

判断Array类型、Number类型

  • typeof
  • instanceof
  • Object.prototype.toString.call()
  • Array.isArray

Object是引用类型吗?引用类型和基本类型的区别?哪一个在堆哪一个在栈

  • Object是引用类型
  • 引用类型是按引用范文,基本类型是按值访问。
  • 引用类型在栈里保存一个十六进制的空间地址,指向堆内存中的一个对象,基本数据类型存储在栈中

DOM操做Api

  • Document.getElementById, ByTagName、ByClassName\querySelector\querySelectAll
  • createElement\ createTextNode\ cloneNode\ createDocumentFragment
  • appendChild\insertChild\removeChild\replaceChild
  • setAttribute\getAttribute\dataset

this的使用场景

  • 函数有所属对象指向所属对象,没有则指向全局对象
  • new 一个对象时指向这个对象
  • call\apply\bind 改变this的指向

建立对象的方式

  • 工厂模式
function Person(name) {
	var o = new Object;
  o.age = name;
  o.sayName = function(){
    console.log(this.name);
  }
  return o;
}
// 缺点:对象没法识别
复制代码
  • 构造函数模式
function person(name) {
  this.name = name;
  // 优化点:能够把函数放到外面,这样每次建立实例的时候函数就不会被从新建立
  this.sayName = function() {
    console.log(this.name)
  }
}
// 优势:能够识别对象的类型
// 缺点:每次建立实例函数都要从新建立一遍
复制代码
  • 原型模式
function Person() {}
person.prototype.name = 'nick';
person.prototype.sayName = function(){
  console.log(this.name);
}
var person1 = new Person();
// 优势:方法和属性都共享
// 缺点:不能初始化参数

// 优化版
function Person(name) {}
Person.prototype = {
    constructor: Person,
    name: 'nick',
    getName: function () {
        console.log(this.name);
    }
};

var person1 = new Person();
// 能够经过constructor找到所属构造函数
复制代码
  • 组合模式
function Person(name) {
  this.name = name;
}
person.prototype = function() {
  console.log(this.name);
}
const person = new Person('nick');
// 优势:方法共享,变量私有。
复制代码
  • 动态原型模式
function Person(name) {
  this.name = name;
    if (typeof this.sayName != 'function') {
    	Person.prototype.sayName = function() {
    	console.log(this.name);
    }
  }
}
复制代码
  • 寄生构造函数模式
function Person(name) {
	var o = new Object;
  o.age = name;
  o.sayName = function(){
    console.log(this.name);
  }
  return o;
}

// 示例
function SpecialArray() {
    var values = new Array();
    for (var i = 0, len = arguments.length; i < len; i++) {
        values.push(arguments[i]);
    }
    values.toPipedString = function () {
        return this.join("|");
    };
    return values;
}

var colors = new SpecialArray('red', 'blue', 'green');
var colors2 = SpecialArray('red2', 'blue2', 'green2');

console.log(colors);
console.log(colors.toPipedString()); // red|blue|green

console.log(colors2);
console.log(colors2.toPipedString()); // red2|blue2|green2
// 你会发现,其实所谓的寄生构造函数模式就是比工厂模式在建立对象的时候,多使用了一个new,实际上二者的结果是同样的。
复制代码
  • 稳妥构造函数模式
function Person(name) {
	var o = new Object;
  o.sayName = function(){
    console.log(this.name);
  }
  return o;
}
// 没有公共属性
复制代码

new 操做符

  • 一、建立一个空的新对象,(如:var car1 = {});
  • 二、新对象的__proto__属性指向构造函数的原型对象。(原型.prototype)
  • 三、将构造函数的做用域赋值给新对象。(即this指向这个新对象)
  • 四、执行构造函数内部的代码,为这个新对象添加属性。
  • 五、若是该函数没有返回对象,则隐式的返回this。
// 定义构造函数
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

var car1 = new Car('Eagle', 'Talon TSi', 1993);
console.log(car1.make);
// expected output: "Eagle"
复制代码

什么是“use strict”, 好处和坏处

变量必须先声明,再使用
不能对变量执行delete操做
对象的属性名不能重复
禁用eval()
函数的arguments参数
禁用with(){}
复制代码

变量提高

  • 在当前做用域下,代码运行以前,把带var的和带function进行提早声明,
  • 带var的只声明,不定义(不赋值),带function声明加定义。
  • 函数定义三步:
    • 开辟一个堆内存,会有一个十六进制的空间地址(指正)
    • 把函数体中的代码当作字符串存储进行
    • 把空间地址(指针)赋值给函数名;
  • 特殊状况
    • 无论条件是否成立,都要进行变量提高
      • function在新的浏览器中只声明不定义,旧浏览器声明加定义
    • 变量提高只发生在等号左边
    • return以后代码不执行,但要进行变量提高
    • 变量名重复,不须要重复声明但要从新定义
    • 匿名函数不进行变量提高

做用域

  • 全局做用域:在代码中任何地方都能访问到的对象拥有全局做用域。
    • 拥有全局做用域的有 window对象的属性 定义在最外层的函数或变量 未定义直接赋值的变量
  • 局部做用域:通常在代码片断内能够访问到的,最多见的如函数内部,因此又称函数做用域
  • 块级做用域: 在任何一对{}中的定义的变量在代码块外都不可见。

堆栈内存

  • 栈是一种遵循先进后出原则的有序集合,主要用来提供JavaScript的运行环境和存储基本数据类型值
  • 堆的数据结构是一种树状结构,特色是存储的键值对(key-value)能够是无序的,主要用来存储引用数据类型值
  • 栈内存释放
    • 通常状况下,当函数执行完所造成的私有做用域会被自动释放掉,但也有特殊不销毁的状况
      • 函数内的某些内容被外部变量占用了。(闭包)
      • 全局栈内存只有在页面关闭的时候才会被释放
  • 堆内存释放
    • 让引用堆内存空间地址的变量赋值为null.
const ary = [1,2,3,4,5,6];
Math.max(1,2,3,4,5,6);
Math.max.call()

1 === '1'
1== '1'

[1] == [2]

复制代码

闭包的理解?

  • 三个特性
    • 函数嵌套函数
    • 内部函数能够访问外部函数的参数和变量
    • 参数和变量不会被垃圾机制回收
  • 优势
    • 变量长期保存在内存中
    • 避免全局变量污染
  • 缺点
    • 常驻内存,增长内存使用量
    • 使用不当会形成内存泄漏

实现跨域有多少种方式

jsonp

  • Jsonp原理利用script标签没有跨域限制的漏洞,从其余来源动态获取数据,jsonp请求须要服务器支持才能够,jsonp属于非同源
  • jsonp的优势是简单、兼容性好,缺点是只支持get请求,但可能会遭受xss攻击
  • jsonp实现的方式是经过将前端方法以参数的形式传递给服务端,服务端注入参数再返回,实现服务端向客户端通讯

cors跨资源共享

  • CORS是一个W3C标准,全称是跨域资源共享,容许浏览器向跨域服务器发送XMLHttpRequest/Fetch请求。
  • 实现通讯的关键是服务器,只要服务器实现了CORS接口,就能够跨域通讯。
res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("Content-Type", "application/json;charset=utf-8");
复制代码

node中间件代理/webpack的devServer代理

  • 接受客户端请求 => 将请求转发给服务器 => 拿到服务器的响应数据 => 将响应数据转发给客户端

nginx反向代理

  • 和Node代理同样的原理,只须要修改nginx的配置项

websocket

  • Websocket是一种双向通讯协议,创建链接以后,server端和client端都能主动向对方接收/发送数据。

postMessage

  • 主要解决页面和新窗口的数据传递,多窗口的数据传递,页面和嵌套的iframe消息传递

window.name + iframe,localtion.hash + iframe,document.domain + iframe

了解过,没用过。

浅拷贝和深拷贝区别

  • 浅拷贝

    • 浅拷贝会建立了一个新对象,这个对象有原始对象属性值的一份精确拷贝。属性值若是是基本类型则拷贝变量的值,引用类型则拷贝内存地址,若是其中一个对象改变了则会影响另外一个对象。
    • 常使用方法
    • Object.assign():将全部可枚举的值从一个或多个源对象复制到目标对象
    • ES6的展开运算符
    • Array.prototype.slice()返回数组的浅拷贝
  • 深拷贝

    • 深拷贝会拷贝基本类型的值并拷贝引用类型指向的对象。深拷贝相对于浅拷贝速度较慢而且花销较大,但拷贝后两个对象互补不影响
    • 能够用JSON.parse(JSON.stringify(object))进行深拷贝,但会有几个问题,好比不能处理正则,不能处理new Date,不能序列化函数,会忽略undefinedsymbol
  • 总结:

    • 若是基本数据,浅拷贝和深拷贝都不会改变原数据
    • 若是是引用类型,浅拷贝的对象改变会影响原对象,深拷贝则不会。
  • 实现一个深拷贝

// 简单深拷贝
const cloneDeep = (source) => {
  let target = Array.isArray(source) ? [] : {};
  for(const key in source) {
    const val = source[key];
    if(typeof val === 'object') {
      target[key] = cloneDeep(val);
    } else {
      target[key] = val;
    }
  }
  return target;
}


// 加强版的深拷贝
const isObject = (object) => {
    // 兼容数组
    return typeof object === 'object' && object != null;
}

const cloneDeep = (source) => {
    // 参数校验
    if(!isObject(source)) return source;

    let target = Array.isArray(source) ? [] : {};
    // 哈希表解决循环引用
    let hash = new WeakMap();
    hash.set(source, target);

    for (const key in source) {
        // hasOwnProperty: 检测对象自身属性中是否具备指定的属性,返回布尔值
        if (source.hasOwnProperty(key)) {
            if(typeof source[key] === 'object') {
                // 递归实现深拷贝
                target[key] = cloneDeep(source[key]);
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

var a = {
    name: "muyiy",
    book: {
        title: "You Don't Know JS",
        price: "45",
        demo: {
            demo: {
                demo: {}
            },
            demo1: {
                demo: {}
            },
            demo2: {
                demo: {}
            }
        }
    },
    a1: undefined,
    a2: null,
    a3: 123
}

var b = cloneDeep(a);
a.name = "高级前端进阶";
a.book.price = "55";
a.circleRef = a;
b.book.demo.demo.demo.a = 2;
console.log(a);
console.log(b);
复制代码

原型链、手绘原型链

原型

  • 函数都带有一个prototype属性,这是属性是指向构造函数原型对象,这个对象包含全部实例共享的属性和方法。
  • 原型对象都有一个constructor属性,这个属性指向所关联的构造函数。
  • 每一个对象都有一个__proto__属性[非标准的方法],这个属性指向构造函数的原型prototype

原型链

  • 当访问实例对象的某个属性时,会先在这个对象自己的属性上查找,若是没有找到,则会经过__proto__属性去原型上查找,若是尚未找到则会在构造函数的原型的__proto__中查找,这样一层层向上查找就会造成一个做用域链,称为原型链

为何要有原型链

  • 构造函数的全部实例均可以访问构造函数原型上的属性和方法
  • 继承,子类能够继承父类的方法

手绘原型图

![image-20191015085835130](/Users/zenghp/Library/Application Support/typora-user-images/image-20191015085835130.png)

事件冒泡/事件捕获/事件委托(事件代理)

  • 事件冒泡
    • 由里向外传播,直到document对象
  • 事件捕获
    • 由外向里传播直到最具体的元素.
  • 事件委托/事件代理
    • 事件委托/代理就是利用事件冒泡,只指定一个事件处理器就能够管理某一类型的全部事件。
    • 优势:
      • 减小大量的内存占用,事件注册。
      • 动态新增元素无需再对其绑定事件
    • 缺点:
      • 若是全部事件都用委托/代理的话会出现本不该该绑定的事件被绑上了事件
  • 阻止冒泡
event.stopPropagation()
//非标准,已废弃
event.cancelBubble=false; // IE
复制代码
  • 阻止事件的默认行为
event.preventDefault()
//非标准,已废弃
event.returnValue=false; //IE
复制代码

手写Ajax

  • promise Ajax
let getJson = url => {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open('get', url);
        xhr.onreadystatechange = () => {
            if (xhr.readyState !== 4) return;

            if (xhr.readyState === 4 && 200 === xhr.status) {
                resolve(xhr.responseText);
            } else {
                reject();
            }
        };
        xhr.send();
    });
};

getJson('/login').then(function (res) {
    return getJson('/order');
}).then(function(res){
    console.log(res);
}).catch(function (err) {
    console.log('error')
})
复制代码
  • 普通函数
let xhr = new XMLHttpRequest();
xhr.open('get', 'http://xxx.com');
xhr.onreadystatechange = () => {
  if (xhr.readyState !== 4) return;
  if (200 === xhr.status) {
    console.log(xhr.responseText)
  }
};
xhr.send();
复制代码

防抖、节流

防抖(debounce)

指的是某个函数在某段时间内,不管触发了多少次回调,都只执行最后一次。

  • 实现方案 使用定时器,函数第一次执行时设定一个定时器,以后调用时发现已经设定过定时器就清空以前的定时器,并从新设定一个新的定时器,若是存在没有被清空的定时器,当定时器计时结束后触发函数执行

  • 示例

/** * 延迟执行 * fn: 须要防抖的函数 * wait: 时间间隔 * */
function debounce(fn, wait = 50) {
    let timer = null;
    return function(...args) {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(this, args);
        }, wait)
    }
}

/** * 当即执行 * fn: 须要防抖的函数 * wait: 时间间隔 * */
function debounce(fn, wait = 50) {
    let timer = null;
    return function(...args) {
        if (timer) clearTimeout(timer);
        let callNow = !timer;
        timer = setTimeout(() => {
            timer = null;
        }, wait);
        if (callNow) fn.apply(this, args);
    }
}
复制代码

节流(throttle)

函数节流指的是某个函数在必定时间间隔内只执行一次,在这间隔内无视后来产生的函数调用请求

  • 实现方案

    • 每次执行时设定一个上次执行的时间戳,判断是否已到执行时间,若是到了就执行。

    • 设定一个定时器,若是定时器存在则直接返回,等待异步执行完毕清空定时器。

  • 示例

// 时间戳
const throttle = (fn, wait = 50) => {
  // 上一次执行 fn 的时间
  let previous = 0;
  // 将 throttle 处理结果看成函数返回
  return function(...args) {
    // 获取当前时间,转换成时间戳,单位毫秒
    let now = +new Date();
    // 将当前时间和上一次执行函数的时间进行对比
    // 大于等待时间就把 previous 设置为当前时间并执行函数 fn
    if (now - previous > wait) {
      previous = now
      fn.apply(this, args)
    }
  }
}

// 定时器版本
const throttle = function(fn, wait) {
    let timer = null;
    return function(...args) {
        if (timer) return;
        timer = setTimeout(() => {
            timer = null;
            fn.apply(this, args);
        }, wait);
    };
}
复制代码

数组去重

const ary1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
const ary2 = ary1.filter((element, index, ary) => {
  // 判断索引是否相等
  return ary.indexOf(element) === index;
}) // => [1, 2, 3, 5, 4]

const ary3 = [...new Set(arr1)]
// => [1, 2, 3, 5, 4]

const ary4 = Array.from(new Set(arr1))
// => [1, 2, 3, 5, 4]

const ary5 = arr1.reduce((prev, next) => {
    return prev.includes(next) ? prev : [...prev, next];
}, []);
复制代码

new 一个对象具体作了什么

  • 一、建立一个空的新对象,(如:var car1 = {});
  • 二、新对象的_proto_属性指向构造函数的原型对象。(原型.prototype)
  • 三、将构造函数的做用域赋值给新对象。(即this指向这个新对象)
  • 四、执行构造函数内部的代码,为这个新对象添加属性。
  • 五、若是该函数没有返回对象,则隐式的返回this。
// 定义构造函数
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

var car1 = new Car('Eagle', 'Talon TSi', 1993);
console.log(car1.make);
// expected output: "Eagle"
复制代码

call\apply\bind

  • call:使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

fun.call(thisArg[,arg1[,arg2[, ...]]])

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name); //=>"cheese"
复制代码
  • apply: 调用一个具备给定this值的函数,以及做为一个数组(或相似数组对象)提供的参数

fun.apply(thisArg, argsArray)

var numbers = [5, 6, 2, 3, 7];
// 最大值
var max = Math.max.apply(null, numbers);
console.log(max); //=> 7
// 最小值
var min = Math.min.apply(null, numbers);
console.log(min); //=> 2
复制代码
  • bind:建立一个新的函数,在bind被调用时,这个新函数的this被bind的第一个参数指定,其他的参数将做为新函数的参数供调用时使用。

function.bind(thisArg[,arg1[,arg2[, ...]]])

var module = {
  x: 42,
  getX: function() {
    return this.x;
  }
}

var unboundGetX = module.getX;
console.log(unboundGetX()); // => undefined

var boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); // => 42
复制代码

数组API

Array.from()

  • 从一个相似数组或可迭代对象中建立一个新的,浅拷贝的数组实例。
console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]

console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]
复制代码

Array.isArray()

  • 用于肯定传递的值是不是一个 Array。
Array.isArray([1, 2, 3]);  
// true
Array.isArray({foo: 123}); 
// false
Array.isArray("foobar");   
// false
Array.isArray(undefined);  
// false
复制代码

Array.prototype.concat()

  • 合并两个或多个数组。此方法不会更改现有数组,返回新数组。
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
console.log(array1.concat(array2));
// expected output: Array ["a", "b", "c", "d", "e", "f"]
复制代码

Array.prototype.every()

  • 测试一个数组内的全部元素是否都能经过某个指定函数的测试。返回布尔值。
var array1 = [1, 30, 39, 29, 10, 13];
function isBelowThreshold(currentValue) {
  return currentValue < 40;
}
console.log(array1.every(isBelowThreshold));
// expected output: true
复制代码

Array.prototype.filter()

  • 建立一个新数组, 其包含经过所提供函数实现的测试的全部元素。
var words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
复制代码

Array.prototype.some()

  • 测试数组中是否是有元素经过了被提供的函数测试。它返回的是一个Boolean类型的值。
var array = [1, 2, 3, 4, 5];

var even = function(element) {
  // checks whether an element is even
  return element % 2 === 0;
};

console.log(array.some(even));
// expected output: true
复制代码

Array.prototype.find()

  • 返回数组中知足提供的测试函数的第一个元素的值。不然返回undefined
var array1 = [5, 12, 8, 130, 44];
var found = array1.find(function(element) {
  return element > 10;
});
console.log(found);
// expected output: 12
复制代码

Array.prototype.findIndex()

  • 返回数组中知足提供的测试函数的第一个元素的索引。不然返回-1。
var array1 = [5, 12, 8, 130, 44];
function isLargeNumber(element) {
  return element > 13;
}
console.log(array1.findIndex(isLargeNumber));
// expected output: 3
复制代码

Array.prototype.forEach()

  • 对数组的每一个元素执行一次提供的函数。
var array1 = ['a', 'b', 'c'];

array1.forEach(function(element) {
  console.log(element);
});

// expected output: "a"
// expected output: "b"
// expected output: "c"
复制代码

Array.prototype.map()

  • 建立一个新数组,其结果是该数组中的每一个元素都调用一个提供的函数后返回的结果。
var array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
复制代码

Array.prototype.includes()

  • 用来判断一个数组是否包含一个指定的值,根据状况,若是包含则返回 true,不然返回false。
var array1 = [1, 2, 3];

console.log(array1.includes(2));
// expected output: true

var pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false
复制代码

Array.prototype.indexOf()

  • 返回在数组中能够找到一个给定元素的第一个索引,若是不存在,则返回-1。
var beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];

console.log(beasts.indexOf('bison'));
// expected output: 1

// start from index 2
console.log(beasts.indexOf('bison', 2));
// expected output: 4

console.log(beasts.indexOf('giraffe'));
// expected output: -1
复制代码

Array.prototype.lastIndexOf()

  • 返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,若是不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。
var animals = ['Dodo', 'Tiger', 'Penguin', 'Dodo'];

console.log(animals.lastIndexOf('Dodo'));
// expected output: 3

console.log(animals.lastIndexOf('Tiger'));
// expected output: 1
复制代码

Array.prototype.join()

  • 将一个数组(或一个类数组对象)的全部元素链接成一个字符串并返回这个字符串。若是数组只有一个项目,那么将返回该项目而不使用分隔符。
var elements = ['Fire', 'Air', 'Water'];

console.log(elements.join());
// expected output: "Fire,Air,Water"

console.log(elements.join(''));
// expected output: "FireAirWater"

console.log(elements.join('-'));
// expected output: "Fire-Air-Water"
复制代码

Array.prototype.flat()

  • 按照一个可指定的深度递归遍历数组,并将全部元素与遍历到的子数组中的元素合并为一个新数组返回。
var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity 做为深度,展开任意深度的嵌套数组
arr3.flat(Infinity); 
// [1, 2, 3, 4, 5, 6]

var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]
复制代码

Array.prototype.flatMap()

var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]); 
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

let arr = ["今每天气不错", "", "早上好"]

arr.map(s => s.split(""))
// [["今", "天", "天", "气", "不", "错"],[],["早", "上", "好"]]

arr.flatMap(s => s.split(''));
// ["今", "天", "天", "气", "不", "错", "早", "上", "好"]
复制代码

Array.prototype.keys()

  • 返回一个包含数组中每一个索引键的Array Iterator对象。
var array1 = ['a', 'b', 'c'];
var iterator = array1.keys(); 
  
for (let key of iterator) {
  console.log(key); // expected output: 0 1 2
}
复制代码

Array.prototype.values()

  • 返回一个新的Array Iterator对象,该对象包含数组每一个索引的值
const array1 = ['a', 'b', 'c'];
const iterator = array1.values();

for (const value of iterator) {
  console.log(value); // expected output: "a" "b" "c"
}
复制代码

Array.prototype.pop()

  • 从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
var plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato'];

console.log(plants.pop());
// expected output: "tomato"

console.log(plants);
// expected output: Array ["broccoli", "cauliflower", "cabbage", "kale"]

plants.pop();

console.log(plants);
// expected output: Array ["broccoli", "cauliflower", "cabbage"]
复制代码

Array.prototype.push()

  • 将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
const animals = ['pigs', 'goats', 'sheep'];

const count = animals.push('cows');
console.log(count);
// expected output: 4
console.log(animals);
// expected output: Array ["pigs", "goats", "sheep", "cows"]

animals.push('chickens', 'cats', 'dogs');
console.log(animals);
// expected output: Array ["pigs", "goats", "sheep", "cows", "chickens", "cats", "dogs"]
复制代码

Array.prototype.shift()

  • 从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
var array1 = [1, 2, 3];

var firstElement = array1.shift();

console.log(array1);
// expected output: Array [2, 3]

console.log(firstElement);
// expected output: 1
复制代码

Array.prototype.unshift()

  • 将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。
var array1 = [1, 2, 3];

console.log(array1.unshift(4, 5));
// expected output: 5

console.log(array1);
// expected output: Array [4, 5, 1, 2, 3]

复制代码

Array.prototype.reduce()

  • 对数组中的每一个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
复制代码

Array.prototype.reduceRight()

  • 接受一个函数做为累加器(accumulator)和数组的每一个值(从右到左)将其减小为单个值。
const array1 = [[0, 1], [2, 3], [4, 5]].reduceRight(
  (accumulator, currentValue) => accumulator.concat(currentValue)
);

console.log(array1);
// expected output: Array [4, 5, 2, 3, 0, 1]
复制代码

Array.prototype.reverse()

  • 将数组中元素的位置颠倒,并返回该数组。该方法会改变原数组。
var array1 = ['one', 'two', 'three'];
console.log('array1: ', array1);
// expected output: Array ['one', 'two', 'three']

var reversed = array1.reverse(); 
console.log('reversed: ', reversed);
// expected output: Array ['three', 'two', 'one']

/* Careful: reverse is destructive. It also changes the original array */ 
console.log('array1: ', array1);
// expected output: Array ['three', 'two', 'one']
复制代码

Array.prototype.slice()

  • 返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
var animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]

console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]
复制代码

Array.prototype.splice()

  • 经过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组
var months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ['Jan', 'Feb', 'March', 'April', 'June']

months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ['Jan', 'Feb', 'March', 'April', 'May']
复制代码

Array.prototype.sort()

  • 用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,而后比较它们的UTF-16代码单元值序列时构建的
var months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// expected output: Array ["Dec", "Feb", "Jan", "March"]

var array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// expected output: Array [1, 100000, 21, 30, 4]
复制代码

Array.prototype.toLocaleString()

  • 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开。
var array1 = [1, 'a', new Date('21 Dec 1997 14:12:00 UTC')];
var localeString = array1.toLocaleString('en', {timeZone: "UTC"});

console.log(localeString);
// expected output: "1,a,12/21/1997, 2:12:00 PM",
// This assumes "en" locale and UTC timezone - your results may vary
复制代码

Array.prototype.toString()

  • 返回一个字符串,表示指定的数组及其元素。
var array1 = [1, 2, 'a', '1a'];

console.log(array1.toString());
// expected output: "1,2,a,1a"
复制代码

字符串API

String.fromCharCode()

  • String.fromCharCode(num1, ..., numN)
  • 返回由指定的UTF-16代码单元序列建立的字符串。
console.log(String.fromCharCode(189, 43, 190, 61));
// expected output: "½+¾="
复制代码

String.fromCodePoint()

  • 返回使用指定的代码点序列建立的字符串。
console.log(String.fromCodePoint(9731, 9733, 9842, 0x2F804));
// expected output: "☃★♲你"
复制代码

String.prototype.charAt()

  • 从一个字符串中返回指定的字符。
var anyString = "Brave new world";
console.log("The character at index 0 is '" + anyString.charAt(0)   + "'");
console.log("The character at index 1 is '" + anyString.charAt(1)   + "'");
console.log("The character at index 2 is '" + anyString.charAt(2)   + "'");
console.log("The character at index 3 is '" + anyString.charAt(3)   + "'");
console.log("The character at index 4 is '" + anyString.charAt(4)   + "'");
console.log("The character at index 999 is '" + anyString.charAt(999) + "'");
复制代码

String.prototype.charCodeAt()

  • 返回0到65535之间的整数,表示给定索引处的UTF-16代码单元 (在 Unicode 编码单元表示一个单一的 UTF-16 编码单元的状况下,UTF-16 编码单元匹配 Unicode 编码单元。但在——例如 Unicode 编码单元 > 0x10000 的这种——不能被一个 UTF-16 编码单元单独表示的状况下,只能匹配 Unicode 代理对的第一个编码单元) 。若是你想要整个代码点的值,使用 codePointAt()。
var sentence = 'The quick brown fox jumps over the lazy dog.';
var index = 4;

console.log('The character code ' + sentence.charCodeAt(index) + ' is equal to ' + sentence.charAt(index));
// expected output: "The character code 113 is equal to q"
复制代码

String.prototype.codePointAt()

  • 返回 一个 Unicode 编码点值的非负整数。
'ABC'.codePointAt(1);          // 66
'\uD800\uDC00'.codePointAt(0); // 65536
'XYZ'.codePointAt(42); // undefined
复制代码

String.prototype.concat()

  • 将一个或多个字符串与原字符串链接合并,造成一个新的字符串并返回。
var hello = "Hello, ";
console.log(hello.concat("Kevin", " have a nice day.")); /* Hello, Kevin have a nice day. */
复制代码

String.prototype.startsWith()

  • 用来判断当前字符串是否以另一个给定的子字符串开头,并根据判断结果返回 true 或 false。
const str1 = 'Saturday night plans';

console.log(str1.startsWith('Sat'));
// expected output: true

console.log(str1.startsWith('Sat', 3));
// expected output: false

复制代码

String.prototype.endsWith()

  • 用来判断当前字符串是不是以另一个给定的子字符串“结尾”的,根据判断结果返回 true 或 false。
const str1 = 'Cats are the best!';
console.log(str1.endsWith('best', 17));
// expected output: true
const str2 = 'Is this a question';
console.log(str2.endsWith('?'));
// expected output: false
复制代码

String.prototype.includes()

  • 用于判断一个字符串是否包含在另外一个字符串中,根据状况返回 true 或 false。
var str = 'To be, or not to be, that is the question.';

console.log(str.includes('To be'));       // true
console.log(str.includes('question'));    // true
console.log(str.includes('nonexistent')); // false
console.log(str.includes('To be', 1));    // false
console.log(str.includes('TO BE'));       // false
复制代码

String.prototype.indexOf()

  • 返回调用它的 String 对象中第一次出现的指定值的索引,从 fromIndex 处进行搜索。若是未找到该值,则返回 -1。
var paragraph = 'The quick brown fox jumps over the lazy dog. If the dog barked, was it really lazy?';
var searchTerm = 'dog';
var indexOfFirst = paragraph.indexOf(searchTerm);

console.log('The index of the first "' + searchTerm + '" from the beginning is ' + indexOfFirst);
// expected output: "The index of the first "dog" from the beginning is 40"
console.log('The index of the 2nd "' + searchTerm + '" is ' + paragraph.indexOf(searchTerm, (indexOfFirst + 1)));
// expected output: "The index of the 2nd "dog" is 52"
复制代码

String.prototype.lastIndexOf()

  • 返回指定值在调用该方法的字符串中最后出现的位置,若是没找到则返回 -1。length为须要检索字符串的长度,默认值为str.length。
"canal".lastIndexOf("a")   // returns 3
"canal".lastIndexOf("a",2) // returns 1
"canal".lastIndexOf("a",0) // returns -1
"canal".lastIndexOf("x")   // returns -1

// 区分大小写
"Blue Whale, Killer Whale".lastIndexOf("blue"); // returns -1
复制代码

String.prototype.localeCompare()

  • 返回一个数字来指示一个参考字符串是否在排序顺序前面或以后或与给定字符串相同。
// The letter "a" is before "c" yielding a negative value
'a'.localeCompare('c'); 
// -2 or -1 (or some other negative value)

// Alphabetically the word "check" comes after "against" yielding a positive value
'check'.localeCompare('against'); 
// 2 or 1 (or some other positive value)

// "a" and "a" are equivalent yielding a neutral value of zero
'a'.localeCompare('a'); 
// 0
复制代码

String.prototype.match()

  • 检索返回一个字符串匹配正则表达式的的结果。
var str = 'For more information, see Chapter 3.4.5.1';
var re = /see (chapter \d+(\.\d)*)/i;
var found = str.match(re);

console.log(found);

// logs [ 'see Chapter 3.4.5.1',
// 'Chapter 3.4.5.1',
// '.1',
// index: 22,
// input: 'For more information, see Chapter 3.4.5.1' ]

// 'see Chapter 3.4.5.1' 是整个匹配。
// 'Chapter 3.4.5.1' 被'(chapter \d+(\.\d)*)'捕获。
// '.1' 是被'(\.\d)'捕获的最后一个值。
// 'index' 属性(22) 是整个匹配从零开始的索引。
// 'input' 属性是被解析的原始字符串。

var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var regexp = /[A-E]/gi;
var matches_array = str.match(regexp);

console.log(matches_array);
// ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']
复制代码

String.prototype.matchAll()

  • 返回一个包含全部匹配正则表达式及分组捕获结果的迭代器。
const regexp = RegExp('foo*','g');
const str = 'table football, foosball';

while ((matches = regexp.exec(str)) !== null) {
  console.log(`Found ${matches[0]}. Next starts at ${regexp.lastIndex}.`);
  // expected output: "Found foo. Next starts at 9."
  // expected output: "Found foo. Next starts at 19."
}


//===

const regexp = RegExp('foo*','g'); 
const str = 'table football, foosball';
let matches = str.matchAll(regexp);

for (const match of matches) {
  console.log(match);
}
// Array [ "foo" ]
// Array [ "foo" ]

// matches iterator is exhausted after the for..of iteration
// Call matchAll again to create a new iterator
matches = str.matchAll(regexp);

Array.from(matches, m => m[0]);
// Array [ "foo", "foo" ]
复制代码

String.prototype.normalize()

  • 按照指定的一种 Unicode 正规形式将当前字符串正规化.
// Initial string

// U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
// U+0323: COMBINING DOT BELOW
var str = "\u1E9B\u0323";


// Canonically-composed form (NFC)

// U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
// U+0323: COMBINING DOT BELOW
str.normalize("NFC"); // "\u1E9B\u0323"
str.normalize(); // same as above


// Canonically-decomposed form (NFD)

// U+017F: LATIN SMALL LETTER LONG S
// U+0323: COMBINING DOT BELOW
// U+0307: COMBINING DOT ABOVE
str.normalize("NFD"); // "\u017F\u0323\u0307"


// Compatibly-composed (NFKC)

// U+1E69: LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
str.normalize("NFKC"); // "\u1E69"


// Compatibly-decomposed (NFKD)

// U+0073: LATIN SMALL LETTER S
// U+0323: COMBINING DOT BELOW
// U+0307: COMBINING DOT ABOVE
str.normalize("NFKD"); // "\u0073\u0323\u0307"
复制代码

String.prototype.padEnd()

  • 会用一个字符串填充当前字符串(若是须要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。
'abc'.padEnd(10);          // "abc "
'abc'.padEnd(10, "foo");   // "abcfoofoof"
'abc'.padEnd(6, "123456"); // "abc123"
'abc'.padEnd(1);           // "abc"
复制代码

String.prototype.padStart()

  • 用另外一个字符串填充当前字符串(重复,若是须要的话),以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。
'abc'.padStart(10);         // " abc"
'abc'.padStart(10, "foo");  // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0");     // "00000abc"
'abc'.padStart(1);          // "abc"
复制代码

String.prototype.repeat()

  • 构造并返回一个新字符串,该字符串包含被链接在一块儿的指定数量的字符串的副本。
"abc".repeat(-1)     // RangeError: repeat count must be positive and less than inifinity
"abc".repeat(0)      // ""
"abc".repeat(1)      // "abc"
"abc".repeat(2)      // "abcabc"
"abc".repeat(3.5)    // "abcabcabc" 参数count将会被自动转换成整数.
"abc".repeat(1/0)    // RangeError: repeat count must be positive and less than inifinity

({toString : () => "abc", repeat : String.prototype.repeat}).repeat(2)   
//"abcabc",repeat是一个通用方法,也就是它的调用者能够不是一个字符串对象.
复制代码

String.prototype.replace()

  • 返回一个由替换值(replacement)替换一些或全部匹配的模式(pattern)后的新字符串。模式能够是一个字符串或者一个正则表达式,替换值能够是一个字符串或者一个每次匹配都要调用的回调函数。
var p = 'The quick brown fox jumps over the lazy dog. If the dog reacted, was it really lazy?';
var regex = /dog/gi;

console.log(p.replace(regex, 'ferret'));
// expected output: "The quick brown fox jumps over the lazy ferret. If the ferret reacted, was it really lazy?"

console.log(p.replace('dog', 'monkey'));
// expected output: "The quick brown fox jumps over the lazy monkey. If the dog reacted, was it really lazy?"
复制代码

String.prototype.search()

  • 执行正则表达式和 String 对象之间的一个搜索匹配。
var paragraph = 'The quick brown fox jumps over the lazy dog. If the dog barked, was it really lazy?';
// any character that is not a word character or whitespace
var regex = /[^\w\s]/g;

console.log(paragraph.search(regex));
// expected output: 43

console.log(paragraph[paragraph.search(regex)]);
// expected output: "."
复制代码

String.prototype.slice()

  • 提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。
var str = 'The quick brown fox jumps over the lazy dog.';

console.log(str.slice(31));
// expected output: "the lazy dog."

console.log(str.slice(4, 19));
// expected output: "quick brown fox"

console.log(str.slice(-4));
// expected output: "dog."

console.log(str.slice(-9, -5));
// expected output: "lazy"
复制代码

String.prototype.split()

  • 使用指定的分隔符字符串将一个String对象分割成字符串数组,以将字符串分隔为子字符串,以肯定每一个拆分的位置。
var str = 'The quick brown fox jumps over the lazy dog.';

var words = str.split(' ');
console.log(words[3]);
// expected output: "fox"

var chars = str.split('');
console.log(chars[8]);
// expected output: "k"

var strCopy = str.split();
console.log(strCopy);
// expected output: Array ["The quick brown fox jumps over the lazy dog."]
复制代码

String.prototype.substring()

  • 返回一个字符串在开始索引到结束索引之间的一个子集, 或从开始索引直到字符串的末尾的一个子集。
var anyString = "Mozilla";

// 输出 "Moz"
console.log(anyString.substring(0,3));
console.log(anyString.substring(3,0));
console.log(anyString.substring(3,-3));
console.log(anyString.substring(3,NaN));
console.log(anyString.substring(-2,3));
console.log(anyString.substring(NaN,3));

// 输出 "lla"
console.log(anyString.substring(4,7));
console.log(anyString.substring(7,4));

// 输出 ""
console.log(anyString.substring(4,4));

// 输出 "Mozill"
console.log(anyString.substring(0,6));

// 输出 "Mozilla"
console.log(anyString.substring(0,7));
console.log(anyString.substring(0,10));
复制代码

String.prototype.toLocaleLowerCase()

  • 根据任何特定于语言环境的案例映射,返回调用字符串值转换为小写的值
console.log('ALPHABET'.toLocaleLowerCase()); 
// 'alphabet'

console.log('中文简体 zh-CN || zh-Hans'.toLocaleLowerCase());
// '中文简体 zh-cn || zh-hans'
复制代码

String.prototype.toLocaleUpperCase()

  • 本地化(locale-specific)的大小写映射规则将输入的字符串转化成大写形式并返回结果字符串。
console.log('alphabet'.toLocaleUpperCase()); // 'ALPHABET'
复制代码

String.prototype.toLowerCase()

  • 将调用该方法的字符串值转为小写形式,并返回。
console.log('中文简体 zh-CN || zh-Hans'.toLowerCase());
// 中文简体 zh-cn || zh-hansconsole.log( "ALPHABET".toLowerCase() ); 
// "alphabet"
复制代码

String.prototype.toUpperCase()

  • 调用该方法的字符串值转换为大写形式,并返回。
console.log( "alphabet".toUpperCase() ); // "ALPHABET"
复制代码

String.prototype.toString()

  • 返回指定对象的字符串形式。
var x = new String("Hello world");
alert(x.toString())      // 输出 "Hello world"
复制代码

String.prototype.trim()

  • 会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是全部的空白字符 (space, tab, no-break space 等) 以及全部行终止符字符(如 LF,CR)。
var orig = ' foo ';
console.log(orig.trim()); // 'foo'

// 另外一个.trim()例子,只从一边删除

var orig = 'foo ';
console.log(orig.trim()); // 'foo'
复制代码

String.prototype.trimRight()/trimEnd()

  • 从一个字符串的右端移除空白字符。trimRight()trimEnd()方法的别名。
var greeting = ' Hello world! ';

console.log(greeting);
// expected output: " Hello world! ";

console.log(greeting.trimEnd());
console.log(greeting.trimRight());
// expected output: " Hello world!";
复制代码

String.prototype.trimLeft()/trimStart()

  • 从字符串的开头删除空格。trimLeft()是trimStart()方法的别名。
var str = " foo ";

alert(str.length); // 8

str = str.trimLeft();
alert(str.length); // 5
document.write( str );
复制代码

String.prototype.valueOf()

  • 返回一个String对象的原始值(primitive value)。
x = new String("Hello world");
alert(x.valueOf())          // Displays "Hello world"
复制代码

String.raw()

  • 是一个模板字符串的标签函数,它的做用相似于 Python 中的字符串前缀 r 和 C# 中的字符串前缀 @(仍是有点区别的,详见隔壁 Chromium 那边的这个 issue),是用来获取一个模板字符串的原始字符串的,好比说,占位符(例如 ${foo})会被处理为它所表明的其余字符串,而转义字符(例如 \n)不会。
String.raw`Hi\n${2+3}!`;
// 'Hi\n5!',Hi 后面的字符不是换行符,\ 和 n 是两个不一样的字符

String.raw `Hi\u000A!`;             
// "Hi\\u000A!",同上,这里获得的会是 \、u、0、0、0、A 6个字符,
// 任何类型的转义形式都会失效,保留原样输出,不信你试试.length

let name = "Bob";
String.raw `Hi\n${name}!`;             
// "Hi\nBob!",内插表达式还能够正常运行


// 正常状况下,你也许不须要将 String.raw() 看成函数调用。
// 可是为了模拟 `t${0}e${1}s${2}t` 你能够这样作:
String.raw({ raw: 'test' }, 0, 1, 2); // 't0e1s2t'
// 注意这个测试, 传入一个 string, 和一个相似数组的对象
// 下面这个函数和 `foo${2 + 3}bar${'Java' + 'Script'}baz` 是相等的.
String.raw({
  raw: ['foo', 'bar', 'baz'] 
}, 2 + 3, 'Java' + 'Script'); // 'foo5barJavaScriptbaz'
复制代码

ES6

ES6的新特性

  • let关键字,用于声明只在块级做用域起做用的变量
  • const关键字,用于声明常量
  • 结构赋值,一种新的变量赋值方式
  • Symbol数据类型,定义一个独一无二的值
  • for...of遍历器,可遍历具备iterator(迭代器)接口的数据类型
  • Set结构,用于存储不重复成员值的集合
  • Map结构
  • Promise对象,一种异步操做的解决方案,更合理、规范的操做异步处理。
  • class类,定义类和更简便的实现类的继承
  • 模板字符串
  • 函数参数默认值
  • fetch

var、let、const的区别

  • var在任何语句执行前就完成了声明和初始化
  • funciton声明、初始化和赋值在一开始就所有完成,函数变量提高的优先级更高
  • let先声明但并无初始化,只有解析到let那一行才会进入初始化阶段。若是在此做用域前提早访问,则报错xx is not defined , 暂时性死区,在相同做用域内不容许重复声明同一个变量
  • constlet区别在于const声明的是一个只读常亮,一旦声明后就不能改变

let const 优势

  • 不会变量提高
  • 变量不能重复声明
  • 有块级做用域

es6 generator 是什么,async/await 实现原理

ES6和node的commonjs模块化规范区别

ES6 都有什么 Iterator 遍历器

一、遍历器(Iterator)是一种接口,为各类不一样的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就能够完成遍历操做(即依次处理该数据结构的全部成员)

二、Iterator 的做用有三个:

  • 一是为各类数据结构,提供一个统一的、简便的访问接口;
  • 二是使得数据结构的成员可以按某种次序排列;
  • 三是 ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费。

三、默认部署了 Iterator 的数据有 Array、Map、Set、String、TypedArray、arguments、NodeList 对象,ES6 中有的是 Set、Map、

ES6 中类的定义

// 一、类的基本定义
class Parent {
  constructor(name = "小白") {
    this.name = name;
  }
}
//=====================================
// 二、生成一个实例
let g_parent = new Parent();
console.log(g_parent); //{name: "小白"}
let v_parent = new Parent("v"); // 'v'就是构造函数name属性 , 覆盖构造函数的name属性值
console.log(v_parent); // {name: "v"}

//=====================================
// 三、继承
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }
}

class Child extends Parent {}
console.log("继承", new Child()); // 继承 {name: "小白"}
//=====================================
// 四、继承传递参数
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }
}
class Child extends Parent {
  constructor(name = "child") {
    // 子类重写name属性值
    super(name); // 子类向父类修改 super必定放第一行
    this.type = "preson";
  }
}
console.log("继承", new Child("hello")); // 带参数覆盖默认值 继承{name: "hello", type: "preson"}
//=====================================
// 五、ES6从新定义的ES5中的访问器属性
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }
  get longName() {
    // 属性
    return "mk" + this.name;
  }
  set longName(value) {
    this.name = value;
  }
}

let v = new Parent();
console.log("getter", v.longName); // getter mk小白

v.longName = "hello";
console.log("setter", v.longName); // setter mkhello

//=====================================
// 六、类的静态方法
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }

  static tell() {
    // 静态方法:经过类去调用,而不是实例
    console.log("tell");
  }
}

Parent.tell(); // tell

//=====================================
// 七、类的静态属性:
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }
  static tell() {
    // 静态方法:经过类去调用,而不是实例
    console.log("tell"); // tell
  }
}

Parent.type = "test"; // 定义静态属性
console.log("静态属性", Parent.type); // 静态属性 test
let v_parent = new Parent();
console.log(v_parent); // {name: "小白"} 没有tell方法和type属性
复制代码

谈谈你对 ES6 的理解

es6 是一个新的标准,它包含了许多新的语言特性和库,是 JS 最实质性的一次升级。

好比'箭头函数'、'字符串模板'、'generators(生成器)'、'async/await'、'解构赋值'、'class'等等,还有就是引入 module 模块的概念。

箭头函数可让 this 指向固定化,这种特性颇有利于封装回调函数

  • (1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
  • (2)不能够看成构造函数,也就是说,不可使用 new 命令,不然会抛出一个错误。
  • (3)不可使用 arguments 对象,该对象在函数体内不存在。若是要用,能够用 Rest 参数代替。
  • (4)不可使用 yield 命令,所以箭头函数不能用做 Generator 函数。
  • async/await 是写异步代码的新方式,之前的方法有回调函数和 Promise。
  • async/await 是基于 Promise 实现的,它不能用于普通的回调函数。async/await 与 Promise 同样,是非阻塞的。
  • async/await 使得异步代码看起来像同步代码,这正是它的魔力所在。

promise的原理和实现

  • promise是同步执行的,promise.then是异步的
  • promise有三种状态。pending、 fulfilled、rejected,状态一旦改变就不会在变
  • resolve/reject屡次调用,只有第一次有效
  • promise每次调用都会返回一个新的promise,从而实现链式调用

解构赋值及其原理

  • ES6容许按照必定的规则,从数组或对象提取值,按照对应的位置对变量进行赋值

箭头函数和普通函数的区别

  • 箭头函数的语法定义要比普通函数简洁,清晰。
  • 函数体内的this对象是定义是所在的对象,而不是使用时所在的对象
  • 不能够当构造函数,也就是说不能用new命令
  • 函数体内不存在arguments对象。可使用参数代替
  • 不可使用yield命令。

es6的继承和es5的继承有什么区别

  • ES5的继承是经过原型或者构造函数实现的。
  • ES6封装了class 用Extends来继承

promise封装ajax

function postJson(url, data) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url);
    xhr.onreadystatuschange = () => {
      if(xhr.readyState !== 4) return;
      if(xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject('请求失败')
      }
    }
    xhr.send(JSON.stringify(data));
  })
}
复制代码

Vue全家桶

Vue

vue中的key的做用

  • 带key

key给每一个vnode(虚拟节点)增长一个惟一ID,在更新组件是判断两个节点是否相同。相同就复用,不相同就删除旧建立新的。

虽然带上key会增长开销,可是对于用户来讲基本感觉不到差距,并且能保证组件状态正确。

  • 不带key

不带key时节点可以复用,省去了销毁/建立组件的开销,同时只须要修改DOM文本内容而不是移除/添加节点。

这种模式diff速度可能会更快,但也会带来一些隐藏的反作用,好比可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。 这种模式只适用于渲染简单的无状态组件

vue双向数据绑定原理

vue.js 是采用数据劫持结合发布者-订阅者模式的方式,经过Object.defineProperty()来劫持各个属性的setter,getter,在数据变更时发布消息给订阅者,触发相应的监听回调。

具体步骤:

  • 第一步:须要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化

  • 第二步:compile解析模板指令,将模板中的变量替换成数据,而后初始化渲染页面视图,并将每一个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变更,收到通知,更新视图

  • 第三步:Watcher订阅者是ObserverCompile之间通讯的桥梁,主要作的事情是:

    • 一、在自身实例化时往属性订阅器(dep)里面添加本身
    • 二、自身必须有一个update()方法
    • 三、待属性变更dep.notice()通知时,能调用自身的 update() 方法,并触发Compile中绑定的回调,则功成身退。
  • 第四步:MVVM做为数据绑定的入口,整合Observer、Compile和Watcher三者,经过Observer来监听本身的model数据变化,经过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通讯桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变动的双向绑定效果。

Vue的生命周期

Vue生命周期一共分为8个阶段

  • 建立前/后
    • beforeCreated阶段,实例的$eldatamethods都未初始化。this指向建立的实例
    • created阶段,实例的datamethods都初始化完成,但 DOM节点还未挂载,不能访问到$el属性,$ref属性为空数组
  • 载入前/后
    • beforeMount阶段,实例找到相对应的template并编译成render函数
    • mouted阶段,实例将el挂载到DOM上,能够获取到DOM节点,$ref属性能够访问
  • 更新前/后
    • data变化时,会触发beforeUpdateupdated
  • 销毁前/后
    • 执行destroy方法后,data的改变不会再触发周期函数,此时Vue实例已经解除了事件监听DOM绑定,但DOM结构依然存在。

Vue 实例生命周期

Vue组件间的参数传递

  • 父传子,父组件经过自定义绑定值,子组件经过props接收数据。
  • 子传父,父组件经过on自定义事件绑定方法来接收值,子组件经过$emit触发自定义事件传递参数给父组件
  • 非父子组件间的数据传递,兄弟组件传值,用EventBus建立一个事件中心,用来传递/接收事件;
  • Vuex 状态管理

keep-alive的做用是什么

  • 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免从新渲染。

Vue性能优化

  • 打包优化
    • 小图片转base64

    • 静态下图片使用雪碧图

    • 生成环境屏蔽sourceMap

    • 开启gzip压缩文件,减小文件大小

    • 第三方依赖按需加载

    • vue-router懒加载/异步路由

  • 代码优化

    • 模板里不写过多表达式
    • 遍历渲染列表数据时,为每一项设置惟一的key值
    • 切换频繁使用v-show,不频繁使用v-if
    • 在适当的环境中使用keep-alive组件,用来缓存组件,避免屡次加载相应的组件,减小性能消耗。(tab切换)
    • 利用Object.freeze()提高性能
  • 用户体验优化
    • 移动端项目,click事件有300ms延迟,应使用touch事件
    • 资源加载完成前可使用loading动画
    • 首屏加载资源较多,出现闪屏/白屏的状况,可使用骨架屏

Vuex

什么是Vuex

  • Vuex是一个专为Vue服务,用于管理页面数据状态、提供统一数据操做的生态系统。

Vuex 原理

  • vuex 仅仅是做为 vue 的一个插件而存在,不像 Redux,MobX 等库能够应用于全部框架,vuex 只能使用在 vue 上,很大的程度是由于其高度依赖于 vue 的 computed 依赖检测系统以及其插件系统,
  • vuex 总体思想诞生于 flux,可其的实现方式完彻底全的使用了 vue 自身的响应式设计,依赖监听、依赖收集都属于 vue 对对象 Property set get 方法的代理劫持。最后一句话结束 vuex 工做原理,vuex 中的 store 本质就是没有 template 的隐藏着的 vue 组件;

Vuex的特性

  • 每个Vuex应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。
  • Vuex 和单纯的全局对象有如下两点不一样:
    • Vuex 的状态存储是响应式的。当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地获得高效更新。
    • 你不能直接改变store中的状态。改变store中的状态的惟一途径就是显式地提交(commit)mutation。这样使得咱们能够方便地跟踪每个状态的变化,从而让咱们可以实现一些工具帮助咱们更好地了解咱们的应用。

Vuex有哪些属性

  • Vuex有五种属性,分别是StateGetterMutationActionModule

Vuex的state特性

  • state为单一状态数,是数据源存放的地方(对应Vue对象里的data),存放的数据是响应式的,Vue组件从store读取数据,当store中的数据发生变化则依赖数据的组件也会发生变化。
  • 当一个组件须要获取多个状态时,可使用mapState辅助函数把state映射到组件的computed计算属性上

Vuex的getter特性

  • 当须要从store的state中派生出一些状态时,就须要使用getter.
  • getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被从新计算。

Vuex的mutation特性

  • 更改Store中状态的惟一方式就是提交mutation。
  • mutation很是相似于事件
  • 每一个mutation都有一个事件类型(type)和回调函数(handler)
  • 要执行这个回调函数须要调用store.commit

Vuex的action特性

  • action相似于mutation,不一样在于action提交的是mutation,而不能直接改变状态。
  • action能够包含任何异步操做
  • 若是须要调用action,则需经过store.dispatch触发

Vuex的module特性

  • 使用单一状态树,致使应用的全部状态集中到一个很大的对象,当应用变得很大时,store 对象会变得臃肿不堪。
  • 经过分割模块,每一个模块拥有本身的 state、mutation、action、getters、甚至是嵌套子模块

ajax请求

  • 若是请求回来的数据若是被其余组件复用则将请求放入action中,包装成promise返回。
  • 若是不须要被其余组件复用则能够放在组件内部。

不用Vuex会带来什么问题

  • 可维护性会降低,你要想修改数据,你得维护三个地方(父组件属性->子组件props->子组件))
  • 可读性会降低,由于一个组件里的数据,你根本就看不出来是从哪来的
  • 增长耦合,大量的上传派发,会让耦合性大大的增长,原本Vue用Component就是为了减小耦合,如今这么用,和组件化的初衷相背。
  • 兄弟组件有大量通讯的,建议必定要用,无论大项目和小项目,由于这样会省不少事

VueRouter

懒加载/按需加载路由

  • 异步组件

    • 组件生成一个js文件
    • component: resolve => require(['../components/PromiseDemo'], resolve)
  • import

    • const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
    • component: ImportFuncDemo2
  • webpackrequire.ensure()

    • 多个路由指定相同的chunkName,会合并打包成一个js文件
    • component: resolve => require.ensure([], () => resolve(require('../components/PromiseDemo')), 'demo')
  • 参考:vue项目按需加载

路由之间跳转

  • 使用router-link 建立a标签来定义导航连接
  • 编程式的导航 router.push router.push({ path: 'home' })

导航守卫

  • 全局守卫

    • 全局前置守卫 router.beforeEach
    • 全局解析守卫 router.beforeResolve (2.5.0 新增)
    • 全局后置守卫 router.afterEach
  • 路由独享守卫

    • beforeEnter
  • 组件守卫

    • beforeRouteEnter
    • beforeRouteUpdate (2.2 新增)
    • beforeRouteLeave

怎么定义 vue-router 的动态路由? 怎么获取传过来的值

  • 在 router 目录下的 index.js 文件中,对 path 属性加上 /:id。
  • 使用 router 对象的 params.id 获取

vue-router模式

  • hash
  • history

VueCli

重要 未写

Vue源码

项目中遇到的问题

微信小程序

1. 简单描述下微信小程序的相关文件类型

微信小程序项目结构主要有四个文件类型

  • WXML: WeiXin Markup Language是框架设计的一套标签语言,结合基础组件、事件系统,能够构建出页面的结构。内部主要是微信本身定义的一套组件
  • WXSS: WeiXin Style Sheets 是一套样式语言,用于描述 WXML 的组件样式
  • js: 逻辑处理,网络请求
  • json: 小程序设置,如页面注册,页面标题及tabBar

主要文件

  • app.json 必需要有这个文件,若是没有这个文件,项目没法运行,由于微信框架把这个做为配置文件入口,整个小程序的全局配置。包括页面注册,网络设置,以及小程序的 window 背景色,配置导航条样式,配置默认标题
  • app.js 必需要有这个文件,没有也是会报错!可是这个文件建立一下就行 什么都不须要写之后咱们能够在这个文件中监听并处理小程序的生命周期函数、声明全局变量
  • app.wxss 可选

2. 简述微信小程序原理

微信小程序采用 JavaScriptWXMLWXSS 三种技术进行开发,本质就是一个单页面应用,全部的页面渲染和事件处理,都在一个页面内进行,但又能够经过微信客户端调用原生的各类接口

微信的架构,是数据驱动的架构模式,它的 UI 和数据是分离的,全部的页面更新,都须要经过对数据的更改来实现

小程序分为两个部分 webviewappService 。其中 webview 主要用来展示 UIappService 有来处理业务逻辑、数据及接口调用。它们在两个进程中运行,经过系统层 JSBridge 实现通讯,实现 UI 的渲染、事件的处理

3. 小程序的双向绑定和vue哪里不同

小程序直接 this.data 的属性是不能够同步到视图的,必须调用:

this.setData({
    // 这里设置
})
复制代码

4. 小程序的wxss和css有哪些不同的地方

WXSSCSS 相似,不过在 CSS 的基础上作了一些补充和修改

  • 尺寸单位 rpx

rpx 是响应式像素,能够根据屏幕宽度进行自适应。规定屏幕宽为 750rpx。如在 iPhone6 上,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素

  • 使用 @import 标识符来导入外联样式。@import 后跟须要导入的外联样式表的相对路径,用;表示语句结束
/** index.wxss **/
@import './base.wxss';

.container{
    color: red;
}
复制代码

5. 小程序页面间有哪些传递数据的方法

  • 使用全局变量实现数据传递 在 app.js 文件中定义全局变量 globalData, 将须要存储的信息存放在里面
// app.js

App({
     // 全局变量
  globalData: {
    userInfo: null
  }
})
复制代码

使用的时候,直接使用 getApp() 拿到存储的信息

  • 使用 wx.navigateTowx.redirectTo 的时候,能够将部分数据放在 url 里面,并在新页面 onLoad 的时候初始化
//pageA.js

// Navigate
wx.navigateTo({
  url: '../pageD/pageD?name=raymond&gender=male',
})

// Redirect
wx.redirectTo({
  url: '../pageD/pageD?name=raymond&gender=male',
})


// pageB.js
...
Page({
  onLoad: function(option){
    console.log(option.name + 'is' + option.gender)
    this.setData({
      option: option
    })
  }
})
复制代码

须要注意的问题:

wx.navigateTowx.redirectTo 不容许跳转到 tab 所包含的页面

onLoad 只执行一次

  • 使用本地缓存 Storage 相关

6. 小程序的生命周期函数

  • onLoad 页面加载时触发。一个页面只会调用一次,能够在 onLoad 的参数中获取打开当前页面路径中的参数
  • onShow() 页面显示/切入前台时触发
  • onReady() 页面初次渲染完成时触发。一个页面只会调用一次,表明页面已经准备稳当,能够和视图层进行交互
  • onHide() 页面隐藏/切入后台时触发。 如 navigateTo 或底部 tab 切换到其余页面,小程序切入后台等
  • onUnload() 页面卸载时触发。如 redirectTonavigateBack 到其余页面时

详见 生命周期回调函数

. 怎么封装微信小程序的数据请求

wx.request({
  url: 'test.php', //仅为示例,并不是真实的接口地址
  data: {
     x: '' ,
     y: ''
  },
  success: function(res) {
    console.log(res.data)
  }
})
// promise

复制代码

参考 这里

8. 哪些方法能够用来提升微信小程序的应用速度

一、提升页面加载速度

二、用户行为预测

三、减小默认 data 的大小

四、组件化方案

9. 微信小程序的优劣势

优点

  • 即用即走,不用安装,省流量,省安装时间,不占用桌面
  • 依托微信流量,天生推广传播优点
  • 开发成本比 App

缺点

  • 用户留存,即用即走是优点,也存在一些问题
  • 入口相对传统 App 要深不少
  • 限制较多,页面大小不能超过2M。不能打开超过10个层级的页面

10. 怎么解决小程序的异步请求问题

小程序支持大部分 ES6 语法

  • 在返回成功的回调里面处理逻辑
  • Promise 异步

11. 小程序关联微信公众号如何肯定用户的惟一性

若是开发者拥有多个移动应用、网站应用、和公众账号(包括小程序),可经过 unionid来区分用户的惟一性,由于只要是同一个微信开放平台账号下的移动应用、网站应用和公众账号(包括小程序),用户的 unionid 是惟一的。换句话说,同一用户,对同一个微信开放平台下的不一样应用,unionid 是相同的

12. 如何实现下拉刷新

  • 首先在全局 config 中的 window 配置 enablePullDownRefresh
  • Page 中定义 onPullDownRefresh 钩子函数,到达下拉刷新条件后,该钩子函数执行,发起请求方法
  • 请求返回后,调用 wx.stopPullDownRefresh 中止下拉刷新

参考 这里

13. bindtap和catchtap的区别是什么

相同点:首先他们都是做为点击事件函数,就是点击时触发。在这个做用上他们是同样的,能够不作区分

不一样点:他们的不一样点主要是 bindtap 是不会阻止冒泡事件的,catchtap 是阻值冒泡的

14. 简述下 wx.navigateTo(), wx.redirectTo(), wx.switchTab(), wx.navigateBack(), wx.reLaunch()的区别

  • wx.navigateTo():保留当前页面,跳转到应用内的某个页面。可是不能跳到 tabbar 页面
  • wx.redirectTo():关闭当前页面,跳转到应用内的某个页面。可是不容许跳转到 tabbar 页面
  • wx.switchTab():跳转到 abBar 页面,并关闭其余全部非 tabBar 页面
  • wx.navigateBack()关闭当前页面,返回上一页面或多级页面。可经过 getCurrentPages() 获取当前的页面栈,决定须要返回几层
  • wx.reLaunch():关闭全部页面,打开到应用内的某个页面

React全家桶

  • 基本语法
  • 生命周期

框架

  • bootstrap
  • element
  • Echarts
  • Swiper

Node\Express\Egg\Koa

  • 熟悉几道经常使用面试题

开发中遇到的问题

为何 0.1 + 0.2 != 0.3 ?

JS 采用的是双精度版本

  • 内部的原理是什么?

咱们计算机的信息所有转化为二进制进行存储的,那么0.1的二进制表示的是一个无限循环小数, 该版本的 JS 采用的是浮点数标准须要对这种无限循环的二进制进行截取,从而致使了精度丢失, 形成了0.1再也不是0.1,截取以后0.1变成了 0.100...001,0.2变成了0.200...002。因此二者相加的数大于0.3。

  • 控制台上输出console.log(0.1)还等于0.1?

由于在输入内容进行转换的时候,二进制转换成十进制,而后十进制转换成字符串, 在这个转换的过程当中发生了取近似值,因此打印出来的是一个近似值。

部分手机第三方输入法会将页面网上挤的问题

// 特定需求页面,好比评论页面,输入框在顶部之类的
const interval = setInterval(function() {
    document.body.scrollTop = 0;
}, 100)
// 注意关闭页面或者销毁组件的时候记得清空定时器
clearInterval(interval);
复制代码
  • 设置一个100ms的定时器,把scrollTop设置成0,当点击完成是清空定时器

ios竖屏拍照上传,图片被旋转问题

flex对低版本的ios和Android的支持问题

/**使用postcss的autoprefixer插件,自动添加浏览器内核前缀, 而且增长更低浏览器版本的配置,自动添加flex老版本的属性和写法**/
autoprefixer({
    browsers: [
        'iOS >= 6',     // 特殊处理支持低版本IOS
        'Safari >= 6',  // 特殊处理支持低版本Safari
    ],
}),
复制代码

ios上自动识别电话

<meta content="telephone=no" name="format-detection" />
复制代码

android上自动识别邮箱

<meta content="email=no" name="format-detection" />
复制代码

移动端click300ms延迟

缘由:click事件要等待是不是双击事件,会有300ms的延迟

解决:使用touchstart/touchend或者使用zepto的tap事件替代click

图片优化

  • 小图片过多,致使请求增多
    • 使用精灵图合并成大图,缺点: 若是换图的化很是麻烦
    • 把图标转成base64
  • 首屏加载慢,到时用户体验极差
    • 图片懒加载,
    • 静态资源放到CDN
相关文章
相关标签/搜索