1. 前端路由的两种模式
传统的后端路由是指:前端发给后端一个url,后端服务器根据地址知道对应的页面,并且把该页面发给前端,然后展示给用户。
- 对于SPA单页面应用而言,当url发生变化,其实只是页面组件发生了变化,并不需要向后端发送请求重载页面。
- 前端路由的核心:改变视图的同时不会向后端发出请求。
为了使浏览器改变路由的同时不会向后端发出请求,支持以下两种模式:
- hash 地址栏的 #:代表网页的位置
- hash虽然出现在URL中,但不会被包含在HTTP请求中,对后端没有影响
- 改变hash不会重载页面
- 通过window.location.hash来访问
- hash改变后,会增加一个历史记录
- 可以监听onhashchange事件
- history 利用html5 History 中新增的pushState()和popState()对历史记录进行修改
区别:
- hash 模式下,仅hash符号之前的内容会被包含在请求中,如 http://www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误。
- history 模式下,前端的 URL 必须和实际向后端发起请求的URL一致,如 http://www.abc.com/book/id。如果后端缺少对book/id的路由处理,将返回404错误。需要后端支持。
2. 高阶函数
接收函数作为参数的函数或者是返回值为函数的称为高阶函数。 js中常用的高阶函数:
- map:通过调用作为输入数组中每个元素的参数提供的回调函数来创建一个新数组。
Array.prototype.map = function(callback) {
let newArray = [];
let arr = this;
for(let i = 0; i < arr.length; i++) {
newArray.push(
callback(arr[i]);
);
}
return newArray;
}
- reduce:将数组从左向右开始缩减,缩减为一个值返回
Array.prototype.reduce = function(callback, initialValue) {
let arr = this;
let result = initialValue ? initialValue : arr[0];
for(let i = 1; i < arr.length; i++) {
result = callback(result, arr[i]);
}
return result;
}
- filter:筛选原数组符合条件的元素来组成新的数组。
Array.prototype.filter = function (callback){
let newArr = [];
let arr = this;
for(let i = 0; i < arr.length; i++){
callback(arr[i]) && newArr.push(arr[i]);
}
return newArr;
}
3. ES6新特性
- let与const变量:块级作用域、无变量提升,解决var定义变量的问题
- 模板字面量:本质上是包含嵌入式表达式的字符串字面量。使用``表示,使用${expression}作为占位符。
- 解构赋值:使用解构从对象或数组中提取值并赋给单独的变量
// 解构对象
var obj = {
name: 'zhangsan',
age: 20
};
let {name, age} = obj;
// 解构数组
let point = [10, 25, -34];
let [x, y, z] = point;
- 对象字面量简写:当key/value一致时,可以简写。
- 新增for of:循环任何可迭代类型的数据,包括Array/String/Set/Map等,默认对象不可迭代。只循环访问对象中的值,不包括原型上的值。
- 展开运算符(…):将字面量对象展开为多个元素
- 箭头函数
- class类的概念
- Set和Map
- Set 有序列表集合,无重复,有add/has/delete/clear等方法,size属性。
- Map 用来存放键值对的集合,无重复,有get(key)/has/clear方法
var arr = new Set(); while(arr.size < 10) { var r = parseInt(Math.random() * 20 + 1); arr.add(r); } console.log(Array.from(arr));
// 实现set集合 class Set { constructor() { this.items = []; this.size = this.items.length; } has(item) { return this.items.indexOf(item) !== -1; } add(item) { if (!this.has(item)) { this.items.push(item); return this; } else { return this; } } remove(item) { if (this.has(item)) { let index = this.items.indexOf(item); this.items.splice(index, 1); return this; } else { return this; } } clear() { this.items = []; } } let set = new Set(); set.add(1).add(2); set.add(3); console.log(set);
4.Array.prototype.flat函数
会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。(扁平化数组)
替代方案:
// reduce + concat
// 只拉伸1层
var arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
arr = arr.reduce((prev, curr) => {
return prev.concat(curr)
}, []);
console.log(arr);
// [ 1, 2, 3, 4, 5, 6, [ 7, 8, 9 ] ]
// 全部拉平
function flattenDeep(arr) {
return arr.reduce((prev, curr) => Array.isArray(curr) ? prev.concat(flattenDeep(curr)) : prev.concat(curr), []);
}
实现flat,参数为深度:
Array.prototype.flat = function(depth) {
let result = [], depthArg = depth || 1, depthNum = 0;
let flatMap = (arr) => {
arr.map((ele, idx, array) => {
if(Array.isArray(ele)) {
if(depthNum < depthArg) {
depthNum++;
flatMap(ele);
} else {
result.push(ele);
}
} else {
result.push(ele);
}
});
};
flatMap(this);
return result;
}
5. 通过onclick添加多个click事件
将onclick包裹上一个函数,进行上下文的切换和参数的传递。 即先判断是否添加过onclick事件;
function addClickEvent(el, cb) {
if (el.onclick) {
var callback = el.onclick;
el.onclick = function(e) {
callback.call(this, e);
cb.call(this, e);
}
} else {
el.onclick = cb;
}
}
6. flex中align-items与align-content的区别
- align-items适用于所有的flex容器,设置每个flex子项在交叉轴上的对齐方式。是设置单行容器的对齐方式,而不是整个容器。
- align-content 功能与alig-items类似,不过只在多行子项上起作用,将子项作为一个整体进行对齐。
align-items属性是针对单独的每一个flex子项起作用,它的基本单位是每一个子项,在所有情况下都有效果。
align-content属性是将flex子项作为一个整体起作用,它的基本单位是子项构成的行,只在两种情况下有效果:
- 子项多行且flex容器高度固定
- flex容器不设置高度的话,高度由子项撑开,flex容器在交叉轴上没有多余的空间,所以看不出效果
- 子项单行,flex容器高度固定且设置了flex-wrap:wrap
7. ES6 proxy
proxy在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截。
var proxy = new Proxy(target, handler);
// target表示所要拦截的目标对象,handler用来定制拦截行为
proxy的作用:
- 拦截和监视外部对于对象的访问
- 降低函数或类的复杂度
- 在复杂操作前对操作进行校验或对所需资源进行管理
代理操作handler所能代理的范围:
- get/set 读取/设置代理对象的某个属性时触发
- getPrototypeOf/setPrototypeOf 读取/设置代理对象的原型时触发
- isExtensible/preventExtensions 判断/设置代理对象可扩展时触发
- getOwnPropertyDescriptor 获取代理对象某个属性的属性描述时触发
- defineProperty/deleteProperty 定义/删除代理对象某个属性时的属性描述时触发
- ownKeys 获取代理对象的所有属性键时触发
- has 判断代理对象是否拥有某个属性时触发
let target = {
name: 'zhangsan',
age: 16
};
let handler = {
get(target, name) {
return 'test';
}
};
let proxy = new Proxy(target, handler);
proxy.name // test
// handler为空时,proxy直接指向target
let proxy1 = new Proxy(target, {});
proxy1.name // zhangsan
Reflect对象:
Reflect主要是为了优化Object的一些操作方法以及更合理的返回Object操作返回的结果。对于一些命令式的Object行为,Reflect对象可以将其变为函数式的行为。
8. vue中的mixins
混入minxin提供了一种灵活的方式,来分发Vue组件中的可复用功能(更高效的实现组件内容的复用)。混入对象中可以包含任意的组件选项。
选项合并
组件和混入对象有同名选项时,会进行合并。
- 数据对象data内部进行递归合并,有冲突时以组件数据优先。
- 同名钩子函数合并为一个数组,都将被调用(先调用混入对象中的)
- methods/components合并为一个对象,键名冲突时取组件对象的键值对。
与vuex的区别:
- vuex用来做状态管理,其中的变量在每个组件中均可以使用和修改,任一组件修改之后没其他组件中的值也会随之改变。(共享vuex中的变量)
- Mixins中可以定义共用的变量,在各个组件中使用,在变量修改在组件之间不会相互影响。
extend方法:返回一个构造函数,是Vue的一个子类。
- Vue.extend只是创建了一个构造器,为了创建可复用的组件
- mixins/extends是为了扩展组件,mixins可以混入多个mixin,extends只能继承一个
- 优先级Vue.extend > extends > mixins
9. HTML5新特性
- 增加了语义化标签:使得页面更加结构化、可读、利于维护、SEO
- header/footer/section/aside/nav/article/dialog/summary等
- 增强型表单:有多个新的表单input输入类型,提供了更好的输入控制和验证。
- 新增input类型
color:用于选取颜色
date:从日期选择器中选取一个日期
email:包含email地址的输入域
number:数值的输入域
range:一定范围内数字值的输入域
tel:定义电话号码字段 - 新增的表单属性
placeholder:显示在输入域的提示语
required:要求输入域不能为空
min和max:设置最大最小值
autofocus:规定在页面加载时输入域自动获得焦点
- 新增input类型
- 视频和音频:audio和video标签
- audio/video中可以使用source标签链接不同的音频文件,将使用第一个浏览器支持的文件
- controls属性添加播放、暂停、音量控件
- video还有width/height属性控制视频的尺寸
- Canvas绘图:canvas只是一个图形容器,需要使用脚本来绘制图形。
<canvas id="canvas" width="200" height="100" style="border: 1px solid #000">
// 绘制图形
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#F00';
ctx.fillRect(0, 0, 150, 75);
// fillRect(x, y, width, height)
// 绘制路径 moveTo-lineTo-stroke
ctx.moveTo(0, 0); // 移到线条开始坐标
ctx.lineTo(200, 100); // 线条结束坐标
ctx.stroke(); // 绘制线条
// 绘制文本
ctx.font = '30px Arial';
ctx.fiiText('Hi', 0, 0)
// text,x,y,maxWidth
// 渐变
var grd = ctx.createLinearGradient(0, 0, 200, 0);
// x,y,x1,y1
grd.addColorStop(0, 'red');
grd.addColorStop(1, 'white');
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 150, 80);
- 新增API:Deolocation地理位置API、拖放API
- Web Worker:是运行在后台的Javascript,独立于其他脚本,不会影响页面性能。
- Web Storage:在本地存储用户浏览数据。
- localStorage:永久化的本地存储,除非手动删除
- sessionStorage:会话级别的数据存储,浏览器窗口关闭之后数据即删除。
- Storage API
setItem(key, value)
getItem(key)
removeItem(key)
clear()
key(index) 得到某个索引的key
- WebSocket:在单个TCP连接上进行全双工通讯的协议。
- 浏览器和服务器只需要一次握手
- send方法向服务器发送数据
- onmessage事件来接收服务器返回的数据
10. 图片懒加载
滚动加载
- 在页面放置img标签
- 给img图片加上alt, width, height 和 data-src
- 通过js判断页面是否滚动到某张图片需要显示的位置,这时将src赋值为data-src
// 图片进入可视范围
// 距离页面顶部的距离 < 可视区域的高度 + 页面滚动的距离
img.offsetTop < document.documentElement.clientHeight + document.documentElement.scrollTop
// getBoundingClientRect
img.getBoundingClientRect().top < document.documentElement.clientHeight
h5的 intersectionObserver
intersectionRatio:目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例,完全可见时为 1 ,完全不可见时小于等于 0。
function getTag(tag) {
// 获取到的元素列表转成数组
return Array.from(document.getElementsByTagName(tag));
}
var observer = new IntersectionObserver((changes) => {
changes.forEach((change) => {
if (change.intersectionRatio > 0) {
var img = change.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
getTag('img').forEach((item) => {
observer.observe(item);
});