文章目录
- 32.【算法与数据结构】伪代码与流程图
- 33.【算法与数据结构】排序算法(上)
- 34.【算法与数据结构】排序算法(下)
- 35.【算法与数据结构】伪代码与流程图
- 36.【JS编程接口】DOM 编程
- 37.【JS编程接口】手写 DOM 库
- 38 【JS编程接口】jQuery 中的设计模式(下)
- 39.【JS编程接口】DOM事件和事件委托
- 40.【项目】前航项目介绍
- 41.【项目】前端导航站点-上
- 42.【项目】前端导航站点(下)
- 43. 打console.log
- 44.【前后分离】AJAX 的原理
- 45.【前后分离】异步与Promise
- 46.【前后分离】跨域、CORS、JSONP
- 47.【前后分离】静态服务器
- 48【前后分离】Ajax 实战:Cookie、Session
32.【算法与数据结构】伪代码与流程图
32.1 视频:1
抽象能力: 越高级的程序员, 抽象能力越强
JS, Python, Java和PHP都有:
- 声明
- if… else…
- while, for循环
- 函数
- 对象
32.2视频:2
- 对编程的热爱
- 逻辑
推荐使用MIndjet画流程图
32.3 视频:3
画流程图
32.4 视频:4
数据结构 = 数据形式 + 操作
用哈希表来解决该问题
33.【算法与数据结构】排序算法(上)
33.1 视频:minOf2的实现
数据结构: 数组
找出较小的那个数
let minOf2 = ([a, b]) => a > b?a: b
//高手调用函数方法
minOf2.call(null, [1, 2])
这种写法叫做析构赋值, 之后的课程会反复使用.
现成min的JS的API
Math.min.call(null, 1, 2)
Math.min.apply(null, [1, 2])
@@@
Math看起来像Object一样是构造函数, 实际上Math只是一个普通对象, 这是唯一的特例: 首字母大写是构造函数.
@@细节@
33.2 视频:minOf4的实现
33.3 视频:min的实现
找出给出数组的最小值
let min = (numbers) =>{
if(numbers.length > 2){
return min([numbers[0], min(numbers.slice(1))])
}else{
return Math.min.apply(null, numbers)
}
}
33.4 视频:实现sort排序
任意长度的数组排序
let min = (numbers) =>{
if(numbers.length > 2){
return min([numbers[0], min(numbers.slice(1))])
}else{
return Math.min.apply(null, numbers)
}
}
let minIndex = (numbers) => numbers.indexOf(min(numbers))
let sort = (numbers) => {
if(numbers.length > 2){
let index = minIndex(numbers)
let min = numbers[index]
numbers.splice(index, 1)
return [min].concat(sort(numbers))
}else{
return numbers[0]>numbers[1]? numbers.reverse(): numbers
}
}
33.5 视频:如何调试代码
console大法:俗称print大法
34.【算法与数据结构】排序算法(下)
34.1 视频:选择排序的循环写法
let minIndex = numbers =>{
let index = 0
for(let i = 1; i< numbers.length; i++){
if(numbers[index] > numbers[i]){
index = i
}
}
return index
}
let swap = (array, i, j) => {
let temp = array[i]
array[i] = array[j]
array[j] = temp
}
let selectSort = numbers =>{
let index = 0
for(let i = 0; i<numbers.length - 1; i++){
//不改变原数组,即可找出逐渐递减的数组中最小值的index
index = minIndex(numbers.slice(i)) + i
if(index !==i ){
swap(numbers, index, i)
}
}
return numbers
}
@@@
所有递归都可以改成循环
@@细节@
34.2 视频:快速排序
let quickSort = numbers => {
if(numbers.length <= 1){
return numbers
}
let pivotIndex = Math.floor(numbers.length / 2)
let pivot = numbers.splice(pivotIndex, 1)[0]
let left = []
let right = []
for(let i = 0; i< numbers.length; i++){
if(numbers[i] < pivot){
left.push(numbers[i])
}else{
right.push(numbers[i])
}
}
return quickSort(left).concat([pivot], quickSort(right))
}
34.3 视频:归并排序
let merge = (a, b) => {
if(a.length === 0) return b
if(b.length === 0) return a
return a[0] > b[0] ? [b[0]].concat(merge(a, b.slice(1))) : [a[0]].concat(merge(a.slice(1), b))
}
let mergeSort = numbers => {
let len = numbers.length
if(len === 1){
return numbers
}
let left = numbers.slice(0, Math.floor(len/2))
let right = numbers.slice(Math.floor(len/2))
return merge(mergeSort(left),mergeSort(right))
}
34.4 视频:计数排序
数据结构:哈希表(key:value这种类型)
let countSort = numbers => {
let hashTable = {}, max = 0, result = []
for(let i=0; i<numbers.length; i++){
if(!(numbers[i] in hashTable)){
if(numbers[i] > max){
max = numbers[i]
}
hashTable[numbers[i]] = 1
}else{
hashTable[numbers[i]] += 1
}
}
for(let j=0; j<=max; j++){
if(j in hashTable){
for(let k=0; k<hashTable[j]; k++){
result.push(j)
}
}
}
return result
}
34.5 视频:四种排序的时间复杂度
34.6 视频:其他的排序算法
35.【算法与数据结构】伪代码与流程图
35.1 视频:队列 & 栈
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>队列</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="screen"></div>
<div id="action">
<button id="createNumber">取号</button>
<button id="callNumber">叫号</button>
</div>
<div>
当前号码: <span id="newNumber"></span>
</div>
<div>
当前队列: <span id="queue"></span>
</div>
<script src="./main.js"></script>
</body>
</html>
#screen {
border: 1px solid #000;
width: 200px;
height: 200px;
}
const divScreen = document.querySelector('#screen')
const btnCreateNumber = document.querySelector('#createNumber')
const btnCallNumber = document.querySelector('#callNumber')
const spanNewNumber = document.querySelector('#newNumber')
const spanQueue = document.querySelector('#queue')
let n = 0
let queueList = []
btnCreateNumber.onclick = () => {
n += 1
queueList.push.call(queueList, n)
spanNewNumber.innerHTML = n;
spanQueue.innerHTML = JSON.stringify(queueList);
console.log(queueList)
}
btnCallNumber.onclick = () => {
if (queueList.length == 0) {
divScreen.innerHTML = ''
return
}
const now = queueList.shift.call(queueList)
divScreen.innerHTML = `请 ${now} 号, 取餐,祝您用餐愉快!`
spanQueue.innerHTML = JSON.stringify(queueList);
}
35.2 视频:链表
35.3 视频:哈希表
35.4 视频:树
36.【JS编程接口】DOM 编程
36.1 视频:DOM(Document )介绍
DOM很难用, 所有后面才会出现JQuery, Vue和React等
获取元素, 也叫标签:
window.idxxx
或者会直接诶idxxx
document.getElementById('idxxx')
document.getElementsByTagName('div')[0]
document.getElementsByClassName('red')[0]
document.querySelector('#idxxx')
document.querySelector('.red')[0]
用哪一个: 工作中使用querySelector和querySelectorAll, 做demo直接使用idxxx(千万别被人发现了), 要做兼容IE的可怜虫才用getElement(s)ByXXX
36.2 视频:获取元素的API
获取特定元素:
- html元素
document.documentElement - head元素
document.head - body元素
document.body - 窗口(窗口不是元素)
- window
- 所有蒜素
document.all
document.all是个奇葩, 第6个falsy值
36.3 视频:元素的6层原型链
获取的元素是对象:
div完整原型链:
节点(Node):
36.4 视频:创建元素的API
- 增
1 创建一个标签节点
let div1 = document.createElement('div')
document.createElement('style')
document.createElement('script')
document.createElement('li')
2 创建一个文本节点
text = document.createTextNode('你好')
3 标签里面插入文本
div1.appendChild(text1)
//法一(IE)
div1.innerText = '你好'
//法二(标准)
div1.textContent = '你好'
//不能用div1.appendChild('你好')
4 插入页面中
创建的标签默认处于JS线程中,必须把它插入到head(默认看不见)或者body中, 它才会生效
document.body.appendChild(div)
已经在页面中的元素.appendChild(div)
- appendChild
let div = document.createElement('div')
test1.appendChild(div)
test2.appendChild(div)
@@@
最终div出现在test2中, 一个元素不能出现在两个地方, 除非复制一份(let div2 = div1.cloneNode(true)
)
@@细节@
- 删
1 两种方法
//旧方法
parentNode.removeChild(ChildNode)
//新方法(不兼容IE)
childNode.remove()
2 如果一个node被移除页面(DOM树), 那么它还可以再次回到页面中吗?
div1.parentNode.removeChild(div1)
div1.remove()
document.body.appendChild(div1)
可以再次回到页面, 删除只是将node从页面(DOM树)移除, 回到了内存(线程)中, 当然可以再次回到页面(DOM树)中.
- 改
1 写标准属性
//改calss
div.className = 'red'//全覆盖
div.className += ' blue'//追加
div.calssList.add('red')//追加
//改style
div.style = ''//全覆盖
div.style.属性 = ''//修改对应属性
//大小写
//background-color
div.style.backgroundColor = 'black'
div.style['background-color'] = 'black'
//增加自定义属性
div.setAttribute('data-x', 'text')
//改data-*属性
div.dataset.x = 'frank'
2 读标准属性
//常规
div.style
div.className
div.id
//特殊
div.calssList
div.getAttribute('class')
a.href
a.getAttribute('href')
- 改事件处理函数
1 div.onclick默认是为null
默认点击div不会有任何响应, 但是如果把div.onclick
改为一个函数fn, 那么点击div的时候, 浏览器就会调用这个函数, 并且是这样调用的fn.call(div, event)
. div会被当做this, event则包含了点击事件的所有信息, 如坐标.
2 div.addEventListener
div.onclick升级版后面讲
- 改内容
1 改文本内容
div.innerText = 'xxx'
div.textContent = 'xxx'
2 改HTML内容(内容过长影响网站性能)
div.innerHTML = '<strong>重要内容<strong>'
3 改标签
div.innerHTML = ''//先清空
div.appendChild(div2)//再添加内容
- 改爸爸
newParent.appendChild(div)
div2.appendChild(div)
36.5 视频:查看元素的API
- 查
1 查爸爸
node.parentNode
node.parentElement
2 查爷爷
node.parentNode.parentNode
3 查子代
node.childNodes
node.children//推荐
用node.childNodes
, 如果有空格的话会算进去
想要正确表达子代个数, 第一种不能打空格用node.childNodes
第二种用node.children
- 查看兄弟姐妹
node.parentNode.childNodes //还要排除自己(for循环)
node.parentNode.children //还要排除自己(for循环)
排除自己,实现代码:
let sibling = []
let c2 = div.parentElement.children
for(let i=0; i< c2.length; i++){
console.log(i)
if(c2[i] !== div){
sibling.push(c2[i])
}
}
- 查看老大
node.firstChild
- 查看老幺
node.lastChild
- 查看上一个哥哥/姐姐
node.previoussibling
- 查看下一个弟弟/妹妹
node.nextSibling
- 遍历一个div里面的所有元素
travel = (node, fn) => {
fn(node)
if(node.children){
for(let i=0; i<node.children.length; i++){
travel(node.children[i], fn)
}
}
}
travel(div1, (node)=> console.log(node))
36.6 视频:DOM操作跨线程
DOM操作是跨线程的
- 浏览器功能分为:
渲染引擎
和JS引擎
1 各线程各司其职
JS引擎不能操作页面, 只能操作JS. 渲染引擎不能操作JS, 只能操作页面.
那么document.body.appendChild(div1), 是如何改变页面的.
2 跨线程通讯
当浏览器发现JS在body里面加了个div1对象, 浏览器就会通知渲染引擎在页面里也新增一个div元素, 新增的div元素所有属性都照抄div1对象.
- 插入新标签完整过程
1 在div1放入页面之前
对div1所有的操作都属于JS线程内的操作
2 把div1放入页面之时
浏览器会发现JS的意图, 就会通知渲染线程在页面中渲染div1对应的元素.
3 把div1放入页面之后
对div1的操作都有可能会触发重新渲染
div1.id = 'newId'
可能会重新渲染, 也可能不会
div1.title = 'new'
可能会重新渲染, 也可能不会
如果连续对div1多次操作, 浏览器可能会合并成一次操作, 也可能不会
合并了
加入test.clientWidth
后, 不合并了.
- 属性同步
1 标准属性
对div1的标准属性的修改, 会被浏览器同步到页面中, 比如id, className, title等
2 data-*属性
同上
3 非标准属性
对非标准属性的修改, 则只会停留在JS线程中, 不会同步到页面里.
- Property v.s. Attribute
1 property属性
JS线程中div1的所有属性, 叫做div1的property
2 attribute也是属性
渲染引擎中div1对应标签的属性, 叫做attribute
3 区别
大部分时候, 同名的property和attribute值相等, 但是如果不是标准属性, 那么他们只会一开始时相等.同时注意attribute只支持字符串, 而property支持字符串, 布尔等类型.
36 测试
37.【JS编程接口】手写 DOM 库
37.1 视频:什么是封装
- 什么是封装
封装: 通俗来说电脑笔记本就是CPU, 内存, 硬盘, 主板, 显卡的封装, 用户只需要接触显示器, 键盘, 鼠标, 触控板等设备即可操作复杂的计算机.
接口: 被封装的东西需要暴露在一些功能给外部, 这些功能就是接口, 如USB接口, HDMI接口.设备只要支持这些接口, 即可与被封装的东西通讯, 比如键盘, 鼠标支持USB接口, 显示器支持HDMI接口
- 术语
1 库
我们把提供给其他人用的工具代码叫做库, 比如JQuery, Underscore
2 API
库暴露出来的函数或属性叫做API(应用程序接口 Application Programming Interface)
3 框架
当你的库变得很大, 并且需要学习才能看懂, 那么这个库就叫做框架, 比如Vue/React
37.2 视频:硬核写DOM库
- 对象风格
1 也叫做命名空间风格
window.dom
是我们提供的全局对象
2 增
window.dom = {
//创建节点
create(string) {
const container = document.createElement("template")
//trim(): 清除string左右两边空格
container.innerHTML = string.trim()
return container.content.firstChild
},
//新增弟弟
//因为没有insertAfter(), 所以思路: 通过获取最后的下一个插入之前, 相当于最后一个插入之后(如果没有下一个节点, 那么默认为text或者null, 最终也能实现)
after(node, node2) {
node.parentNode.insertBefore(node2, node.nextSibling)
},
//新增哥哥
before(node, node2) {
node.parentNode.insertBefore(node2, node)
},
//新增儿子
append(parent, node) {
parent.appendChild(node)
},
//新增爸爸
wrap(node, parent) {
dom.before(node, parent)
dom.append(parent, node)
}
};
VScode打开设置快捷键: command
+,
自动格式化代码:
@@@
-
为啥test直接就是对应的id为test的div
因为window.test是直接获取id名为test的方式, 可简写为test. -
运行的before, after和append为什么只执行一次
@@问题@
3 删除
//删除节点, 考虑到最新语法node.remove()会不兼容, 采用: 找到node父节点然后删除其子节点, 并返回node
remove(node) {
node.parentNode.removeChild(node)
//保留节点引用
return node
},
//删除后代(下一级)
empty(node) {
const array = []
let x = node.firstChild
while (x) {
array.push(dom.remove(node.firstChild))
x = node.firstChild
}
return array
}
4 改
//用于读写属性
//重载
attr(node, name, value) {
if (arguments.length === 3) {
node.setAttribute(name, value)
} else if (arguments.length === 2) {
return node.getAttribute(name)
}
},
//用于读写文本内容
//适配
text(node, string) {
if (arguments.length === 2) {
if ("innerText" in node) {
//IE方式
node.innerText = string
} else {
//firefox/chrome
node.textContent = string
}
} else if (arguments.length === 1) {
if ("innerText" in node) {
//IE方式
return node.innerText
} else {
//firefox/chrome
return node.textContent
}
}
},
//用于读写HTML内容
html(node, string) {
if (arguments.length === 2) {
node.innerHTML = string
} else if (arguments.length === 1) {
return node.innerHTML
}
},
//用于修改style样式
style(node, name, value) {
if (arguments.length === 3) {
node.style[name] = value
} else if (arguments.length === 2) {
if (typeof name === 'string') {
return node.style[name]
} else if (name instanceof Object) {
const object = name
for (let key in object) {
node.style[key] = object[key]
}
}
}
},
//class的添加/删除
class: {
add(node, className) {
node.classList.add(className)
},
remove(node, className) {
node.classList.remove(className)
},
has(node, className) {
return node.classList.contains(className)
}
},
//添加数据监听
on(node, eventName, fn) {
node.addEventListener(eventName, fn)
},
off(node, eventName, fn) {
node.removeEventListener(eventName, fn)
}
5 查
//获取单个/多个标签
find(selector, scope) {
return (scope || document).querySelectorAll(selector)
},
//获取父元素
parent(node) {
return node.parentNode
},
//获取子元素
children(node) {
return node.children
},
//用于获取兄弟姐妹元素
siblings(node) {
return Array.from(node.parentNode.children).filter(n => n !== node)
},
//获取弟弟
next(node) {
let x = node.nextSibling
while (x && x.nodeType === 3) {
x = x.nextSibling
}
return x
},
//获取哥哥
previous(node) {
let x = node.previousSibling
while (x && x.nodeType === 3) {
x = x.previousSibling
}
return x
},
//遍历所有节点
each(NodeList, fn) {
for (let i = 0; i < NodeList.length; i++) {
fn.call(null, NodeList[i])
}
},
//获取排名第几
index(node) {
let list = dom.children(node.parentNode)
let i
for (i = 0; i < list.length; i++) {
if (list[i] === node) {
break
}
}
return i
}
37.3 视频:总结
vscode打开多个窗口: ctrl
+shift
+n
38. 【JS编程接口】jQuery 中的设计模式(上)
38.1 创建项目
38.2 闭包 & 链式操作
38.3 实现find函数
- jQuery是构造函数吗?
1 是
因为jQuery函数确实构造了一个对象
2 不是
因为不需要写new jQuery就能构造一个对象(以前讲的构造函数都要结合new才行)
3 结论
jQuery是一个不需要加new的构造函数
jQuery不是常规意义上的构造函数, 这是因为jQuery用了一些技巧(目前没必要讲)
(口头上)jQuery对象是指jQuery函数构造出来的对象, jQuery是一个函数.
38.4 $div.find返回新的api对象
定义const必须要赋值.
38.5 实现end函数
38.6 实现each/parent/ children
38 【JS编程接口】jQuery 中的设计模式(下)
39.1 window.$ = window.jQuery
多等号是从右向左执行
39.2 $div v.s. div
变量使用$开头, 则表示该变量为jQuery对象, 正常写的话一般来说是DOM对象.
39.3 jQuery.prototype
原型的本意就是节约内存
JS中只有浅复制, 没有深复制.
39.4 jQuery.prototype 续
- 构造prototype:
1 变量问题
2 添加constructor
39.5 设计模式是什么
39.【JS编程接口】DOM事件和事件委托
39.1 视频:捕获与冒泡
2002年, W3C发布标准, 文档名为DOM Level 2 Events Specification, 规定浏览器应该同时支持两种调用顺序, 首先 爷爷->爸爸->儿子(由外到内找监听函数, 叫事件捕获), 然后按儿子->爸爸->爷爷(由内到外找监听函数, 叫事件冒泡).有监听函数就调用, 并提供时间信息, 没有就跳过.
开发者自己选择事件放在捕获阶段还是冒泡阶段.
39.2 视频:W3C事件模型
39.3 视频:target, currentTarget, 取消冒泡
39.4 视频:如何阻止滚动
39.5 视频:自定义事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id=div1>
<button id=button1>点击触发 frank 事件
</button>
</div>
</body>
</html>
button1.addEventListener('click', ()=>{
//创建自定义时间frank
const event = new CustomEvent("frank", {"detail":{name:'frank', age: 18}})
button1.dispatchEvent(event)
})
button1.addEventListener('frank', (e)=>{
console.log('frank')
console.log(e)
})
39.6 视频:事件委托
39.7 视频:扩展内容(玄学)
40.【项目】前航项目介绍
41.【项目】前端导航站点-上
41.1 视频:项目介绍
41.2 视频:制作手机页面
41.3 视频:制作PC页面
41.4 视频:写HTML和CSS
41.5 视频:使用Icon
41.6 视频:导入图片
41.7 视频:创建站点
- 引入jQuery
1通过bootcdn, 选择min.js的链接到index.html的main.js之前.
2 判断是否引入成功jQuery
console.log($)
console.log(jQuery)
- 寻找ul中的最后一个li标签
//li.last失效, 改用li:last
const $lastLi = $siteList.find('li:last')
41.8 视频:使用hashMap和localStorage
- 为什么header和main等标签都需要加class.
防止以后有人来写该项目代码的时候, 直接用header等标签来添加样式, 这样会使得叠加了之间其他地方的样式.
const hashMap = []
正常情况下会是一个全局变量, 因为在parcel中所以就多了一层作用域使得hashMap不是全局变量, 在parcel中全局变量写法window.hashMap
- 清除除了最后一个li的所有li
// li:not(.last)失效, 改用li:not(:last)
$siteList.find('li:not(:last)').remove()
- 勾选chrome浏览器中的Preserve log可以保留上个网站的console
- localStorage存储数据
@@@
1 localStorage中的数据什么情况下会消失
第一种情况(常见): 清除游览器的cookie
第二种情况:电脑的硬盘满了, 然后chrome会智能删除一些缓存文件.
第三种情况: chrome的无痕窗口模式
@@细节@
2 存储数据
localStorage只接受string类型
//转为string
const string = JSON.stringify(hashMap)
//数据存入localStorage
localStorage.setItem('x', string)
3 获取数据
//取localStorage数据
const x = localStorage.getItem('x')
//转为object
const xObject = JSON.parse(x)
42.【项目】前端导航站点(下)
42.1 视频:定一个小目标
@@@
1 定一个目标, 实现它
2 没有完美, 做到看不出bug
@@箴言@
42.2 视频: 处理logo
处理英文字母变为大写:
- js方法
.toUpperCase() - css方法
text-transform: uppercase;
42.3 视频: 简化URL
42.4 视频: 删除功能
42.5 视频: PC网页(媒体查询)
42.6 视频: PC网页样式优化
42.7 视频: PC样式影响了手机样式
42.8 视频: 键盘事件
42.9 视频: 发布到github
- parcel build错误:
/Users/bens/Documents/selfStudy/jiRenGu/fangFang-FrontEndSystem/41-FrontEndNavigationSite(1)/navigation/src/index.html: Error in parsing SVG: Unbound namespace prefix: "xlink"
Line: 0
Column: 66
Char: >
添加–no-minify
parcel build src/index.html --no-minify
- 调整parcel生成的index.html下的路径
先查找命令
parcel build --help
然后找到命令
记得清空dist目录后再执行命令
parcel build src/index.html --no-minify --public-url ./
./
可以用网站绝对路径来替代
42.10视频: yarn build 一键发布
首先
yarn init -y
在文件package.json添加
{
"scripts": {
"build": "rm -rf dist && parcel build src/index.html --no-minify --public-url ./"
},
"devDependencies": {
"cssnano": "^4.1.10"
},
"name": "navigation",
"version": "1.0.0",
"main": "index.js",
"repository": "https://github.com/bens1320/Navigation.git",
"author": "bens1320 <yu1piao09@163.com>",
"license": "MIT"
}
42.11 文章: 优秀的项目 = 用心的项目
很多同学认为前航是一个小项目,不能放在简历里。
这是一个错误的观念。能不能放在简历里完全取决你你有多用心。
比如有一个前端就把导航网页做成了这样:https://www.vipbic.com/rank.html
代码很简单,但是你就是能看出他的用心,不是么?
当然我不是让你也花费大量时间把前航做成他那样,但是我喜欢你在做项目的时候能够「用心」。
就说这些,自己体会一下~
43. 打console.log
44.【前后分离】AJAX 的原理
44.1 视频:下载server.js
AJAX(Async JavaScript And XML): 用JS发送请求和接收响应.
- AJAX是浏览器的功能(由微软IE5推出)
原本是浏览器的功能(发请求和收响应), 后面浏览器开放出来, 在window上加了个XMLHttpRequest函数, 用这个构造函数(类)可以构造出一个对象, JS通过它实现发请求和收响应.
- node-dev: 自动刷新代码
安装
yarn global add node-dev
44.2 视频:复习server.js的用法
44.3 视频:挑战1 - 加载CSS
44.4 视频:挑战2 - 加载JS
44.5 视频:挑战3 - 加载HTML
动态加载html. 一般不用iframe了, 比较臃肿.用ajax来请求, 比较轻量.
44.6 视频: onreadystatechange事件
专业的前端: 不会用onload和onerror(图片, css, js等, 没想到监听ajax)事件( 因为历史原因:没有很好匹配ajax,先发明这2个事件, 然后再发明的ajax, 因此报错监听不到404错误).
因此用onreadystatechange来替代上面2个事件函数.
44.7 视频: 挑战4 - 加载XML
DOM不仅仅可以操作HTML还可以操作XML.
44.8 视频: 挑战5 - 加载JSON
JSON(JavaScript Object Notation): 是一门语言, 它不是编程语言, 而是标记语言(HTML, XML, Markdown).
- 支持的数据类型
string - 只支持双引号, 不支持单引号和无引号
number - 支持科学计数法
bool - true和false
null - 没有undefined
array
object - 比较常用
就只有6种, 注意跟JS的7种数据类型区别开来. 不支持函数, 也不支持变量(所以也不支持引用)
JSON是借鉴JS发明的.
-
JSON.parse
-
JSON.stringify
由于JS的数据类型比JSON多, 所以不一定能成功.
44.9 视频: 加载分页
44.10 视频: 总结
45.【前后分离】异步与Promise
45.1 视频:异步?回调?
打电话叫外卖是异步加回调.
回调是一个名词.
回调: 写了却不用, 给别人调用的函数, 就是回调「回头你调用一下呗」, 有将来的含义, 类似我回头请你吃饭.
1 没有调用f1
2 将f1传给f2(别人)
传的方式1(常见):
function f1 (){}
function f2 (){}
f2(f1)
传的方式2:
f2.f1 = ()=>{}
例子: AJAX中
request.onreadystatechange = () => {}
//等价于
function onreadystatechange(){}
request.setCallback(onreadystatechange)
3 f2调用了f1
45.2 视频:异步函数的例子
一个函数里面可能有2个函数, 不要看见return认为是该函数的return.
这样得不到摇色子的返回值,需要用回调函数.
面试题:
45.3 视频:Promise的用法
如果异步任务有两个结果, 成功或失败, 怎么办?
Promise/A+规范
析构赋值
const {success, fail} = options
//等价于
const success = options.success
const fail = options.fail
function缩写有this, 箭头函数没有this
success(response){}
//等价于
success: function (response){}
以前用jQuery.ajax
然后.done是因为前端promise规定没制定之前, jQuery作者自己对比promise规范来自己设定的. 目前来看应该用.then
现在用axios
参考链接
45.4 视频:总结
Promise不可以取消.
Axios发明canceltoken(编号)来终止ajax请求.来达到Promise可以取消的效果.
Promise是前端解决异步问题的统一方案.
46.【前后分离】跨域、CORS、JSONP
46.1 视频:同源策略
跨域: 面试必必必问,菜逼必定不会答.
可用window.origin
或location.origin
可查看当前网站的源
同源策略是浏览器的功能: 不同源的页面之间, 不准互相访问数据. 目的是为了保护用户隐私.
referer不同. 可通过开发者工具查看.
46.2 视频:创建两个server.js
46.3 视频:修改hosts
46.4 视频:演示跨域被阻止
通过ajax访问外网资源都会被阻止.
46.5 视频:解决方法一:CORS
header中加入: Access-Control-Allow-Origin: 允许指定网站访问
IE: 6 7 8 9 不支持CORS, 因此出现了JSONP
46.6 视频:解决方法二:JSONP
callback是jsonp的函数名
46.7 视频:referer和functionName
46.8 视频:封装JSONP
46.9 视频:面试回答JSONP
46.10 视频:跨域其他方案
47.【前后分离】静态服务器
47.1 视频:静态服务器
多种类型转换可以用hash表
47.2 视频:处理错误
错误用try
47.3 视频:问答
48【前后分离】Ajax 实战:Cookie、Session
48.1 视频:动态服务器是什么
静态服务器: 没有请求数据库, 就是静态服务器.
动态服务器: 请求了数据库, 就是动态服务器.
48.2 视频:怎么做用户注册
48.3 视频:获取post数据
48.4 视频:使用cookie标记用户
48.5 视频:使用cookie记录user id
cookie也可以前端设置, 但是不建议, 一般都是后端设置.
48.6 视频:使用session会话保存用户信息
防篡改user_id:
1 加密(JWT)
2 把信息隐藏在服务器(session) 一般用文件保存