文章目录
- 一、HTML
- 二、CSS
- 三、JS
- 1. 常用数据类型,及区别。
- 2. 类型判断typeof和instanceof,typeof返回什么。
- 3. 列举强制类型转换和隐式类型转换。
- 4. `==` 和 `===` 的不同?。
- 4. 什么是浅拷贝和深拷贝?
- 5. 常用深拷贝的方法。
- 6. 手写深度比较,模拟 lodash isEqual。
- 7. 常用数组的增加删除。
- 8. split() 和 join() 的区别。
- 9. slice()切片 和 splice()剪接 的区别。
- 10. 数组的pop push unshift shift 分别做什么。
- 11. 什么是纯函数
- 12. 数组纯函数api
- 13. 数组非纯函数api
- 14. 数组ES6新增函数
- 15. 常用遍历及区别
- 16. var和let const 的区别
- 17. call 和 apply 的区别
- 18. 数组和类数组的区别
- 19. ES6 set
- 20. 函数声明和函数表达式的区别
- 21. new Object() 和 Object.create() 的区别
- 22. 普通函数和箭头函数的区别
- 23. 高阶函数
- 24. 正则
- 25. 手写字符串 trim 方法,保证浏览器兼容性
- 26. 手写 flatern 考虑多层级
- 27. 如何获取多个数字中的最大值
- 28. 数组去重方法
- 29. 如何用JS实现继承
- 四、JS-三座大山
- 五、JS-Web-API
- 六、Http
- 七、性能优化
- 八、开发环境
一、HTML
1. HTML是怎么加载的
- 下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的。
- 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),下载过程会启用单独连接进行下载。
- 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染(CSS覆盖)。
- JS、CSS中如有重定义,后定义函数将覆盖前定义函数。
- css放顶部,js放在底部,避免使用内联样式导致从新渲染。
2. 如何理解HTML语义化
- 让人更容易读懂,增加代码可读性。
- 让搜索引擎更容易读懂(SEO)。
- 屏幕阅读器可以更好识别。(Accessibility)
3. 默认情况下,哪些HTML标签是块级元素,哪些是内联元素?
- display: block/table; div, h1, table, ul, ol, li, p 独占一行
- display: inline/inline-block; span, img, input, button
4. Javascript事件流模型(事件委托和事件冒泡)。
- 事件委托:自己想所触发的事件,让他的父元素代替执行!阻止默认事件,设置true或者(1)return false;(2) ev.preventDefault();
addEventListener('click', (event)=>{ //判断event.target执行 var element = e.target; if (element.className == 'btn') { console.log(element.id); } }, true); // 阻止默认事件,设置true或者(1)return false;(2) ev.preventDefault();
- 事件冒泡:当前元素接收事件时,会把接收的事件传给自己的父级,一直到window。子元素和父元素的同类型事件会被依次触发。ie:阻止冒泡ev.cancelBubble = true;非IE ev.stopPropagation();
二、CSS
1. 左边固定,右边自适应。
- 给左边div设置float:left,给右边div设置margin-left
- 左边div绝对定位,右边div设置margin-left
- 双float + calc, 两个都float,右边calc(100% -200px)
- flex, 父盒子flex,子盒子左边flex: 0 0 200px; 右边: flex: 1.
2. 垂直居中
// 1. 子绝父相,子left: 50%, top: 50%, ml: -width, mt: -height.
.box3{
border: 2px solid yellow;
position: relative;
}
.img3{
width: 224px;
height: 224px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -112px;
margin-left: -112px;
}
// 2. 子绝父相,子top,right,bottom,left都0 外加margin:auto
.box4{
border: 2px solid green;
position: relative;
}
.img4{
width: 224px;
height: 224px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin:auto;
}
// 3. 子绝父相,
.box5{
border: 2px solid cyan;
position: relative;
}
.img5{
width: 224px;
height: 224px;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
// 4. 父盒子,flex,justifyCoteent, alignItems 都center
.box6{
display: flex;
justify-content: center;
align-items: center;
}
.img6{
width: 224px;
height: 224px;
}
3. 什么是BFC
- Block format context(块级格式化上下文): 一块独立的渲染区域,内部元素的渲染不会影响边界以外的元素
- 形成BFC的条件
- float不是none
- position是absolute或者fixed
- overflow不是visible
- display是flex inline-block
- 清除浮动: 当子元素浮动时,形成BFC,导致父元素高度无法被撑开,解决办法,给父元素也形成BFC,但是为了不给父元素以外的其他元素再次造成BFC的影响,一般采用overflow:hidden或者display: flow-root,也就是创建一个无副作用的BFC
4. 盒模型
- 盒模型offsetWidth计算:(内容宽度+边框+内边距)
- margin-top和margin-bottom会发生重叠
- margin四个方向设置负值,什么效果?
- margin-top负值: 元素向上移动.
- margin-left负值: 元素向左移动.
- margin-right负值: 右侧元素左移,自身不受影响.
- margin-bototm负值: 下边元素上移,自身不受影响.
- calc计算: 100% - value
5. 重绘和回流
- 重绘: color,字体大小从新设置的时候。
- 回流:dom节点丢失, 盒模型的改变, img 不设置默认宽高。
三、JS
1. 常用数据类型,及区别。
-
值类型:String、Number、Boolean、undefined、Symbol。
-
引用类型:Object、Array、Function、null(特殊引用类型,指向空地址)。
-
区别:变量声明时,会存储在栈里,值类型存储值,引用类型则存储地址(堆地址)。
2. 类型判断typeof和instanceof,typeof返回什么。
- typeof判断值类型和引用类型Object
- typeof 返回:string,number,boolean,symbol,object(typeof null === ’object), function。
- instanceof判断变量属于哪个Class、构造函数或者判断是否属于Array、Date、RegExp、Object
3. 列举强制类型转换和隐式类型转换。
- 强制:parseInt parseeFloat toString等
- 隐式:if、逻辑运算、==、+ 拼接字符串
4. ==
和 ===
的不同?。
==
: 只比较值,表示等同,会发生隐式转换。===
: 既比较值还比较类型,表示恒等。
4. 什么是浅拷贝和深拷贝?
- 浅拷贝:将原对象或原数组的引用直接赋给新对象或新数组,新的只是原来的一个引用,新的发生变化,原来的也会发生变化。
- 深拷贝:是指新建一个对象,将原对象的所有属性的‘值’拷贝过来(不是引用),新对象的值发生改变的时候,原对象的值不发生改变。
5. 常用深拷贝的方法。
-
JSON.parse(JSON.stringify(obj):缺点是不能处理函数和正则。
-
使用递归实现(简易版本有缺陷)
-
Object.assign() 一层的时候深拷贝,二层是浅拷贝
-
lodash库的 lodash.cloneDeep()
6. 手写深度比较,模拟 lodash isEqual。
// 判断是否是对象或数组
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
// 全相等(深度)
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型(注意,参与 equal 的一般不会是函数)
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
// 两个都是对象或数组,而且不相等
// 1. 先取出 obj1 和 obj2 的 keys ,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 2. 以 obj1 为基准,和 obj2 一次递归比较
for (let key in obj1) {
// 比较当前 key 的 val —— 递归!!!
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
// 3. 全相等
return true
}
7. 常用数组的增加删除。
- push()尾部添加,pop()尾部删除
- unshift()头部添加,shift()头部删除
8. split() 和 join() 的区别。
- split()字符串拆分成数组,join()将数组转换成字符串
'1-2-3'.split('-') // [1, 2, 3]
[1, 2, 3].join('-') // '1-2-3'
9. slice()切片 和 splice()剪接 的区别。
- slice
- 返回一个新的浅拷贝的数组
- 是纯函数
- splice arr.splice(1, 2, ‘a’, ‘b’, ‘c’)
- 返回 arr 从下标1开始的2个元素
- 不是纯函数,arr 从下标1开始的2个元素被替换成abc了。
10. 数组的pop push unshift shift 分别做什么。
- push()
- 功能:从数组后面追加一个元素
- 返回值:返回 length
- 会对原数组造成影响
- pop(),
- 功能:从数组后面刨除一个元素
- 返回值:刨除的那个元素
- 会对原数组造成影响
- unshift()
- 功能:从数组前面插入一个元素
- 返回值:返回 length
- 会对原数组造成影响
- shift()
- 功能:从数组前面刨除一个元素
- 返回值:刨除的那个元素
- 会对原数组造成影响
11. 什么是纯函数
- 不改变原数组(没有副作用)
- 返回一个数组
12. 数组纯函数api
cosnt arr = [1, 2, 3, 4]
// concat
const arr1 = arr.concat([5, 6, 7 ])
// map
const arr2 = arr.map(num => num * 2)
// slice
const arr3 = arr.slice()
const arr4 = arr.slice(1, 3) // 从数组下标1切到下标4,不包含4
// filter
const arr4 = arr.filter(num => num > 3)
13. 数组非纯函数api
// push pop unshift shift
// forEach 返回的不是数组 没有返回值
// some every 返回的不是数组
// reduce 返回的不是数组
14. 数组ES6新增函数
15. 常用遍历及区别
map(), some(), every(), filter(), reduce()
16. var和let const 的区别
- var 是 ES 语法,let const 是ES6语法。
- var有变量提升。
- var 和 let 是变量,可修改;const 是常量,使用时必须初始化(即必须赋值),不可修改。
- let const 有块级作用域(可以定义内部变量),var 没有。
- 同一个变量只能使用一种方式声明,不然会报错。
- 块级作用域:任何一对花括号 {} 中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
17. call 和 apply 的区别
// 都是改变this指向
fn.call(this, p1, p2, p3); // 参数一个一个传
fn.apply(this, arguments); // 参数传数组或类数组
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
18. 数组和类数组的区别
- 类数组
- 拥有length属性,其属性(索引)为非负整数
- 不具有数组所具有的方法
- 常见的类数组有 arguments对象和 DOM方法的返回结果。比如 document.getElementsByTagName()。
- arguments(实参)对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数
- 类数组转换数组
args = Array.prototype.slice.call(arguments);
19. ES6 set
- Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用
let mySet = new Set();
mySet.add(1); // Set(1) {1}
mySet.add(5); // Set(2) {1, 5}
mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性
console.log([...mySet]) // [1, 5]
20. 函数声明和函数表达式的区别
- 函数声明式,也就是具名函数,变量名会提升
- 函数表达式,也就是匿名函数。
// 函数声明式
function funDeclaration(type){
return type==="Declaration";
}
// 函数表达式
var funExpression = function(type){
return type==="Expression";
}
21. new Object() 和 Object.create() 的区别
- new Object(): {} 和 new Object() 除了本身创建的对象,都继承了 Object 原型链上(Object.prototype)的属性或者方法,eg:toString();当创建的对象相同时,可以说 {} 等价于 new Object() 。
- Object.create(): 是将创建的对象继承到原型链上,而本身没有继承 Object.prototype 的属性和方法。
22. 普通函数和箭头函数的区别
- 写法不一样
- 普通函数存在变量提升的现象
- 箭头函数不能作为构造函数使用
- 箭头函数没有new.target
- new.target是用来检测函数是否被当做构造函数使用,他会返回一个指向构造函数的引用
- 两者this指向不同
- 普通函数:谁调用该函数就指向谁
- 箭头函数:箭头函数的this指向的是声明时候的上下文环境对象的this,如果没有上下文环境对象,那么就指向最外层对象window。
- arguments
- 箭头函数不绑定arguments,取而代之用rest参数…解决
- 箭头函数的arguments指向它的父级函数所在作用域的arguments
23. 高阶函数
- 一个函数接收另一个函数作为参数,这种函数就称之为高阶函数。
24. 正则
1. 贪婪模式
*
:尽可能多的匹配,从字符串后面开始匹配,如果匹配则返回,不匹配则去掉最后一个继续匹配。
2. 惰性匹配
?
: 表示匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。
2. 常用的正则匹配
25. 手写字符串 trim 方法,保证浏览器兼容性
26. 手写 flatern 考虑多层级
27. 如何获取多个数字中的最大值
// 1. Math.max
Math.max(10,20,30,40) // 40
Math.min(10,20,30,40) // 10
// 2. sort 排序法
var arr = [12,56,25,5,82,51,22];
arr.sort(function (a, b) {
return b-a;
}); // [5,12,22,25,51,56]
var max = arr[0]; // 5
var min = arr[arr.length - 1]; // 56
28. 数组去重方法
// indexOf
var arr=[2,8,5,0,5,2,6,7,2];
function unique1(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
if(hash.indexOf(arr[i])==-1){
hash.push(arr[i]);
}
}
return hash;
}
// new Set() 加 扩展运算符
function unique2(arr){
var x = new Set(arr);
return [...x];
}
// new Set 加 Array.from
function unique3(arr){
var x = new Set(arr);
return Array.from(x);
}
29. 如何用JS实现继承
- 借助原型链实现继承
- 借助构造函数继承
- 通过call和apply改变this指向实现继承
四、JS-三座大山
1.原型、原型链和构造函数(大山之一)
- 原型:每个class都有显示原型prototype,每个实例都有隐式原型__proto__,实例的__proto__指向对应的class的prototype,对象以其原型为模板、从原型继承方法和属性。
- 原型链:就是__proto__和prototype的依赖关系,当声明一个实例,我们查找一个实例的属性时,实例里没有会去实例的隐式原型原型里面找,在没有去实例的显示原型里面,再没有去显示原型的隐式原型里面去找,一直找到Object的隐式原型null,最后返回一个undefined。
2.闭包和作用域(大山之二)
2.1 闭包是什么?有什么特性?应用?有什么负面影响?
含义
- 通俗的讲就是函数a的内部函数b,被函数a外部的一个变量引用的时候,就创建了一个闭包。
- 作用域应用的特殊情况
特性
- 封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口;
- 一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在系统中,闭包中的数据依然存在,从而实现对数据的持久使用(内存常驻)。
- 自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方
应用
- 函数作为参数被传入(应用场景)
- 函数作为返回值被返回 (应用场景 )
- 封装,设计私有的方法和变量。
优点
- 避免全局变量的命名冲突 。
- 缓存值,比如闭包内部return ++的值,循环的值。
- 外部可以函数内部变量。
- 实现封装,防止变量跑到外层作用域中,发生命名冲突。
影响缺点
- 变量会常驻内存,得不到释放。有可能会内存泄漏。
// JS中的垃圾回收机制
function test(){
var a = 1;
alert(a)
}
test(); // 执行完这个函数,函数里面的a就不存在了.
function aaa() {
var a = 1;
a++;
alert(a)
}
aaa(); // 2
aaa(); // 2
aaa(); // 2
上面的函数不管执行几次,弹出的都是2,如果我们想弹出2/3/4 这样的结果呢?就需要用到闭包
// 闭包
function aaa() {
var a = 1;
return function(){
a++;
alert(a)
}
}
var bbb = aaa();
bbb(); // 2
bbb(); // 3
bbb(); // 4
// 函数表达式的写法
var aaa = (function () {
var a = 1;
return function () {
a++;
alert(a)
}
})()
aaa() // 2
aaa() // 3
aaa() // 4
2.2 作用域
- 指一个变量的作用范围
3.异步(大山之三)
3.1 同步和异步的区别是什么(js单线程,只能同时做一件事)
- 同步:会阻塞代码执行,遇见未完成的代码执行,会一直等待。
- 异步:不会阻塞代码执行(通过callBack实现)。
3.2 前端使用的异步场景有哪些
- ajax: Asynchronous Javascript And XML,异步JavaScript和XML无需加载整个页面,对网页局部进行更新,使网页实现异步更新的技术。
- 网络请求,比如图片加载(onload就是一个callBack)。
- 定时任务,比如如setTimeout。
3.3 异步实现的方式
- callback 使用回调函数实现(普通的和promise)
- 普通的缺点:请求接口复杂时会陷入callbback hell(回调地狱)
- 普通的缺点:请求接口复杂时会陷入callbback hell(回调地狱)
- Promise(pending/reslove/reject)
3.4 手写Promise加载一张图片
4. 异步进阶
4.1 描述event loop(事件循环/事件轮询)的机制。
- event loop:就是异步回调实现的原理
- js执行
- js从前到后,一行一行执行。
- 如果某一行执行报错,则停止下面代码的执行。
- 先把同步代码执行完,再执行异步。
- event loop 过程:
- 过程1
- 同步代码一行一行放在Call Stack(调用栈)执行。
- 遇到异步,会先"记录"下,等待时机(定时、网络请求等)。
- 如果Call Stack为空(即同步代码执行完)Event Loop 开始工作。
- 轮询查找Callback Queue, 如果有则移动到Call Stack 执行
- 然后继续轮询查找
- 过程2 DOM事件和Event loop
- 事件触发时,先触发事件的函数,再执行事件函数里的callback。
- 事件函数里的callback代码块继续执行event loop。
- 过程1
4.2 什么是宏任务和微任务,区别是什么。
- 宏任务(触发迟)
- setTimeout, setInterval,Ajax,DOM 事件
- 微任务(触发早)
- Promise, async,await
- DOM (微任务执行完渲染DOM,再执行宏任务)
- 区别
- 微任务早宏任务迟是因为微任务在DOM渲染前触发,而宏任务是在DOM渲染后触发。
- 微任务在DOM渲染之前触发,宏任务在DOM渲染之后触发。
4.3 Promise ?有哪三种状态?如何变化?
- promise。
- promise 是什么? 作用?
- 是异步编程的一种解决方案,链式调用,但也是基于回调函数(并不是promise就是异步)
- 主要用于异步计算
- 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
- 可以在对象之间传递和操作promise,帮助我们处理队列
- promise 解决的问题
- 解决回调地狱的问题。
- promise可以支持多个并发的请求,获取并发请求中的数据
- 三种状态(pending resolved rejected)
- pending:不会触发then和catch,从pending -> resolved 或 从pending -> rejected.
- resolved(fulfilled):会触发后续的then回调函数。
- rejected:会触发后续的catch回调函数。
- 状态不可逆
- promise 是什么? 作用?
4.4 async/await语法,及顺序。
- async/await ?
- 同步语法,彻底消灭异步回调, 同步语法编写异步代码。
- await必须依赖于async, await后面可以追加promise也可以追加async函数,或者具体值。
- async/await和Promise的关系
- 和Promise并不互斥,两者相辅相成
- 执行async函数,返回的是Promise
- await相当于Promise的then
- 如果是失败的情况,await不会执行,需要用catch 捕获
- try…catch可捕获异常,代替了Promise的catch
- 执行顺序
4.5 for-of
- 常用来异步的遍历
五、JS-Web-API
1. DOM
1.1 DOM 是哪种数据结构( Document Object Model)
- 树 DOM树
本质
:DOM 的本质就是一个树,浏览器内存里面已经初始化好的树的结构。
1.2 DOM 操作常用的API
1.2.1 DOM 节点操作API
- 获取DOM节点
- id获取
- className获取
- tagName获取
- 获取DOM节点的 property
- 节点的 style,className, nodeName, nodeType, innerHTML 等等
- 获取DOM节点的 attribute
- p.getAttribute(param1)
- p.settAttribute(param1, param2)
attribute 和 property 的区别
- attribute: 修改html属性,会改变html解构
- property:修改对象属性,不会体现到html解构中
两者都可能引起DOM从新渲染
1.2.2 DOM 结构操作API
- 新增/插入节点
// 获取 const div1 = document.getElementById('div1'); // 新增 const p1 = document.createElement('p'); // 插入 div1.appendChild(p1); // 移动已有节点 const p2 = document.getElementById('p2'); div1.appendChild(p2);
- 获取子元素列表,获取父元素
// 获取父元素 const p1 = document.createElement('p'); console.log(p1.parentNode); // 获取子元素列表 const div1 = document.getElementById('div1'); // 包含标签和标签内的文本,所以应该判断nodeType为1就可以获取标签 console.log(div1.childNodes);
- 删除子元素
const div1 = document.getElementById('div1'); div1.removeChild(div1.childNodes[0]);
1.2.3 如何减少DOM操作?
1.3 DOM 性能,一次性插入多个 DOM 节点, 考虑性能
- DOM 操作非常 消耗性能,避免频繁的DOM操作
- 对DOM查询做缓存
// 不缓存 DOM 查询结果 for (let i = 0; document.getElementsByTagName('p').length; i++) { // 每次循环都会计算length,频繁进行 DOM 查询 } // 缓存 DOM 查询结果 const pList = document.getElementsByTagName('p'); const length = pList.length; for( let i = 0; length; i++) { // 缓存 length, 只进行一次 DOM 查询 }
- 将频繁操作改为一次性操作
// 频繁操作 const list = document.getElementsByTagName('list'); for (let i = 0; i < 10; i++) { const li = document.createElement('li'); li.innertHTML = `List item ${i}`; list.appendChild(li) } // 一次操作 const list = document.getElementById('list') // 创建一个文档片段,此时还没有插入到 DOM 结构中 const frag = document.createDocumentFragment() for (let i = 0; i < 20; i++) { const li = document.createElement('li') li.innerHTML = `List item ${i}` // 先插入文档片段中 frag.appendChild(li) } // 都完成之后,再统一插入到 DOM 结构中 list.appendChild(frag)
2. BOM
2.1 将 url 参数解析为 JS 对象
- 获取当前url参数值?ES6?
2.2 知识点
- navigator 浏览器的信息
const ua = navigator.userAgent
const ishrome = ua.indexOf('Chrome');
- screen 屏幕的信息
console.log(screen.width);
console.log(screen.height);
- location 地址的信息
- history 前进后退
3. 事件
3.1 document load 和 ready 的区别。
- document load:资源全部加载完(包括DOM树,js,css,图片)后执行。
- ready:jquery封装的方法,当DOM文档树加载完成后执行一个函数 (不包含图片,css等)。
3.2 事件代理(委托) 和冒泡
3.2.1 事件冒泡
- 事件冒泡:基于DOM树形结构,事件会顺着触发元素往上冒泡,应用场景代理。当前元素接收事件时,会把接收的事件传给自己的父级,一直到window。子元素和父元素的同类型事件会被依次触发。ie:阻止冒泡ev.cancelBubble = true;非IE ev.stopPropagation();
3.2.2 事件委托/代理
- 事件委托:让自己想所触发的事件,让他的父元素代替执行,用e.target获取触发元素阻止默认事件,设置true或者(1)return false;(2) ev.preventDefault();
addEventListener('click', (event)=>{
//判断event.target执行
var element = e.target;
if (element.className == 'btn') {
console.log(element.id);
}
}, true);
// 阻止默认事件,设置true或者(1)return false;(2) ev.preventDefault();
4. Ajax
1. 什么是Ajax ?优点?缺点?
- 定义ajax: Asynchronous Javascript And XML,异步JavaScript和XML,无需重新加载整个网页的情况下,能够更新部分网页的技术。
- 优点:减轻服务器的负担,按需取数据,最大程度的减少冗余请求,局部刷新页面,带来更好的用户体验。
- 缺点:AJAX干掉了Back和History功能,即对浏览器机制的破坏。
- 为什么是异步的:async默认的设置值为true,这种情况为异步方式,就是说当ajax发送请求后,在等待server端返回的这个过程中,前台会继续 执行ajax块后面的脚本,直到server端返回正确的结果才会去执行success
- 通过XMLHttpRequest API实现的,Jquery,浏览器Fetch,Axios,XMLHttpRequest(XHR)都可以实现。
// XMLHttpRequest
// get 请求
const xhr = new XMLHttpRequest();
// false 代表异步请求
xhr.open('GET', '/api', false);
xhr.onreadystatechange = function () {
// 这里的函数式异步执行
// 0: 请求未初始化,没有调用send方法。
// 1: 服务器连接已建立,已调用send方法。
// 2: 请求已接收,send方法执行完成。
// 3: 请求处理中,正在响应解析内容。
// 4: 请求已完成,且响应已就绪。
if (xhr.readyState === 4) {
if (xhr.status = == 200) {
aleeert(xhr.reesponseText);
}
}
}
// send 发送向后台请求的参数,只能发送字符串
// xhr.send(JSON.stringify(Data));
xhr.send(null);
// Promise 封装
function ajax (url) {
const p = new Promise((reesolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if(xhr.status === 200) {
reesolve(
JSON.pase(xhr.responseText)
)
} else if (xhr.status === 404) {
reject(new Error('404 not found'))
}
}
};
xhr.send(null) // 必须写
})
return p;
}
const url = '/data/test.json';
ajax(url)
.then(res => console.log(res))
.catch(err => console.log(err))
1. 什么是JSON ?
- JavaScript Object Notation,是一种轻量级的数据交互格式。
- JSON.parse() 将字符串转换为 JavaScript 对象。
- JSON.stringify()将对象串转换为json字符串对象。
1. 什么是跨域?
- 什么是跨域:
- 在不同源下,访问server。
- 同源策略(仅限浏览器)
- ajax请求时,浏览器要求当前网页和server必须同源(安全)
- 同源:协议(http/https)、域名、端口、三者必须一致。
- CSS JS 可无视同源策略
- 搜索引擎和爬虫是通过服务端发送请求的,可以跨域请求,攻击。
- 所有的跨域,都必须经过servere端允许和配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号。
- 实现方式
- JSONP
<script>
可绕过跨域限制,实现 JSONP- 服务端可以任意动态拼接数据返回,只要符合html格式要求
- 同理于
<script src="https://test.com/test.js">
js也可以动态拼接js代码返回数据 - 所以,
<script>
就可以死获得跨域的数据,只要服务端愿意返回。
- CORS(服务端支持)
- 服务器端设置 http header
- JSONP
1. 什么是 jsonp ?
- 定义:JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
- 原理:动态插入scrip标签(scrip标签引入不受跨域限制,加载scrip后,调用里面的回调函数实现跨域,但是得和后台约定好内容和callback函数名)
1. get 和 post 区别
- get 一般用于查询操作,post 一般用于用户提交操作
- get 参数拼接在url 上, post 放在请求体内(数据体积更大)
- post 易于防止 CSRF(跨站请求伪造) 攻击
5. 存储
- cookie
- 作用1:本身用于浏览器和server通讯,http请求会发送Cookie信息
- 作用2:html5之前用来做本地存储,前后端都可以修改操作,document.cookie
- 缺点:
- 只有4kb
- http 请求时会发送到服务端, 增加请求数据量
- sessionStorage,localStorage
- 共同点
- 最大可存储5M(每个域名都可以存储5M)
- API 简单易用 setItem getItem
- 不会随着http发送到服务端
- 存储的都是字符串形式,获取的也是字符串,会强制转换
- 不同点
- localStorage 数据会永久存储,除非代码或手动删除
- sessionStorage 数据只存在于当前会话,浏览器关闭则清空
- 一般用 localStorage 会更多一些
- cookie localStorage sessionStorage的区别
- 大小限制,存储期限,安全。
- 共同点
六、Http
1. http 常见的状态码有哪些
- 状态码 Status code
- 状态码分类
- 1xx 服务器收到请求
- 2xx - 表示成功处理请求,如200,204
- 3xx - 需要重定向,浏览器直接跳转,如
- 301(永久重定向,配合 location, 浏览器自动处理,用于更换域名)
- 302(临时从定向,配合 locatiion,浏览器自动处理,访问A链接服务端返回locationB,然后跳转到B)
- 304(资源未改变,请求缓存,用来做优化)
- 4xx - 客户端请求错误
- 404(资源未找到,请求地址有错误)
- 403(没有权限访问)
- 5xx - 服务端错误, 挂掉
- 500 服务器错误
- 504 网关超时
- 关于协议和规范,就是一个约定,要求大家都跟着执行,不要违反规范,例如IE浏览器
2. http 常见的header 有哪些
2.1 Request Headers
- Accept 浏览器可接收的数据格式
- Accept-Encoding 浏览器可接收的压缩算法,如gzip
- Accept-Language 浏览器可接收的语言,如 zh-CN
- Connection:keep-alive 一次 TCP 连接重复使用
- cookie
- Host 请求的域名
- User-Agent(简称UA)浏览器信息
2.2 Response Headers
- Content-type 返回数据的格式,如如application/json
- Content-length 返回数据的大小,多少字节
- Content-type(post或者patch)发送数据格式,如application/json
- Content-Encoding 返回数据的压缩算法,如gzip
- Set-Cookie
2.3 缓存headers
- Cache-Control Expires
- Last-Modified If-Modified-Since
- Etag If-None-Match
2.4 自定义header
- 比如添加密钥判断是不是非法请求
3. http methods
3.1 get和post 的区别
- 参考前面
3.2 传统的 methods
- get
获取服务器的数据
- post
向服务器提交数据
- 就这两个简单的功能
3.3 现在的 methods
- get
获取数据
- post
新建数据 ,新建留言
- patch(常用)/put
更新数据
- delete
删除数据
3.4 Restful API 一种的新的 API 设计方法 url
3.4.1. 对比
- 传统 API 设计:把每个url当做一个功能
- Restful API 设计:把每个url当做一个唯一的资源
3.4.2. 如何把Restful API设计成一个资源
- 尽量不用url参数
- 传统api:/api/list?pageIndex=2
- Restful API: /api/list/2
- 用method表示操作类型
- 传统
- post请求:/api/create-blog
- post请求:/api/update-blog?id=100
- get请求:/api/get-blog?id=100
- Restful API
- post请求:/api/blog
- patch请求:/api/blog/100
- get请求:/api/blog/100
- 传统
4. 描述一下 http 的缓存机制(见下http缓存图)
4.1 缓存介绍
- 什么是缓存:把一些没有必要从新获取的东西不在从新获取,这就是缓存。
- 为什么需要缓存:使用缓存视为了让页面加载更快。
- 哪些资源可以被缓存:静态资源(js,css,img),html,API返回数据
4.2 http 缓存策略 (强制缓存 + 协商缓存)
4.2.1 强制缓存
Cache-Control
- 浏览器初次请求的时候服务端在返回时,会把适合缓存的资源在Reponse Headers中加Cache-Control
- 在Response Headers中
- 控制强制缓存的逻辑
- 例如 Cache-Control:max-age=31536000(单位是秒)缓存一年时间
- 缓存过期就会再次请求服务器获取资源
Cache-Control的值
- max-age 强制缓存的值 时间
- no-cache 不用强制缓存,交给服务端处理,服务端怎么处理不管 比如html
- no-store 不用强制缓存,也不让服务端做缓存,直接返回就行
- private 只能允许最终用户作为缓存 比如电脑 手机
- public 允许中间的路由、代理也可以做缓存
Expires
- 比较老,被Cache-Control代替了
4.2.2 协商缓存(对比缓存)
协商缓存
- 服务器端缓存策略(服务端可以告诉我资源没动,直接用本地的就行,服务端判断能不能用缓存内容)
- 服务器判断客户端资源,是否和服务端资源一样
- 一致返回 304, 否则返回200和最新的资源
资源标识
- 在 Response Headers中,有两种
- Last-Modified 资源的最后修改时间
- Etag 资源的唯一标识 (一个字符串,类似人类的指纹)
4.2.3 Last-Modified 和 Etag
- 两者存在时会优先使用 Etag
- Last0-Modified 只能精确到秒级
- 如果资源被重复生成,二呢绒不变,Etag更精确
4.3. 刷新操作方式,对缓存的影响
- 正常操作:地址栏输入url, 跳转连接,前进后退等
- 强制缓存有效,协商缓存有效
- 手动刷新:F5, 点击刷新按钮,点击菜单刷新
- 强制缓存失效,协商缓存有效
- 强制刷新:ctrl + F5
- 强制缓存失效,协商缓存失效
七、性能优化
前端性能如何优化?一般从哪几个方面考虑?
- 原则:多使用内存,缓存,减少计算,减少网络请求
- 方向:加载页面,页面渲染,页面操作的流畅度(防抖和节流)
html,CSS, JS
2.2 性能优化方案
- 让加载更快
- 减少资源体积:压缩代码(js, css, 图片)
- 减少访问次数:合并代码,SSR服务端渲染,缓存(webpack就可以合并代码,雪碧图,分页缓存)
- 使用更快的网络:CDN(针对不同的区域用户选择更近的服务端)
- 让渲染更快1
- CSS 放在 head,JS 放在 body 最下面
- 尽早开始执行JS, 用DOMContentLoaded触发
- 懒加载(图片懒加载,上滑加载更多,什么时候用什么时候加载)
- 让渲染更快2
- 对DOM查询进行变量缓存(避免重复多次查询DOM)
- 频繁DOM操作,合并到一起插入DOM结构
- 防抖,节流(渲染更加流畅)
- 缓存
- 机制:文件内容不变,则hash不变,则url不变,url和文件不变,则会自动触发
http缓存机制返回304。 - webpack打包文件添加hash值,新功能增加,hash改变,从新请求,老功能
没有改变,hash不变,请求触发缓存。
- 机制:文件内容不变,则hash不变,则url不变,url和文件不变,则会自动触发
- SSR
- 服务器端渲染:将网页和数据一起加载,一起渲染
- 非SSR(前后端分离):先加载网页,再加载数据,再渲染数据
2.2 防抖debounce、节流throttle
- 防抖:频繁输入和频繁操作的时候,最后才触发。
- input输入keyup会频繁触发
- 防抖:只在输入完指定时间(一般300)后触发 。
function debounce(fn, delay = 500) {
// timer 是闭包中的
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// this指 input1
// arguments 调用函数时传的参数 event等
fn.apply(this, arguments)
timer = null
}, delay)
}
}
input1.addEventListener('keyup', debounce(function (e) {
console.log(e.target)
console.log(input1.value)
}, 600))
- 节流:频繁输入和频繁操作的时候,保持一个频率连续触发。
- 拖拽一个元素时,要随时拿到该元素被拖拽的位置。
- 直接用drag时间,则会频繁触发,很容易导致卡顿。
- 节流:无论拖拽速度多快,都会每隔100ms触发一次。
// 节流
function throttle(fn, delay = 100) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
Http(优化具体参考文章前面的http缓存)
- http 缓存策略 (强制缓存 + 协商缓存)
RAF requestAnimationFrame
- setTimeout 要手动控制频率,而RAF浏览器会自动控制
- 后台标签或隐藏(最小化或者切换标签)iframe中,RAF会暂停,而setTimeout依然执行
安全
1. 攻击方式
- XSS 跨站请求攻击
- 一个博客网站,其中嵌入
<script>
脚本 - 脚本内容:获取cookie,发送到我的服务器(服务器配合跨域)
- 发布博客,有人查看他,轻松收个访问者的cookie
- 一个博客网站,其中嵌入
- XSRF 跨站请求攻击
- 你正在购物看中某个商品,商品id是100
- 付费接口是 xxx.com/pay?id=100,但没有任何验证
- 我是攻击者,我看中了一个商品,id是200
- 我向你发送一封电子邮件,钓鱼邮件
- 但邮件正文隐藏着
<img src=xxx.com/pay?id=200
- 你一查看邮件,就帮我购买了id是200的商品。
2. 消除攻击
- XSS 预防
- 替换特殊字符,如
<
变为<
>
变为>
- 前端要替换,后端也要替换,双层保障。
- 替换特殊字符,如
- XSRF 预防
- 使用 post 接口
- 增加验证,例如密码、短信验证码、指纹等。
八、开发环境
1. Git
1. 常用命令
# git clone xxx
# git init
# git branch 查看分支
# git checkout xxx / git checkout -b xxx 还原分支/创建分支
# git checkout . 撤销所有修改
# git add. 所有变化提交到暂存区
# git commit -m 'xxx' 提交到版本库
# git log 查看日志
# git show hash值(git log可以看到)
# git stash 先把修改的内容放到一边,用于临时切换分支
# git stash pop 把之前放到一边的内容在拿出来
# git fetch 获取最新的所有分支
# git merge develop 把develop合并到当前分支
# git pull orgin develop
# git push origin develop
# git diff / git diff 文件名
2. 调试工具
1. Elements
2. Console
3. debugger
4. Network
5. Application
3. 抓包(手机移动端)
1. windows 一般用 fiddler
2. Mac OS 一般用charles
3. 作用
- 查看网络请求
- 网址代理
- https
4. webpack babel
1. 诞生的背景
- ES6 模块化,浏览器暂不支持
- ES6 语法, 浏览器并不完全支持
- 压缩代码,整合代码,让网页加载更快
2. webpack(整合代码)
- 配置dev环境打包 webpack.config.js
- 配置生产环境打包 webpack.prod.jd
- 打包 npm run build
3. babel(ES6转ES5)
- npm install @babel/core @babel/preset-env babel-loader -D
- .babelrc 里配置
{
"presets": ["@babel/preset-env"]
}
- 在webpack.config.js 里面的 module 里添加 rules
4. ES6 模块化规范
- 模块化就是导出和导入的过程
5. linux 常用命令(连接测试机)
ssh work@192.169.12.21 登录
ls 查看pingpu
ls -a 查看所有包括隐藏的文件
ll 查看列表
mkdir test 创建文件夹
clear 清屏
rm -rf test 递归强制删除test文件夹
rm test.js 删除文件
cd test 进入test
mv test.html test1.html 修改名字
mv test.js ../test.js 移动test.js到上层目录
cp a.js a1.js 把a.js拷贝成a1.js
vi a.js 新建a.js并用vim打开,点击i计入编辑模式,点esc退出编辑,
:w 回车保存,:q 回车退出,:q! 强制退出
vim a.js vim 打开a.js
cat package.js 查看文件内容
grep "babel" package.json 在package.json里查找关键字