一.检测数据的类型
1.typeof检测类型方式
console.log(typeof 1) // 'number'
console.log(typeof 'haha') // 'string'
console.log(typeof true) // 'boolean'
console.log(typeof undefined) // 'undefined'
console.log(typeof null) // 'object'
面试题:typeof null 为"object",
原因 :是因为不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object"
2.constructor属性检测类型方式
-
数据.constructor === Array 代表数据是数组
-
数据.constructor === Object 代表数据是对象
-
数据.constructor === RegExp 代表数据是正则
-
数据.constructor === Date 代表数据是日期对象
-
无法判断Null 因为null无法访问constructor
1.普通函数
function Person(){
}
console.log(Person.prototype)
console.log(Person.prototype.constructor)
// 函数的prototype属性里面的constructor就是函数自身
console.log(Person.prototype.constructor === Person)
2.构造函数
var p1 = new Person()
console.log(p1.__proto__)
console.log(p1.__proto__ === Person.prototype)
console.log(p1.__proto__.constructor)
// 任何实例访问constructor属性得到是构造函数自身
3.instanceof检测数据类型方式
-
数据 instanceof 构造函数
-
数据 instanceof Array
-
数据 instanceof RegExp
-
数据 instanceof Date
var arr = [1,2,3,4]
console.log(arr.constructor === Array) // arr的构造函数是Array
console.log(arr instanceof Array) // arr是不是Array的实例
4.万能检测方式
Object.prototype.toString.call(数据)
{}.__proto__ === Object.prototype
{}.toString.call(数据)
5.判断是不是数组
Array.isArray(数据)
二.数据类型的分类
-
基础数据类型: string number boolean undefined null
-
复杂数据类型 :array object function regExp Date...
三.es5构造函数+原型+面向对象
-
// 定义
function 构造函数(){
}
-
// 利用构造函数实例化对象
var 实例 = new 构造函数()
// es5 构造函数
function Person(){
// 利用this给对象自身添加属性
this.x = 1
this.y = 2
}
// 利用prototype来添加方法
Person.prototype.test = function(){
}
var p1 = new Person()
console.log(p1)
四.es6的面向对象
// 定义
class 类名{
}
// 利用类来实例化对象
var 实例 = new 类名()
五.前后端交互---ajax篇
1.http协议
-
http协议:超文本传输协议, 主要是约束我们前后端数据交互的行为
-
数据交互:前后端相互发送数据的过程
-
前端:又称客户端 浏览器、app、 小程序
-
后端:又称服务端
http流程(面试热身题) important!!!
1.前后端发送数据之前,需要建立链接
这个过程不需要前后端写代码实现,一般软件就已经自带了
三次握手:
目的: 为了保证前后端双方都能够正常的收发信息
第一次握手 客户端向服务端发送一个seq=a 服务端接收到这个信息
服务端知道客户端能够正常的发送信息
服务端知道服务端能够正常的接收信息
第二次手误 服务端接收到seq 对客户端返回一个ack=a+1和seq=b 客户单接收到这个信息
服务端知道客户端能够正常的发送信息
服务端知道服务端能够正常的接收信息
客户端知道客户端能正常接收信息
客户端知道客户端能正常发送信息
客户端知道服务端能正常发送信息
客户端知道服务端能正常接收信息
第三次握手 客户端发送一个ack=b+1 服务端接收到这个信息
服务端知道客户端能够正常的发送信息
服务端知道服务端能够正常的接收信息
服务端知道服务端能够正常的发送信息
服务端知道客户端能够正常的接收信息
客户端知道客户端能正常接收信息
客户端知道客户端能正常发送信息
客户端知道服务端能正常发送信息
客户端知道服务端能正常接收信息
2.前端需要向后端发送一个请求(request)
这个是需要前端写代码实现了
表单: 在提交数据的同时会跳转到后端的地址
表单点击后具有自动刷新网页的功能
ajax: 在提交数据的同时会在当前页面 不会发生跳转
每一个请求都有一个请求报文,可以通过这个报文去判断数据有没有发送 发送的对不对
network-->找到某一个请求点击-->headers-->request headers
请求行:
请求方式 请求地址 请求协议
请求头:
键值对结构
请求空行:
看不见 但是存在
区分请求头和请求体
请求体:
具体的请求数据 要发给后端的数据
新版本的浏览器在payload里面查看
老版本的浏览器在请求报文的最下面
3.后端需要给前端做出响应(response)
响应需要后端写代码实现的
每一个响应都有响应报文 这个响应报文可以通过这个响应的报文判断数据有没有给或者给的对不对
响应行:
协议 状态码 状态文字
响应头:
键值对结构
响应体:
具体后端响应的数据
preview:
此处看到的数据是经过浏览器解析
response:
看到的是源数据
4.断开链接
这个断开的过程是软件自带的
四次挥手
目的:确保双方都知道对方即将断开
第一次挥手: 客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
第二次挥手: 服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表 明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
第三次挥手: 如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服 务端处于 LAST_ACK 的状态。
第四次挥手: 客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报 文之后才会进入CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
2.ajax的基本使用流程--get请求方式
// 创建ajax实例
var xhr = new XMLHttpRequest()
// 使用open配置请求信息
xhr.open('get', 'http://localhost:8888/test/first', false)
// 发出请求
xhr.send()
// 获取后端返回的数据
console.log(xhr.responseText)
get方式
携带的参数需要拼接在地址的后面
格式`?参数名=数据&参数名=数据&...`
3.ajax的基本使用流程--post请求方式
var xhr = new XMLHttpRequest()
xhr.open('post', 'http://localhost:8888/test/fourth', false)
// post方式发送数据之前需要设置参数的格式
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// post方式发送的数据需要放在send里面 不要第一个问号
xhr.send('name=jack&age=18')
// 获取后端返回的数据
console.log(xhr.responseText)
携带参数需要放在send函数里面发送
格式`参数名=数据&参数名=数据&...`
在请求发出之前需要加上一行配置
`xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')`
4.xhr.open详解
xhr.open('post', 'http://localhost:8888/test/fourth', false)
xhr.opne 用来配置请求信息具体如下:
第一个参数 'post'/'get'是ajax的请求方式
第二个参数 是ajax的请求接口
(1)http://localhost:8888 是基准地址
(2)/test/fourth 是接口地址
第三个参数 false表示同步,true表示异步
同步: 代码执行的方式都是从上往下依次执行,上面的代码没有执行结束 下面不会执行的。这样如果我们有一段代码执行 的时间比较长或者无法预估,可能导致程序阻塞
异步: 当js遇到执行时间比较长或者无法预估,js会把这段程序放到另一个队列,等所有同步的代码执行完成之后才会执 行异步的代码
5.使用ajax登录的小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form>
<!-- 此处的name -->
<input type="text" placeholder="请输入用户名" id="username"><br>
<input type="password" placeholder="请输入密码" id="password"><br>
<button id="loginBtn">登陆</button>
</form>
<script>
var userInput = document.getElementById('username')
var passInput = document.getElementById('password')
// 绑定点击事件 点击按钮提交数据
document.getElementById('loginBtn').onclick = function(e){
var evt = event || e
evt.preventDefault()
console.log('以ajax方式提交')
var xhr = new XMLHttpRequest()
xhr.open('post', 'http://localhost:8888/users/login', false)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 获取用户名
let xm = userInput.value
// 获取密码
let mm = passInput.value
xhr.send(`username=${xm}&password=${mm}`)
console.log(xhr.responseText)
}
</script>
</body>
</html>
6.封装同步ajax
function ajax(type, url, data=''){
var xhr = new XMLHttpRequest()
if(type==='get'){
// get方式请求参数是拼接在地址栏后面
xhr.open(type, url+data, false)
xhr.send()
}else{
// post方式放在send里面
xhr.open(type, url, false)
// 加上一行配置
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 裁去第一个问号
xhr.send(data.slice(1))
}
// 将后端返回的数据作为返回值
return xhr.responseText
}
调用:var goodsList = ajax('get', 'http://localhost:8888/goods/list')
console.log(goodsList)
var res = ajax('post', 'http://localhost:8888/users/login', '?username=jack&password=123456')
console.log(res)
7.封装异步ajax
function ajax(options){
// 定义一个默认的配置
const defaultOptions = {
type: 'get',
data: ''
}
// 传入的配置和默认的配置合并
const opt = {...defaultOptions, ...options}
// {a:1, b:2, c:3} ---> '?a=1&b=2&c=3'
// 传入的data对象拼接成一个参数格式的字符串
if(opt.data.constructor === Object){
let tmp = '?'
for(let key in opt.data){
tmp += `${key}=${opt.data[key]}&`
}
opt.data = tmp.slice(0, -1)
}
var xhr = new XMLHttpRequest()
if(opt.type==='get'){
// get方式请求参数是拼接在地址栏后面
xhr.open(opt.type, opt.url+opt.data, true)
xhr.send()
}else{
// post方式放在send里面
xhr.open(opt.type, opt.url, true)
// 加上一行配置
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 裁去第一个问号
xhr.send(opt.data.slice(1))
}
xhr.onload = function(){
// 在load里面调用callback 调用callback实际上就是在调用传过来的aa
// 把后端返回的数据当成一个参数传给callback
opt.success(xhr.responseText)
}
}
调用 //当type为get时可以默认不写
ajax({
type: 'post',
url: 'http://localhost:8888/users/login',
data: '?username=jack&password=123456',
// data: {
// username: 'jack',
// password: 123456
// },
success: function(res){
console.log(res)
}
})
8.json与js的数据转换
JSON的含义:通用的数据格式,本质上是一种带有一定格式要求的字符串
!json是所有语言中都具有一种格式
前端把数据发送给后端
- 需要把前端数据格式转换成json的格式在发送给后端
+ json格式 = JSON.stringify(js数据)
后端把数据响应给前端
- 后端返回的json格式前端需要解析成js格式
+ js格式 = JSON.parse(json格式)
js和json的转换方式
js数据 = JSON.parse(json格式)
json数据 = JSON.stringify(js数据)
9.跨域问题
-
JSONP解决跨域问题
script标签
利用script去请求别人的服务器 script可以请求别人的服务器 别人服务器响应的任何数据script都会当成js来解析
这种方式需要后端的配置 需要后端返回一定格式的数据
var 变量名 = 后端返回数据
函数名(后端返回数据)
前端就可以定义一个同名的函数 去接收后端返回的数据
缺点:
需要后端的配合 后端需要返回一定格式的数据
只支持get方式请求
-
配置代理
-
后端可以放开CORs(这样的话,安全性会大大的降低)
六.Promise
1.主要作用
主要为了解决异步获取数据的问题, 在promise之前 js获取一些异步的数据都是采用回调函数的方式
2.回调地狱
一个回调函数里面嵌套了另一个回调函数,回调函数层层嵌套
3.promise使用时注意的事项
-
在创建实例的时候 Promise需要传递参数
-
这个参数必须是一个函数类型 可以是一个普通函数 也可以是箭头函数
4.promise传参时注意的事项
-
传入回调函数自带两个参数
-
这两个参数也是一个函数
-
resolve 只要调用resolve 实例状态就会变为成功
-
reject 只要调用reject 实例状态就会变为失败
5.实例状态的不同
-
每一个原型方法里面都需要传入一个回调函数
-
then的回调函数在实例状态变为成功的时候才会调用
-
catch的回调函数在实例状态变为失败的时候才会调用
-
无论是成功还是失败都会调用finally里面回调函数
6.promise的状态
-
一个promise实例最终只能有一个状态 要么成功 要么失败
-
在调用reject的时候可以传递一个数据 这个数据就是实例失败的结果 叫做拒因 reason
-
在调用resolve的时候可以传递一个数据 这个数据就是实例成功的结果 叫做终值value
7.语法
基于面向对象封装 构造函数(Promise)
实例的状态
pending 等待
fulfilled 成功
rejected 失败
实例的结果
var p1 = new Promise(function(){})
console.log(p1)
var p2 = new Promise(()=>{})
console.log(p2)
实例的原型方法 then catch finally
var p1 = new Promise(function (resolve, reject) {
resolve();//成功
});
// 每一个原型方法里面都需要传入一个回调函数
p1.then(function () {
console.log("then的回调函数被调用了");// then的回调函数在实例状态变为成功的时候才会调用
});
p1.catch(function () {
console.log("catch的回调函数被调用了");
});
p1.finally(function () {// 无论是成功还是失败都会调用finally里面回调函数
console.log("finally的回调函数被调用了");
});
var p2 = new Promise((resolve, reject) => {
reject();//失败
});
p2.then(function () {
console.log("then的回调函数被调用了");
});
p2.catch(function () {
console.log("catch的回调函数被调用了");// catch的回调函数在实例状态变为失败的时候才会调用
});
p2.finally(function () {// 无论是成功还是失败都会调用finally里面回调函数
console.log("finally的回调函数被调用了");
});
8.作用
可以替代回调函数获取异步的数据
七.请求状态码
-
xhr. status
-
200: 成功
-
301: 永久重定向
-
302: 临时重定向
-
304: 使用的是缓存的数据
-
305: 使用代理
-
400: 泛指一切客户端异常 请求方式不对 请求地址不对 请求参数没发或者发错了 请求参数的格式不对
-
401: 未授权(你要登录的网站需要授权登录)
-
403: 禁止访问 服务器拒绝了你的请求
-
404: 网页走丢了 服务器找不到你请求的 URL
-
500: 服务器内部错误
-
503: 服务器当前不可用(过载或者维护)
-
505: 请求的协议服务器不支持
八.promise的方法
-
链式调用: 每一次调用then catch finally之后都会返回一个新的promise实例
-
实例方法(原型方法)
-
使用的时候需要得到一个实例 利用实例来调用
-
then/catch/finally
-
静态方法
-
使用的时候直接通过构造函数或者类来调用的
-
all/race/allSettled/any
Promise.all()
Promise.any()
Promise.allSettled()
Promise.race()
// 这些方法直接通过Promise的构造函数来调用
all方法
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('11')
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('22')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('33')
}, 1000)
})
// all方法接受一个数组类型的参数
// 该数组里面每一项数据都是一个promise实例
// 方法调用之后会有一个返回值 返回的是一个新的promise实例
// 数组里面所有实例状态变为成功 就会调用then方法
// 数组里面只要有一个实例状态变为失败 就会调用catch方法
Promise.all([p3, p1, p2]).then(res=>{
// res是一个数组 数组里面是每一个实例的终值
console.log(res, 'then')
}).catch(err=>{
console.log(err, 'catch')
})
allSettled方法
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('11')
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('22')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('33')
}, 1000)
})
// allSettled无论成功还是失败都会进入then
Promise.allSettled([p1,p2,p3]).then(res=>{
console.log(res, 'res')
// 能够拿到所有实例的结果 这个结果是经过处理 标记一个状态
const successList = res.filter(item=>item.status === 'fulfilled')
console.log(successList)
const errorList = res.filter(item=>item.status === 'rejected')
console.log(errorList)
})
any方法
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('11')
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('22')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('33')
}, 1000)
})
Promise.any([p1,p2,p3]).then(res=>{
console.log(res, 'then')
})
race方法
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('11')
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('22')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('33')
}, 1000)
})
Promise.race([p1,p2,p3]).then(res=>{
console.log(res, 'res')
}).catch(err=>{
console.log(err, 'err')
})
九.数组的高级用法(es6)
// 伪数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
for(let i=0;i<arrayLike.length;i++){
console.log(i)
console.log(arrayLike[i])
}
// 伪数组--> 真数组
const arr = Array.from(arrayLike)
arr.forEach(item=>{
console.log(item)
})
// 多维数组扁平化
var arr3 = [1,2,[3,4], 5]
var arr2 = [1,3,[2,3,4, [5,6,7,[4,5,65,7,8,[4,3,43]],5]]]
arr2 = arr2.flat(Infinity)
console.log(arr2)
十.对象遍历的方法(es6)
const person = {
a:1,
b:2,
c:3,
d:4
}
// 最常用 for...in 循环
for(let key in person){
console.log(key)
console.log(person[key])
}
// Object.keys()
Object.keys(person).forEach((item, index)=>{
console.log(item)
})
console.log('-----------------')
for(let key of Object.keys(person)){
console.log(key)
}
// Object.getOwnPropertyNames(obj)
console.log(Object.getOwnPropertyNames(person))
十一.对象合并的方法(es6)
const person = {a:1, b:2, c:3}
const person2 = {aa:1, bb:2, c:30}
// 1. 展开运算符
const newPerson = {...person, ...person2}
// 2. Object.assign()
Object.assign(person, person2) // 将person2合并到person
console.log(person)
// Object.assign()不可以合并数组
var arr = [1,2,3]
var arr2 = [11,22,33]
const newArr = Object.assign(arr, arr2)
console.log(newArr)
判断对象是否为空
// 1. 判断对象为空 可以将对象转换成json 利用字符串比较规则
function move(obj){
console.log(JSON.stringify(obj))
if(JSON.stringify(obj) === '{}'){
// 代表空对象
console.log('是空对象')
}else{
// 不为空
console.log('不是空对象')
}
}
// 2. 利用for..in方法
function isEmptyObj(obj){
for(let key in obj){
return false
}
return true
}
console.log(isEmptyObj({}))
// 3. es的方法 Object.keys()
// 此方法可以将对象里面所有的键都取出 放到一个数组里面
const person = {}
const arr = Object.keys(person)
console.log(arr)
// function isEmptyObj2(obj){
// return Object.keys(obj).length === 0
// }
const isEmptyObj2 = obj => Object.keys(obj).length === 0