PHP
php:一门后台语言
php不是必须会写,但是你需要了解流程
php是世界上最好的语言(现在被大环境抛弃了)
php性能要比java高,java要比php稳定并且java可以多平台应用,php只能配合web
必须在服务器(电脑)下运行,依赖于环境(适合某个语言)
Apache环境或者Nginx环境,phpstudy(集成环境)俩都带可以任意切换
安装目录的www就是我们的服务器环境
有一个phpMyAdmin文件夹这就是我们的数据库文件(不要动),其他文件随意,想删除就删除
www里面的php文件和html文件等其他文件,都是可以被用户访问的
怎么打开www里面的文件:localhost:端口/路径/文件名
c d e f这种打开方式都是本地方式,不代表服务器
apache如果红色启动不了,记得改端口,
其他选项菜单 -> phpstudy设置 -> 端口常规设置
mysql启动不了,把以前启动的mysql停掉,再启动当前数据库,
任务管理器找到mysql字样的程序,鼠标右键终止程序运行
安装phpstudy
下载之前,要关闭杀毒软件(会被当成病毒自动清理)
不要安装有中文目录的文件夹内
安装过程中,还有一个路径选择(必须记住),没记住也不慌(有方法)
怎么证明安装成功(有一个启动界面)
怎么证明启动成功(启动界面里面的apache, mysql都是绿色就可以了)
只要把文件放到phpstudy里面的www目录下就可以使用,这就是我们的服务器目录
使用服务器
怎么确定服务器是可以使用的
必须保证apache没有冲突(红色启动不了就是冲突)
解决冲突改端口(其他选项菜单->phpStudy设置->端口常规设置),默认80被占用了所有才启动不了apache
找到安装目录里面的www文件夹,除了phpMyAdmin文件夹其他文件都删除
WWW目录里面新建一个index.html文件用来测试
在浏览器地址栏里修改成:localhost:端口 / 127.0.0.1:端口号
默认端口80可以省略不写
使用php
WWW目录下新建一个以.php后缀名的文件,就为php文件
不支持浏览器打开,只能在地址栏里输入地址
localhost:端口/文件.php || localhost:端口/文件夹/文件.php
文件内容写内容 <?php 代码... ?>
建立数据库
查看www下有没有phpMyAdmin这个文件夹
地址栏里面输入:localhost:端口/phpMyAdmin
账户名:root
密码:root
创建数据库
1. 点击数据库
2. 创建数据库:输入名字 点击创建
3. 创建表:输入表名 字段数 点击创建
4. 把表字段都填写上,注意数据格式,填写长度,id 把 A_I 勾选上
5. 新建一些数据,去浏览里查看
6. 把username设置成唯一
php基本语法
必须加分号
中文(设置字符集):header('content-type:text/html;chraset=utf-8');
输出内容:echo 只能输出字符串
变量:$name = value
判断:if switch
循环:for while
函数:function fn() {}
数组:array(12, 5, 7, 99, 103)
对象:array("msg" => "测试文字", "data" => array(100, 200, 300))
转字符串:json_encode()
ajax
ajax:async(异步) javascript and xml(严格的html格式数据)
概念:
一般情况下,前端和后台进行交互都需要页面跳转才能获取到新的数据,然后重新跳转到一个页面,将数据加载到页面上。这对资源的消耗很大,用户体验感也不是很好。
所以js提供了一个不需要刷新页面就能加载后台数据的技术:AJAX
AJAX,全称:`async javascript and XML`,是一个异步执行的和后台交互的技术。
- 前后端交互
客户端和服务端进行通讯- 目的:如果想服务端发送一段消息,得到服务端返回的结果
- 比如: 登录, 发送 用户名 和 密码,接收返回 登录成功 或 登录失败
- 比如: 列表, 发送 第几个 和 一页多少条数据,接收返回 列表数组
ajax特点
优势:
不需要插件支持(一般浏览器且默认开启 JavaScript 即可)
用户体验极佳(不刷新页面即可获取可更新的数据)
提升 Web 程序的性能(在传递数据方面做到按需放松,不必整体提交)
减轻服务器和带宽的负担(将服务器的一些操作转移到客户端)
缺点:搜索引擎的支持度不够(因为搜索引擎爬虫 *暂时* 还不能理解 JS 引起变化数据的内容)
ajax语法
ajax进行交互
1. 创建一个请求,创建ajax对象(专门用于发送ajax请求的'工具')
let xhr = new XMLHttpRequest()
XML: 一种数据格式
Http: 使用http方式传输
Request: 请求
2. 连接(配置本次请求的信息)
xhr.open('get', 'server/01.php', true)
参数1: 请求方式 get 获取 / post 发送
参数2: 请求地址
参数3: 是否异步,同步false:依次执行,异步true:一起执行(结果不一定谁先回来)
3. 发送请求(异步)
xhr.send()
4. 接收响应(异步)
xhr.onload = function () {
// response 响应 结果
console.log(xhr.responseText)
}
触发事件: 当这个ajax请求结束并接收到响应后,触发
在xhr 对象中,有一个叫做 responseText的属性, 记录了后端返回的信息
和后台配合,后台必须提供的东西:
1. 请求地址: server/week.php
2. 请求方式: get
3. 请求参数: n=数字
[小提示]报错有“XMLHttpRequest”那就证明地址有问题,改成服务器地址
1. 单词错误
2. 地址错误 www为节点,www加前面的地址都替换成localhost:端口
3. 相对路径地址错误,404告诉我们地址有问题
new XMLHttpRequest() 不兼容低版本浏览器ie 6 7 8
new ObjectXActive('Microsoft') 兼容ie低版本浏览器使用
-
ajax_get案例
1. 创建 let xhr = new XMLHttpRequest() 2. 连接 get方式参数是拼接到地址后面的(规定),地址后面写一个? 问号后面的作为参数 参数格式:a=1&b=2&c=3 xhr.open('get',`server/week.php?n=${new Date().getDay()}`, true) 3. 发送 xhr.send() 4. 接收 xhr.onload = function () { console.log(xhr.responseText) } -
ajax_post案例
请求地址:server/login.php 请求方式:post 请求参数:username=string&password=string 1. 创建 let xhr = new XMLHttpRequest() 2. 连接 xhr.open('post', 'server/login.php', true) 3. (post再发送之前必须设置请求头) xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') 4. 发送 post需要把参数放到send里面 xhr.send(`username=admin&password=123456`) 5. 接收 xhr.onload = function () { console.log(xhr.responseText) }
请求头
请求头:
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
application/x-www-form-urlencoded 使用表单文本方式提交
application/json 提交的 json格式文本
text/xml 提交普通文本
multiline/form-data 使用表单的文件域方式
请求头:(根据后台要求)
默认:xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.setRequestHeader('Content-Type', 'application/json')
ajax 状态码
-
ajax 状态码 -
xhr.readyState -
用来表示一个 ajax 请求的全部过程中的某一个状态
readyState === 0: 表示未初始化完成,也就是 open 方法还没有执行 readyState === 1: 表示配置信息已经完成,也就是执行完 open 之后 readyState === 2: 表示 send 方法已经执行完成 readyState === 3: 表示正在解析响应内容 readyState === 4: 表示响应内容已经解析完毕,可以在客户端使用了 -
只有当
readyState === 4的时候,才可以正常使用服务端给我们的数据
http 状态码
- ajax 对象中有一个成员叫做
xhr.status - 这个成员就是记录本次请求的 http 状态码
- 在事件处理函数中判断 http状态码为200且ajax状态码为4 就可以获取到响应数据,说明本次请求正常完成
readyStateChange事件
- ajax 对象中有一个事件,叫做
readyStateChange事件 - 这个事件是专门用来监听 ajax 对象的
readyState值改变的的行为 - 触发机制:当**
readyState** 值发生变化的时候触发事件函数 - 语法:
xhr.onreadyStateChange = function () {}
responseText
- ajax 对象中的
responseText成员 - 用来记录服务端给我们的响应体内容的
请求方式
前后端传递信息的方式
本质的区别 就是 语义的区别
*get 偏向于获取的语义(获取列表数据)
*post 偏向于提交的语义(登录)
*put 偏向于提交的语义(偏向于提交插入)(注册)
*delete 偏向于获取的语义(偏向于删除)(删除)
*pacth 偏向于提交的语义(偏向于提交修改)(更新)
head 偏向于获取的语义(就是为了获取响应头信息)
connect 保留请求,管道连接改为代理连接使用
options 偏向于获取的语义(获取服务器的信息,需要服务器允许)
get和post的区别
*get和post的区别
1.语义化区别
GET 偏向于获取
POST 偏向于提交
2.传递数据的方式(携带信息位置)
get请求直接在地址栏后面拼接
post请求在请求体里面传递
3. 携带信息的格式
GET: 只能使用查询字符串格式
POST: 不限制任何格式,但是你要在请求头中的 content-type 说明
查询字符串: appliaction/x-www-form-urlencoded
json字符串: application/json
二进制流: multipart/form-data
4. 携带信息的大小
GET: 2KB
POST: 理论上没有限制大小,但是会服务端限制
5. 安全性
GET: 相对不安全
POST: 相对安全
6. 浏览器缓存
GET: 会被浏览器主动缓存
POST: 不会被浏览器主动缓存
token
① 用户首次登录,将输入的账号和密码提交给服务器
② 服务器对输入内容进行校验,若账号和密码匹配则验证通过,登录成功,并生成一个token值,将其保存到数据库,并返回给客户端
③ 客户端拿到返回的token值将其保存在本地(如cookie/local storage),作为公共参数,以后每次请求服务器时都携带该token(放在响应头里),提交给服务器进行校验
④ 服务器接收到请求后,首先验证是否携带token,若携带则取出请求头里的token值与数据库存储的token进行匹配校验,若token值相同则登录成功,且当前正处于登录状态,此时正常返回数据,让app显示数据;若不存在或两个值不一致,则说明原来的登录已经失效,此时返回错误状态码,提示用户跳转至登录界面重新登录
⑤ 注意:用户每进行一次登录,登录成功后服务器都会更新一个token新值返回给客户端
手动抛出异常
-
语法:
throw Error(异常信息) -
注意: 手动抛出异常会中断代码的执行
例: throw Error('我设置的错误'); -
异常捕获语法
try{代码1}catch(参数){代码2}- 先执行
代码1,如果代码1执行中有异常抛出,则执行代码2并且将异常信息传递给参数 - 如果**
代码1执行没有任何异常,则代码2不执行**
try { throw Error('我错了,你帮帮我吧'); console.log( 666 ) } catch (error) { console.log( 777 ) console.log( error ) } console.log( 123 ) 控制台: /* 777 Error: 我错了,你帮帮我吧 at 07-封装ajax函数.html:91:11 123 */
http通信协议
http: 超文本传输协议,默认端口: 80
https: 安全超文本传输协议,默认端口: 443
ajax 和 后台的通讯都是通过http完成
http规定了前后端通信的传输协议
这个约定只能 前端主动发送请求,接收后端响应
也叫做 请求-响应协议
请求:客户端给服务端发送的一个消息
响应:服务端给客户端返回的一个消息
因为只有请求了才可能会有响应,所以http也是短连接
因为每一次请求,对于服务端都是新的请求(不能和之前的请求联系起来)
也叫做无状态协议
每一个请求-响应都必须经历一个完整的http协议
1. 客户端和服务端建立连接
2. 客户端向服务端发送请求
3. 服务端向客户端返回响应
4. 断开连接
1. 基于 TCP\IP 协议的三次握手
目的是为了建立 客户端 和 服务端 的连接,确保道路通畅
2. 前端主动发送请求
把想要和后端说的事情说出来(一次只能说一个事)
前端以请求报文的形式发送所有内容
2.1 请求报文行
- 请求方式 `get`
- 请求地址 `/abc/cba`
- 传输协议版本 `http/1.1`
2.2 请求报文头(对本次请求的描述)
- host 客户端主机域名
- userAgent 客户端信息
- platform 客户端操作系统
- cookie 当你的cookie空间有信息,会自动携带
- content-type 前端请求体的数据格式
- ....
2.3 请求报文空行
- 是一个空白行
- 区分请求头和请求体
2.4 请求报文体
- 前端传递给后端的真实信息
- post 提交方法,之鞥在此处查看传递的信息
3. 后端返回响应给前端
后端准准备好给前端的信息
后端以响应报文的形式返回所有内容
3.1 响应状态行
- 传输协议版本 HTTP/1.1
- 响应状态码 200
- 状态码描述 OK
3.2 响应报文头(本次响应的描述信息)
- server 服务器版本
- date 服务器时间
- content-type 服务器响应体的数据格式
- content-Length 服务器响应体的长度
- ...
3.3 响应报文体
- 后端返回给前端的真实信息
4. 基于 TCP\IP 协议的 四次挥手
为了确保正确且安全的断开
- 三次握手
三次握手
1. 前端发送一个 '包' 给后端
2. 后端接收到前端的 '包' 以后,返回一个 '包+包' 给前端
- 后端接收到前端第一次发送的 '包' 之后
+ 后端知道 前端可以正常发送
+ 后端知道 后端可以正常接收
3. 前端接收到后端的 '包+包' 以后, 返回一个 '包+包+包' 给后端
- 前端接收到后端的 '包+包' 以后
+ 前端知道 前端可以正常发送
+ 前端知道 前端可以正常接收
+ 前端知道 后端可以正常发送
+ 前端知道 后端可以正常接收
- 后端接收到 前端发送的 '包+包+包'
+ 后端知道 前端可以正常接收
+ 后端知道 后端可以正常发送
- 四次挥手
四次挥手
1. 前端发送一个 '包' 给后端
- 告诉他: "我接收到你的响应了,可以准备断开了"
2. 后端接收到前端的 '包', 返回一个 '包+包1' 给前端
- 告诉前端: "我知道你接收到我的信息了,我准备断开了"
3. 后端再次返回一个 '包+包2' 给前端
- 告诉前端: "我已经准备好断开了,当我再次收到消息会直接断开,不在回复"
4. 前端接收到所有 '包' 之后,返回一个 '最终包' 给后端
- 告诉后端: '我已经断开了,你别回了'
- 响应状态码
以一个数字的形式 表示 本次响应的状态(成功/失败)
分为五类: 100~599
1. 100~199 表示各种连接继续(一般看不到)
100: 继续请求,前面的一部分内容服务端已经接受到了,正在等待后续内容
101: 请求者已经准备切换协议,服务器页表示同意
2. 200~299 表示各种成功
200: 响应成功,标准请求成功
201: 创建成功(一般是注册的时候,表示新用户信息已经添加到数据库)
203: 表示服务器已经成功处理了请求,但是返回的信息可能来自另一源
204: 服务端已经成功处理了请求,但是没有任何数据返回
3. 300~399 表示重定向
301: 永久重定向
302: 临时重定向
304: 缓存
305: 使用代理
4. 400~499 表示各种客户端的错误
400: 请求的语法服务端不认识
401: 未授权(你要登录的网站需要授权登录)
403: 服务器拒绝了你的请求
403: 请求地址权限不够
404: 请求地址不存在
407: 你的代理没有授权
408: 请求超时
410: 你请求的数据已经被服务端永久删除
5. 500~599 表示各种服务端错误
500: 服务器内部错误
503: 服务器当前不可用(过载或者维护)
505: 请求的协议服务器不支持
当我在浏览器地址输入地址访问的时候,都经历了哪些过程:
地址解析DNS 变成ip
三次握手 访问 接收 返回
加载html css js img video audio flash...
当网页完成请求以后,再进行ajax
只会经历三次握手
1.连接
2.接收
3.返回
四次挥手
4.断开
在哪里能看到这些步骤:
浏览器控制台里面的network 或者 网络
Fetch/XHR就是我们所有的ajax请求
名称里面都是我们的ajax请求
每一个请求里面都有
标头 载荷 预览 响应 启动器 时间 Cookie
[标头]
*常规*
前后台通用的内容
请求网址: 绝对路径
请求方法: GET / POST / PUT / DELETE 用不到的有:CONNECT / PATCH / HEAD
状态代码:
100 - 199 请求继续
200 - 299 成功
300 - 399 重定向(304缓存 也算成功)
400 - 499 客户端问题400/401/402(参数有问题) 403(禁止访问)404(肯定是地址写错了)
500 - 599 服务端问题
响应标头
后台的内容
Connection:连接方式
Content-Length:数据长度
Content-Type:内容类型
Date:服务器返回的时间
Keep-Alive:缓存属性
Server:服务器信息
X-Powered-By:后台语言版本
请求标头
Accept:数据格式
Accept-Encoding:使用什么压缩方式
Accept-Language:语言
Connection:连接方式
Cookie:前后台共享数据(缓存数据)
Host:主机地址
Referer:客户端地址
User-Agent:浏览器信息
[*载荷*]
前端给后台传的参数
[预览]
查看后台返回的数据
[*响应*]
后台返回的数据
[启动器]
前端使用文件地址和请求过程
[时间]
访问服务器的时间
[Cookie]
前后台共享数据
Promise
ES6新增的一个异步方法
解决Ajax嵌套调用问题(回调地狱)
三个状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
一个参数:回调函数,回调函数里面有两个参数(成功,失败)
一堆方法:then(成功)、catch(失败)、all(全部完成)、race(完成一个)、allSettled(所有状态)
- Promise - 承诺
- 如果你要执行异步代码,给我来执行,我承诺给你一个结果
- 用于 封装执行异步代码的
-
承诺的几个状态
pending执行中fulfilled/resolved成功rejected失败
-
promise状态变化
- 从
执行中到成功 - 从
执行中到失败
- 从
-
Promise是js中的一个构造函数
- 语法:
new Promise(function(第一个参数,第二个参数){}) - 两个参数都是回调函数
- 第一个参数(
resolve) 执行的时候,状态会变为成功 - 第二个参数(
resolve) 执行的时候,状态会变为失败
- 语法:
-
promise状态从执行中变为成功会执行的函数
- 语法:
promise对象.then(函数) - 注意: 因为resolve回调函数执行了才 触发的状态变为成功,让then中的函数执行,而且resolve调用传入的实参 会传递给then中的函数形参
- 语法:
-
promise状态从执行中变为失败也会执行的函数
- 语法:
promise对象.catch(函数) - 注意1: 因为reject回调函数执行了才 触发的状态变为失败,让catch中的函数执行,而且reject调用传入的实参 会传递给catch中的函数形参
- 注意2: 我们一般会将catch方法在then方法调用后连贯调用
- 语法:
-
-
在new Promise中执行代码的时候如果有异常抛出,则默认会将promise状态变为失败的
-
会执行catch中的函数,而且抛出的异常信息会传递给catch方法中的形参(而不会报错)
-
-
promise对象方法的的链式调用
promsie对象.then().then().then()....catch()- 一般我们只需要写一个catch就可以捕获所有的异常
- 如果then()函数没有返回新的promise对象,则所有then方法中的函数都执行,但是只有第一个then方法中的函数能接到实参
- 如果then()函数中返回了一个新的promise对象,则后续的then()方法执行都依赖这个新的promise对象
- 链式调用中 catch()方法只需要在最后书写,中间任何一个环节有异常抛出,则直接执行catch方法中的函数
-
promise对象的then方法中其实有两个回调函数
then(函数1,函数2)- 状态为成功的时候,执行函数1,失败的时候执行函数2
-
- new promise(函数) 函数中的代码执行是同步的
- promise对象的then和catch方法中的函数代码执行是异步的
console.log( 1 )
new Promise((reslove,reject)=>{
console.log( 2 )
reslove(3);
// reject('err')
console.log( 4 )
})
.then(v=>console.log( v ))
.catch(er=>console.log( er ))
console.log( 5 )
// 1 2 4 5 3
promise对象的方法
.then(f1,f2)方法中有两个参数函数- f1是处理promise对象成功的回调
- f2是处理promise对象失败的回调
- 在new Promsie(()=>{此处的函数中有一个默认的**
try-catch**语法来获取异常,但是如果是在异步中的异常则捕获不到}) .then(f1,f2)和.catch(f)在处理失败异常中 有什么区别?- **
catch**可以捕获到f1函数中的异常,但是f2不能捕获到f1中的异常
- **
.then().then().then()前一个函数return 的是非promise数据 则后一个then执行的函数形参可以接收到retuen的结果.finaly(f)方法 无论promsie对象是成功还是失败都会执行函数,但是不接受任何参数
Promise的静态方法
Promise.resolve(值)- 等价于
new Promise(resolve=>resolve(值))
- 等价于
Promise.reject(值)- 等价于
new Promise((resolve,reject)=>reject(值))
- 等价于
Promise.all(可迭代的数据)- 一般参数都会传递一个 数组,数组中是多个promise
- 会等所有promise都实行成功后 返回一个执行成功的promise对象
- 在then()函数中接受的结果是一个数组,数组中的元素是每一个promise的成功结果
- 传入的多个promsie有一个是失败的,all方法最终结果就是失败的
Promise.allSettled(可迭代的数据)- 一般参数都会传递一个 数组,数组中是多个promise
- 无论失败还是成功,都会返回结果,而且结果是一个对象
Promise.race(可迭代的数据)- 一般参数都会传递一个 数组,数组中是多个promise
- 哪一个先成功或失败,则返回对应的结果
Promise.any(可迭代的数据)- 一般参数都会传递一个 数组,数组中是多个promise
- 返回第一个成功的结果
- 但是如果全部都是失败 catch中的形参.errors[索引] 可以获取到对应promsie的异常
柯里化
- 柯里化: 函数的一种转换形式
f(a,b,c)---->f(a)(b)(c)f(a,b,c)---->f(a)(b,c)
封装ajax
-
ajax函数
function ajax(options = {}) { // 请求地址必须存在 if (!options.url || typeof options.url != 'string') throw Error('请求地址必须传递,且为字符串类型'); // 请求方式校验 if (!(options.method === undefined || /^(get|post)$/i.test(options.method))) throw Error('请求方式只能是get或post'); // 是否异步只能是布尔类型 if (!(options.async === undefined || typeof options.async == 'boolean')) throw Error('是否异步只能是布尔'); // 传递的数据类型校验只能是对象 if (!(options.data === undefined || Object.prototype.toString.call(options.data) === '[object Object]')) throw Error('携带的数据 只能是对象或undefined'); // 请求头类型校验只能是对象 if (!(options.headers === undefined || Object.prototype.toString.call(options.headers) === '[object Object]')) throw Error('请求头数据 只能是对象或undefined'); // 是否JSON转换参数校验 if (!(options.json === undefined || /^(string|json)$/i.test(options.json))) throw Error('只能是string或json'); // 成功回调函数的 校验 if (!(options.success === undefined || typeof options.success == 'function')) throw Error('成功的回调 只能是 函数类型'); // 失败回调函数的 校验 if (!(options.error === undefined || typeof options.error == 'function')) throw Error('失败的回调 只能是 函数类型'); // 2. 准备默认值---用于发起请求的数据 const defInfo = { url: options.url, method: options.method || 'get', // 'get'为默认值 async: typeof options.async === 'boolean' ? options.async : true, // true为默认值 data: options.data || {}, // {} 为默认值 headers: options.headers ? { 'content-type': 'application/x-www-form-urlencoded', ...options.headers } : { 'content-type': 'application/x-www-form-urlencoded' }, json: options.json || 'string', success: options.success || function () { }, error: options.error || function () { }, } // 3. 将请求携带的数据转为查询字符串 let str = ''; // 遍历对象 for (const key in defInfo.data) { // 拼接查询字符串 str += `${key}=${defInfo.data[key]}&`; } str = str.slice(0, -1); // 创建ajax对象 let xhr = new XMLHttpRequest(); // 2. 配置请求信息 // 判断请求方式,如果是get请求,则在请求地址后拼接请求携带的数据 if (defInfo.method.toLowerCase() == 'get') defInfo.url += `?${str}`; xhr.open(defInfo.method, defInfo.url, defInfo.async); // 请求头设置 // 遍历请求头对象 for (const k in defInfo.headers) { xhr.setRequestHeader(k, defInfo.headers[k]); } // 3. 发送请求 defInfo.method.toLowerCase() == 'get' ? xhr.send() : xhr.send(str); // 4. 接受响应 xhr.onload = function () { let result; if (defInfo.json === 'string') { result = xhr.responseText; } else { try { result = JSON.parse(xhr.responseText); } catch (error) { //失败的回调 defInfo.error(error); } } // 执行成功的回调函数 defInfo.success(result); } } //调用 ajax({ url:'http://localhost:8888/test/third', data:{name:'zf',age:38}, json:'json', success(res){ console.log( res ) } }) -
使用promise再次封装ajax请求
// 使用promise再次封装ajax请求--返回一个promsie对象且,是执行成功的 function pAjax(opts) { // 第二次ajax请求 return new Promise((resolve, reject) => { ajax({ url: opts.url, method: opts.method || 'get', async: typeof opts.async === 'boolean' ? opts.async : true, // true为默认值 data: opts.data || {}, // {} 为默认值 headers: opts.headers ? { 'content-type': 'application/x-www-form-urlencoded', ...opts.headers } : { 'content-type': 'application/x-www-form-urlencoded' }, json: opts.json || 'string', error: opts.error || function (err) { reject(err)// 执行失败 }, success: opts.success || function (res) { resolve(res);// 执行成功 }, }) }) } //调用 pAjax({ url: 'http://localhost:8888/test/second', json: 'json', }) .then(v=>{ console.log( v ) // 第二次请求 return pAjax({ url:'http://localhost:8888/test/third', data:{name:'zf',age:v.age}, json:'json', }) }) .then(result=>{ console.log( result ) // 解构出name和age let {info:{name,age}} = result; // 第二次请求 return pAjax({ url:'http://localhost:8888/test/fourth', method:'post', data:{name,age}, json:'json', }) }) .then(re=>console.log( re ))
回调地狱
-
当回调函数嵌套过多的时候,容易形成回调地狱
- 回调地狱代码的可读性比较差
- 回调地狱代码的可维护性比较低
-
针对回调地狱的解决方案
-
在ES6中 提出了Promise语法可以在一定程度上解决回调地狱问题
1. 请求接口`second`,并将请求响应的结果输出 2. 在第一次请求响应结束后,发起第二次请求,请求`third`接口,并输出结果 3. 在第二次请求响应结束后,发起第三次请求,请求`forth`接口,并输出结果例: pAjax({ url: 'http://localhost:8888/test/second', json: 'json', }) .then(v=>{ console.log( v ) // 第二次请求 return pAjax({ url:'http://localhost:8888/test/third', data:{name:'zf',age:v.age}, json:'json', }) }) .then(result=>{ console.log( result ) // 解构出name和age let {info:{name,age}} = result; // 第二次请求 return pAjax({ url:'http://localhost:8888/test/fourth', method:'post', data:{name,age}, json:'json', }) }) .then(re=>console.log( re ))
-
async/await
-
ES7 中提出了最终解决回调地狱的方案
-
async/await异步/等待 -
**
async**关键字- 用于修饰函数的(包括箭头函数)
- 作用: 为了在该函数内,可以使用
await关键字
-
**
await**关键字- await 修饰的语句 之后的代码都是同步执行(要等await修饰的代码执行成功,函数内后续的代码才执行)
- await 修饰的语句执行的结果(异步的返回值) 会作为 await 这个语句的结果,可以使用变量接收,后续使用
例:
console.log(1)
async function fn() {
console.log(2)
let r = await new Promise(re => {
console.log(3);
setTimeout(() => {
console.log(4);
re(5)
}, 1000);
})
console.log(r)
console.log(6)
}
fn();
console.log(7)
// 1 2 3 7 4 5 6
-
ES7 中提出了最终解决回调地狱的方案
- 使用async/await完成
例: async function fn() { // 发起第一次请求 let r1 = await pAjax({url:'http://localhost:8888/test/second',json:'json'}) console.log( r1 ) // 发起第二次请求 let r2 = await pAjax({url:'http://localhost:8888/test/third',json:'json',data:{name:'zf',age:r1.age}}) console.log( r2 ) // 解构出name和age let {info:{name,age}} = r2; // 发起第三次请求 let r3 = await pAjax({url:'http://localhost:8888/test/fourth',json:'json',data:{name,age},method:'post'}) console.log( r3 ) } fn();
浏览器存储
cookie
存储特点
1. cookie是按照域名存储的
- 本地直打开页面无法存储cookie,必须在服务器打开
+ 注意: 服务器打开,使用vscode的 Live Server插件
- 哪一个域名存储的cookie,就哪一个域名使用
- 在同一个目录下的所有文件都可以访问到
2. cookie存储大小
- 4kb左右
- 50条左右
3. cokie的存储都是字符串,有自己的固定格式(键值对存储)
- 'key=vlaue; key2=vlaue2; key3=vlaue3... '
4. cookie的存储时效性
- 默认时效性为 会话级别(页面打开到关闭)
- 我们也可以手动的设置cooie的过期时间
5. cookie 的通讯相关
- 在前后端交互的时候
- 会在每一个和 后端的 `通讯` 中 自动写在cookie 在请求头
6. cookie的操作
- 前后端都可以操作cookie
7. 前端cookie操作语法
- document.cookie
操作cookie
1. 获取cookie值
语法:document.cookie
例:console.log(document.cookie);
注意: 一次只能设置一条,设置的默认就是会话级别的时效性
2. 设置cookie值
会话级别:
语法:document.cookie = '键=值';
例:document.cookie = 'a=100';
有过期时间:
语法:document.cookie = '键=值;expires='+时间对象;
例:var time = new Date('2022-08-02 08:29:00');
document.cookie = 'age=19;expires='+time;
# cookie采用的时间规范:
不管给的什么规范的时间,都当做世界标准时间使用
因为时区的问题:中国时间=世界时间+8小时
3. 删除cookie值
利用cookie的过期时间,设置一个过去的时间
语法:document.cookie = 'b=200;expires=Thu, 18 Dec 2018 12:00:00 GMT";'
cookie操作封装
-
设置cookie值
/** * @description: 设置cookie的方法 * @param {String} key 设置cookie 的键名 * @param {String} vlaue 设置cookie 的键值 * @param {Number} expires 设置cookie的有效时间 单位s */ function setCookie(key, value, expires) { // 1. 判断expires是否传递 if (expires) { // 设置时间 var time = new Date(); time.setTime(time.getTime() - 8 * 60 * 60 * 1000 + expires * 1000) // 设置cookie document.cookie = `${key}=${value};expires=${time}`; } else { document.cookie = `${key}=${value}`; } } //调用 setCookie('name','zl'); setCookie('age','18',10);// 10s后过期 -
根据键名删除cookie
function delCookie(key) { setCookie(key, '', -1); // 通过调用设置cookie函数,设置cookie的时间过期 } //调用 delCookie('name'); -
根据键名获取cookie值
/** * @description: 根据键名获取cookie值 * @param {String} key 传入的键名 * @return {String} 对应键名的键值 或者 不存在则返回 '' */ function getCookie(key) { let cookies = document.cookie; let str = ''; // 将获取的所有cookie字符串通过 ;分隔为数组,然后遍历 cookies.split(';').forEach(item => { if (item.split('=')[0].trim() === key) { str = item; } }) // 循环结束 str就是 具有key键名的cookie数据 // console.log( str ) if (str === '') return ''; return str.split('=')[1]; // 将cookie值返回 } //调用 getCookie('name');
浏览器本地存储
ES5新增
本地存储特点
1. 存储数据的位置
就存储在浏览器本地
2. 存储大小
20M左右
3. 存储数据的方式
键值对存储
存储数据的类型只能是字符串类型,
在存储的时候,而且别的数据类型会转为字符串类型然后存储
4. 时效性 -- 不能设置
localStorage 永久存储(除非手动删除,否则永远在)
sessionStorage 会话存储(时效是会话级别)
5. 前后端操作
只有前端可以操作
后端不能操作
6. 通讯相关能力
storage在前后端交互的时候,不会自动传递
7. 跨页面访问的能力
localStorage
在相同方法打开的时候,所有页面都可以访问
sessionStorage
只有在当前页面跳转的该页面才可以访问到
cookie
在项目目录下可以访问到
8. 前端操作
storage在js中有单独的API操作方法
-
localStorage本地存储设置/新增/修改 localStorage.setItem('id', 9527) localStorage.setItem('name', '狗蛋') 获取 console.log(localStorage.getItem('name')) console.log(localStorage.getItem('age')) 删除 localStorage.removeItem('id') 清除所有 localStorage.clear() -
sessionStorage会话存储设置 sessionStorage.setItem('id', '111') sessionStorage.setItem('name', '张三') 获取 console.log(sessionStorage.getItem('id')) 删除 sessionStorage.removeItem('id') 清除全部 sessionStorage.clear()
闭包
-
函数的两个阶段 (回顾)
-
函数定义阶段
1. 在内存中开辟了一个函数的存储空间 2. 将函数的代码一模一样的放到该空间中(函数中的变量没有解析) 3.将函数的存储空间地址给到函数名(变量)- 函数调用阶段
1. 根据函数名(变量)中地址,找到函数的存储空间 2. 实参传递给形参 3. 函数中的代码解析 4. 执行函数中的代码
- 函数调用阶段
-
-
重新解释函数调用阶段
1. 根据函数名(变量)中的地址,找到函数的存储空间 2. 在内存中开辟了一个函数的`执行空间`,在`函数的执行空间`中 3. 实参给形参赋值 4. 预解析 5. 执行函数中的代码 6. 函数代码执行结束,函数的`执行空间`销毁 -
js的垃圾回收机制
在js中数据被外部的变量引用着,我们说该数据具有`可达性` 如果数据不可达,也就是没有任何外部对其进行引用 则js会自动将该数据回收(销毁) -
函数的执行空间
每一个函数会会有一个`存储空间` 但是每一次调用都会生成一个完全不一样的`执行空间` 并且`执行空间`会在函数执行完毕后就销毁,但是`存储空间`不会 `闭包`就是要利用这个`不销毁的执行空间` -
一个不销毁的函数执行空间
1. 在函数内部需要返回一个复杂数据类型 2. 在函数外部有变量对函数的返回值一直引用着 -
闭包--js中函数的一种高级操作
-
作用域的嵌套形成的一种函数高级应用场景
-
概念:
1. 有一个A函数,在A函数内部返回一个B函数 2. 在A函数外部有变量引用这个B函数 3. B函数内部访问着A函数内部的私有变量 '以上三个条件缺一不可'
-
-
闭包形成条件
1. 大函数的执行空间不销毁 2. 大函数返回的不是一个对象,而是一个函数 3. 小函数中引用着大函数中的变量 我们就说`小函数是大函数的闭包函数` -
闭包的特点
1. 作用域空间不销毁 优点:因为不销毁,变量也不会销毁,增加了变量的生命周期 缺点:因为不销毁,会一直占用内存,多了会导致内存溢出 2. 可以利用闭包访问,在一个函数外部访问函数内部的变量 优点:可以在函数外部访问内部数据 缺点:必须要时刻保持引用,导致函数执行栈不被销毁 3. 保护私有变量 优点:可以把一些变量放在函数里面,不会污染全局 缺点:要利用闭包函数才能访问,不是很方便
例:
function f1(i) {
return function (n) {
return n + i++;
}
}
let f2 = f1(1);
let r3 = f2(1) // 2
let r4 = f2(2) // 4
let r5 = f2(3) // 6
console.log(r3, r4, r5)// 2 4 6
let r1 = f1(1)(2) // 3
let r2 = f1(2)(2) // 4
console.log(r1, r2)// 3 4
继承
- 继承是和构造函数相关的一个应用
- 指 让一个构造函数去继承另一个构造函数的属性和方法
- 所以继承一定出现在两个构造函数之间
- ES6之前没有特定的 继承语法
常见的继承方式
-
先准备一个父类(让别的构造函数使用这个构造函数的属性和方法)
function Person(){ this.name = 'jack'; } Person.prototype.sayHi = function(){ console.log('hello'); }
原型继承
-
原型继承:
-
改变自构造函数的原型指向,将其指向父构造函数的实例对象,则自构造函数的实例对象就可以访问到 父构造函数的方法和属性
-
原型继承可以实现 子构造函数 继承 父构造函数 的属性和方法,但是 子构造函数 实例化对象 访问继承的属性值都是一样的,这样不合理
举个🌰: // 自定义动物构造函数 function Animal(name, age) {this.name = name;this.age = age;} // 原型添加方法 Animal.prototype.say = function () {console.log('hello')} // 自定义 猫构造函数 function Cat(eye) {this.eye = eye} // 改变Cat构造函数的原型指向 Cat.prototype = new Animal('旺财', 2); Cat.prototype.constructor = Cat; // 原型添加方法 Cat.prototype.xingwei = function () {console.log('抓老鼠');} // 实例化对象 let cat1 = new Cat('大眼睛'); let cat2 = new Cat('小眼睛'); console.log(cat1);console.log(cat2)
-
借用构造函数继承
-
借用构造函数继承
-
让父构造函数在自构造函数内通过call/apply方法调用执行,在函数调用执行的时候让父构造函数中的this指向子构造函数的实例对象
-
借用构造函数继承 可以实现 属性的继承,但是 父类原型中的方法没有继承到
举个🌰: // 自定义 动物构造函数 function Animal(name, age) {this.name = name;this.age = age;} // 原型添加方法 Animal.prototype.say = function () {console.log('hello')} // 自定义 猫构造函数 function Cat(eye,name,age) { Animal.call(this,name,age);// 通过call方法调用 父构造函数,此处的this就是子构造函数的实例对象 this.eye = eye; } // 原型添加方法 Cat.prototype.xingwei = function () {console.log('抓老鼠');} // 实例化对象 let cat1 = new Cat('大眼睛','叮当',2); console.log( cat1 ); cat1.say(); // 报错
-
组合继承
-
组合继承:
-
原型继承 + 借用构造函数继承
-
组合继承 实现了属性和方法的完美继承
-
缺点: 在原型中也有继承的属性,但是我们不需要访问,浪费了
举个🌰: // 自定义动物构造函数 function Animal(name, age) {this.name = name;this.age = age;} // 原型添加方法 Animal.prototype.say = function () {console.log('hello')} // 自定义 猫构造函数 function Cat(eye, name, age) { // 借用构造函数继承 Animal.apply(this, [name, age]) this.eye = eye } // 改变原型的指向 Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; // 原型添加方法 Cat.prototype.xingwei = function () {console.log('抓老鼠');} // 实例化对象 let cat1 = new Cat('大眼睛', '加菲', 3); let cat2 = new Cat('小眼睛', '机器', 13); console.log(cat1);console.log(cat2)
-
拷贝继承
-
拷贝继承:
-
将父构造函数的实例对象在 自构造函数中通过forin遍历 将属性都遍历到自构造函数的原型中
-
拷贝继承: 将父类中的所有属性和方法都拷贝到原型中
-
for-in循环 会将一直遍历到 顶级构造函数的原型---性能消耗较大
-
for-in遍历只能遍历 可枚举的属性
-
不可枚举的属性---对象中比较暗的属性
举个🌰: Object.prototype.ff = function () { }; // 自定义动物构造函数 function Animal(name, age) {this.name = name;this.age = age;} // 原型添加方法 Animal.prototype.say = function () {console.log('hello');} // 自定义 猫构造函数 function Cat(eye, name, age) { this.eye = eye // 实例化 动物类型 let a = new Animal(name, age); // 通过 for-in遍历 for (const attr in a) { Cat.prototype[attr] = a[attr] } } // 原型添加方法 Cat.prototype.xingwei = function () {console.log('抓老鼠');} // 实例化对象 let cat1 = new Cat('大眼睛', '加菲', 3); console.log(cat1);
-
寄生继承
-
寄生继承:
-
将父构造函数 在子构造函数内实例化对象并返回
-
可以继承父类的所有内容,但是自身的原型方法都无法使用
-
可以在子构造函数中实例化对象后改变 原型指向,但是此时父构造函数原型中的方法就访问不到
// 自定义动物构造函数 function Animal(name, age) {this.name = name;this.age = age;} // 原型添加方法 Animal.prototype.say = function () {console.log('hello');} // 自定义 猫构造函数 function Cat(eye, name, age) { // 在子构造函数 中实例化对象 let a = new Animal(name, age); a.__proto__ = Cat.prototype; a.eye = eye // 返回 实例化对象 return a; } // 原型添加方法 Cat.prototype.xingwei = function () {console.log('抓老鼠');} // 实例化对象 let c1 = new Cat('小眼睛', '小猫', 1); console.log(c1); c1.say(); // 报错
-
寄生组合继承
-
寄生组合继承:
-
将寄生继承和借用第三方独立构造函数实现继承
// 自定义动物构造函数 function Animal(name, age) {this.name = name;this.age = age;} // 原型添加方法 Animal.prototype.say = function () {console.log('hello')} // 自定义 猫构造函数 function Cat(eye, name, age) {this.eye = eye;} function fn(name, age) { // 第三方构造函数继承 function Temp() { // 借用继承 Animal.call(this, name, age); } Temp.prototype = new Animal(); Cat.prototype = new Temp(); } fn('叮当', 17); // 原型添加方法 Cat.prototype.xingwei = function () {console.log('抓老鼠');} // 实例化的对象 let c1 = new Cat('单眼皮'); console.log(c1); c1.xingwei(); c1.say();
-
ES6继承
-
ES6中有特定的语法实现继承
- 使用 **
super和extends**关键字
- 使用 **
-
语法:
class 子类 extends 父类{ constructor(){ super() } }举个🌰: // 人类--父类 class Person { constructor(name, age) { this.name = name;this.age = age; } say() {console.log(`my name is ${this.name}`);} } // 学生类 -- 继承人类 class Stu extends Person { constructor(name, age, score) { super(name, age);this.score = score; } eat() { console.log('eat ruoxifen'); this.say(); super.say()// super 可以调用到父类原型中的方法 } say() {console.log('say goodbye');} } // 实例化对象 let s1 = new Stu('zf', 17, 100); console.log(s1);s1.say();s1.eat();
严格模式(补充)
-
因为js代码在设计之初,有很多不合理的地方
-
所以在ES5的时候,推出了一个严格模式
-
目的是为了代码的规范及代码的严谨,也是为了为后续的ES版本做铺垫
-
开启方式: 在代码的最前面写上
'use strict'1. 在严格模式下,变量声明必须要使用关键字(不能隐式声明变量),否则不能使用该变量 2. 在严格模式下,不能使用0开头的8进制写法 3. 在严格模式下,函数普通调用和函数自调用中的this不指向window,而是undefined 4. 在严格模式下,函数不能使用同名形参 5. 在严格模式下,函数中的arguments.callee 不能使用 在非严格模式下,函数中的arguments.callee 就是函数本身 6. 对象属性的批量修改 语法: with(对象){属性名=值} 只能修改对象已有的属性,如果不是对象已有的属性,则会当做变量使用 但是在严格模式下,不能使用with语法 举个🌰: let o = { a: 1, b: 2 } with (o) { a = 666; b = 888; c = 999; } console.log(o) console.log(c) 再举个🌰: let dv = document.querySelector('div'); dv.style.width = '100px'; dv.style.height = '100px'; dv.style.border = '1px solid red'; with (dv.style) { width = '200px'; height = '200px'; border = '3px solid yellow' } 7. 执行js代码字符串 语法: eval(js代码字符串) 作用: 会将传入的js代码字符串当做js代码执行 在严格模式下eval只能当做函数调用 举个🌰: console.log(eval('1+1')); eval = str //报错 8. 在严格模式下 只读属性不可修改 修改会报错 str[0] = 'a'; // 报错 str.length = 10 //报错 9. 在模块化语法中,默认开启严格模式
Symbol
- Symbol 是 ES6 中新增的基本数据类型
- Symbol 数据的特点 是唯一(不重复)
创建Symbol数据
- 语法1: Symbol()
- 语法2: Symbol(描述) 创建有描述的symbol数据
- 两种方式创建的数据没有什么不一样,都是唯一的
- 返回Symbol数据
查看symbol数据类型
-
语法: typeof 数据
let id1 = Symbol('id'); console.log( typeof id1) // symbol
symbol不会隐式转为字符串
-
如果需要将symbol数据转为字符串则 通过
toString()方法console.log( 'hello'+id1 ) // 报错 console.log( 'hello '+id1.toString() )
查看symbol的描述
-
语法:
symbol数据.descriptionconsole.dir( id1.description ) // 'id'
symbol可作为对象的键名
-
对象的属性 如果是变量或symbol数据则 需要使用
[ ]包裹let attr1 = 'name'; let s1 = Symbol(); let o = { [a1]:'zs', [s1]:'666', [Symbol()]:'999', [Symbol('id')]:'1', [Symbol('id')]:'2', ['hel'+'lo']:'world' } console.log( o )
通过Symbol数据来替代魔术字符串
- 魔术字符串: 在程序中重复出现特别多次,和别的代码耦合性很高的字符串或数字
对象补充
-
对象创建方式
-
字面量方式创建
-
构造函数创建
-
通过已有的对象创建
-
语法:
Object.create(已有的对象) -
作用: 创建一个新的对象,让其原型指向指向传入的对象
-
第二个参数是 描述对象
例: let o = {a:1,b:2}; let o2 = Object.create(o,{id:{value:999}}); o2.c=666; console.log( o2 )
-
-
-
遍历对象的方法
-
使用for-in语法
-
会将对象及其原型链上所有的可枚举属性遍历获取
-
不能循环遍历Symbol
let o = {a:1,b:2}; let o2 = Object.create(o,{id:{value:999}}); o2.c = 666; o2[Symbol()] = 123; for (const attr in o2) { console.log( attr ); } console.log( o2 )
-
-
对象的属性排序----for-in遍历
-
非负整数,按数字排序输出
-
除了非负整数,则都是按照写入顺序输出
let obj = { 4: 'a',c: 3,a: 1,3: 's',1: '6','-1': 7 }; for (const key in obj) { console.log(key); }
-
对象方法
-
constructor-
语法:
对象.constructor -
作用:获取对象对应的构造函数 ---- 其实就是原型对象的构造器
例: let o = {}; console.log( o.constructor === Object) // true let arr = new Array(); console.log( arr.constructor === Array ) // true
-
-
hasOwnProperty-
语法:
对象.hasOwnProperty() -
返回值:布尔值
true:是该对象的属性false:不是该对象的属性
-
原型中的属性不能判断
例: let id = Symbol(); let o = {a:1,b:2,[id]:666} console.log( o.hasOwnProperty('a') ); console.log( o.hasOwnProperty(id) ); console.log( o.hasOwnProperty('constructor') );
-
-
isPrototypeOf- 语法:
对象.isPrototypeOf(传入的对象) - 作用: 判断对象是否是 传入对象原型链中的一个
- 语法:
-
propertyIsEnumerable- 语法:
对象.propertyIsEnumerable(属性名) - 判断该对象的属性是否是可枚举的
- 语法:
-
in语法- 语法:
属性 in 对象 - 注意: in左边的如果不是变量则要用引号包裹
- 返回值:布尔值
true:表示该属性是 对象的属性false:表示该属性不是 对象的属性
let id= Symbol(); let obj = {a:1,b:2,[id]:666,und:undefined}; console.log( 'a' in obj ); console.log( id in obj ); console.log( 'und' in obj ); console.log( 'c' in obj ); console.log( 'constructor' in obj ) // true - 语法:
对象的静态方法
Object是构造函数 也是对象
作为函数的的时候 具有的属性和方法
length 表示函数的形参个数
name 表示函数的名称
-
assign-
语法:
Object.assign(对象1,对象2,对象3...); -
作用: 将对象2之后的所有对象中的数据拷贝到对象1中
-
浅拷贝
例: let obj = Object.assign({},{a:1},{b:2},{c:3},1,2)
-
-
defineProperty-
语法:
Object.defineProperty(对象,属性,描述对象)描述对象: 配置对象中属性的 标志符的 { configurable: 布尔值 表示属性是否可配置(是否可以修改属性的 任何标志符) enumerable: 布尔值 表示属性是否可枚举 writable: 布尔值 表示属性的是否可修改 value: 值 就是属性值 set()方法 设置该属性的时候触发的方法 get()方法 访问该属性的时候触发的方法 } 注意: set和get方法不能同时和writeable,value一起出现 字面量对象中添加的属性和点语法,数组关联法添加的属性,标志符默认都为true
-
-
defineProperties -
语法:
Object.defineProperties(对象,{属性:描述对象, ... }) -
freeze- 语法:
Object.freeze(对象) - 冻结对象
- 对象中的属性不可修改,不可扩展
- 语法:
-
isFrozen- 语法:
Object.isFrozen(对象) - 判断是否被冻结
- 返回值:布尔值
- 语法:
-
getOwnPropertyDescriptor- 语法:
Object.getOwnPropertyDescriptor(对象,属性) - 获取该属性的 描述对象
- 语法:
-
getOwnPropertyDescriptors- 语法:
Object.getOwnPropertyDescriptors(对象) - 获取对象所有的属性 描述对象
- 语法:
-
getOwnPropertyNames- 语法:
Object.getOwnPropertyNames(对象) - 获取对象的所有字符串属性名
- 语法:
-
getOwnPropertySymbols- 语法:
Object.getOwnPropertySymbols(对象) - 获取对象所有的symbol属性
- 语法:
-
preventExtensions- 语法:
Object.preventExtensions(对象) - 对象不可扩展,但是属性可以修改删除
- 语法:
-
isExtensible- 语法:
Object.isExtensible(对象) - 判断对象是否可扩展
- 语法:
-
seal- 语法:
Object.seal(对象) - 对象属性,不可删除,不可扩展,但是可以修改
- 语法:
-
isSealed- 语法:
Object.isSealed(对象) - 判断对象的属性是否锁死(seal)
- 语法:
-
is- 语法:
Object.is(参数1,参数2) - 判断两个值是否相等(===) ,而且 NaN和NaN是相等的
- 语法:
-
entries- 语法:
Object.entries(对象) - 返回一个二维数组,小数组中的第一个元素是键名,第二个是键值
- 注意: 不包含symbol属性
- 语法:
-
keys- 语法:
Object.keys(对象) - 得到一个所有属性组成的数组(不包含symbol属性)
- 语法:
-
fromEntries- 语法:
Object.fromEntries(可迭代数据) - 传入一二维数组,小数组第一个元素对象的属性名,第二个元素当对象的数值
- 返回值创建的对象
- 语法:
-
values- 语法:
Object.values(对象) - 获取对象的属性值组成数组(不包含symbol属性)
- 语法:
-
getPrototypeOf- 语法:
Object.getPrototypeOf(对象) - 获取对象的原型
- 语法:
-
setPrototypeOf- 语法:
Object.setPrototypeOf(对象,原型对象) - 设置对象的原型
- 语法:
对象的set和get
-
对象的属性
1. 通过等号值赋的可以存储数据 2. 直接通过属性名 获取到了存储数据 -
对象的方法
1. 调用的时候可以传递多个参数 2. 方法执行的时候可以返回值 3. 执行的时候可以运行多个语句 -
是否可以在对象属性访问或赋值的时候,也可以运行多条语句?
- 使用 对象的访问器属性
- 对象的访问器属性 其实就是对象的
setter和getter属性
-
set
- 语法:
set 属性(){} - 在
设置修改对象的这个属性的时候会执行的方法 - 此方法有且仅有一个形参,形参接受 设置对象属性赋值的数据
- 语法:
-
get-
语法:
get 属性(){} -
在
访问对象的这个属性的时候会执行的方法 -
此方法不能有参数, 此方法的返回值就是 对象访问属性的属性值
-
例:
let obj = {
a: 1,
b: function (n, m) {
let res = n + m;
return res * 10
},
_c: 0,
set c(value) { // 对象设置c属性的时候执行方法
console.log(value)
this._c = value;
},
get c() { // 对象访问c属性的时候执行的方法
return this._c;
}
}
obj.c = 999;
console.log(obj);
console.log(obj.c);
proxy代理
-
一个proxy代理包装一个源对象,可以拦截一些 对象的操作,比如设置获取属性,对象的原型操作等等...
-
代理包装的原对象,可以是对象,也可以是数组,函数....
-
语法:
let p = new Proxy(原对象,代理配置对象)- 参数1: 就是我们代理包装的对象,我们真实要操作的对象
- 参数2: 代理配置对象的都是方法---拦截器方法
set当需要设置对象属性的时候,我们通过p来设置,则会被拦截器拦截
- 返回值: 包装对象p 和原对象基本一样,如果需要通过拦截器,则需要通过p对象来进行操作
- 注意: 我们只有通过 包装对象p 进行操作才会 进入到拦截器
对象的set 和 get拦截器
-
当对象访问属性和设置属性的时候都必须通过
set,get方法执行let obj = { a: 1, b: 2 }; // 只能设置数字 let p = new Proxy(obj, { set(o, k, v, pr) { // o 就是源对象 // k 就是操作的属性名 // v 就是操作的赋值的数据 // pr 代理对象 console.log( o,k,v,pr ) if (isNaN(v)) return; o[k] = v; }, get(o, k, pr) { // o 就是源对象 // k 就是操作的属性名 // pr 代理对象 console.log( o,k,pr ) return o[k]; } }) p.a = 100; console.log(p) console.log(p.a); console.log(obj);
对象属性的删除拦截器
-
deleteProperty() -
当删除对象属性的时候,会触发的拦截器
例: // 除了对象的c属性,其他都可以删除 let obj = { a: 1, b: 2, c: 2 }; let p = new Proxy(obj, { deleteProperty(o, k) { // 判断然后删除 if (k != 'c') delete o[k]; } }) // 删除属性 delete p.attr; console.log(obj);
对象的原型访问设置拦截器
-
setPrototypeof()getPrototypeof()let o1 = {a:1,b:2}; let o2 = Object.create(o1); o2.c = 666; // console.log( o2 ) let p = new Proxy(o2,{ setPrototypeOf(o,v){ // v就是想要设置的原型对象 // console.log( o,v ) if(Array.isArray(v)) return true; return Object.setPrototypeOf(o,v); }, getPrototypeOf(o){ // console.log( 666 ) return Object.getPrototypeOf(o); } }) // 获取元素则触发 拦截器 console.log( Object.getPrototypeOf(p) ); // 设置的原型不可以是数组类型 Object.setPrototypeOf(p,{q:'qq'}) Object.setPrototypeOf(p,[1,2,3]) Object.setPrototypeOf(p,function fn() {}) console.log( o2 )
数组的静态方法
-
isArray- 语法:
Array.isArray(参数) - 作用: 判断传入的参数是否是数组类型
- 返回值: 布尔值
- 语法:
-
of-
语法:
Array.of(参数) -
作用:根据传入的参数创建数组
let arr1 = new Array(5); console.log(arr1) // [empty × 5] let arr2 = Array.of(5) console.log(arr2) // [5]
-
代理数组
例:
let arr = [];
// 只能写数字,arr数组只存储数值类型数据
let p = new Proxy(arr, {
set(a, i, v) {
// a 代理的数组,i 设置数据的索引 ,v设置的数据
if (typeof v === 'number') {
a[i] = v;
}
}
})
p[0] = 'qwewe';
p[0] = 12345;
p[1] = 666;
p[2] = 'asd';
console.log(arr); // [12345,666]
constructor拦截器
-
函数或类在实例化对象的时候会被拦截
class Stu { constructor(name) { this.name = name; } } let p = new Proxy(Stu, { construct(s, argArr) { // s 就是原类, argArr是实例化对象的参数 // console.log( s ) // console.log( argArr ) if (typeof argArr[0] === 'string') { return new s(...argArr); } return {}; } }) let s1 = new p('zs', 18, 'ctrl'); // let s1 = new p(18, 'ctrl'); console.log(s1)
迭代器
for-of
-
for-of可以遍历任何可迭代数据 -
可迭代的数据其本身或原型上一定实现了 **
Symbol.iterator**方法 -
所以如果某一个数据可以使用**
Symbol.iterator**方法,则该数据是可迭代的内置的一些可迭代的数据: 1. 数组 2. 获取的页面元素集合 3. arguments 4. 字符串 5. set 6. map -
语法:
for(变量 of 数据集合){ 变量是数据集合中每一个 元素 }例: let arr = [1, 2, 3, 4] for (const v of arr) { console.log(v) } let dvs = document.getElementsByTagName('div') for (const ele of dvs) { console.log(ele) }
迭代器
可以使用for-of语法遍历的数据,都是实现了Symbol.iterator方法的
此方法调用,则会返回一个迭代器
除了for-of会自动调用此方法
我们也可以手动的调用Symbol.iterator方法,返回的迭代器具有next()方法
for-of调用next方法,得到一个对象 { vlaue:值,done:布尔值 }
value键名对应的值,就是遍历迭代的数据
done表示迭代是否结束,true表示结束了
例:
let arr = ['a', 'b', 'c', 'd'];
let iter = arr[Symbol.iterator]()
console.log(iter)
console.log( iter.next() ) // {value: "a", done: false}
console.log( iter.next() ) // {value: "b", done: false}
console.log( iter.next() ) // {value: "c", done: false}
console.log( iter.next() ) // {value: "d", done: false}
console.log( iter.next() ) // {value: undefined, done: true}
例:模拟迭代器
// 默认对象没有实现Symbol.iterator方法,所以不能使用forof遍历数据
// 我们手动的给一个对象添加一个Symbol.iterator方法,模拟迭代器
let obj = {
name: 'zs',
age: 17,
nickname: '法外狂徒'
}
obj[Symbol.iterator] = function () {
return { // 模拟迭代器
keys: Object.keys(obj),
n: 0,
next() {
let v = obj[this.keys[this.n]]
if (this.n < this.keys.length) {
this.n++;
return { value: v, done: false }
} else {
return { value: v, done: true }
}
}
}
}
for (const k of obj) {
console.log(k)
}
// 将Symbol.iterator方法写到Object.prototype中
Object.prototype[Symbol.iterator] = function () {
let that = this; // 将迭代的数据对象 保存起来
return { // 模拟迭代器
keys: Object.keys(that),
n: 0,
next() {
// let v = that[this.keys[this.n]]
let v = [this.keys[this.n], that[this.keys[this.n]]]
if (this.n < this.keys.length) {
this.n++;
return { value: v, done: false }
} else {
return { value: v, done: true }
}
}
}
}
let o = {
name: 'zf',
age: 18,
nickname: '千锋小旋风'
}
for (const [k, value] of o) {
console.log(k, value)
}
Set 数据类型
在ES6中新增的`set数据类型`,是一个`复杂数据类型`
set也是数据集合,和数组很像,但是set是`松散型数据结构`,set中的数据是`无序`的
最主要的作用就是创建一个数据结构,设置一个数据结构,结构类似于数组(不是数组)
set和数组虽然都是`直接存储数据`,但是set最大的特点是`存储的数据不能重复`
主要应用在数组去重
var s=new Set(arr)
使用new Set()去重,数组中不支持复杂数据类型;复杂数据类型要么循环要么损招(仅用于面试题):直接给他们拼接上引号
-
new Set操作方法
-
var s=new Set(参数) -
参数必须是可迭代的数据
-
返回值: 不重复的数据组成的set类型数据集合
-
add()s.add(内容)向set集合添加数据 在set集合中NaN和NaN是相同的
-
-
delete()s.delete(删除的数据)删除set集合中的数据 返回值:布尔值 -
has()s.has(数据)判断数据是否存在 返回:布尔值 存在则返回true 不存在返回false -
clear()s.clear()清空set中的数据集合 -
sizes.size获取到set集合中数据的个数 -
forEach循环遍历 new set只能使用forEach进行遍历 s.forEach(function(x,y,z){} 参数1和参数2:值是一样的,是set中的数据 参数3:原set集合 -
entries()keys()values()返回set结合的迭代器 -
set数据集合可以使用展开运算符转为数组console.log( [...s] ) -
使用set数据集合给数组去重let arr = [8, 1, 3, 2, 5, 3, 4, 5, 6, 6, 7, 8, 7]; console.log([...new Set(arr)])
map 数据类型
map数据集合,是ES6新增的`复杂数据类型`
map也是`松散型`数据结构,
map和对象数据结构差不多,都是`键值对`存储
map集合中的,键名可以是任意数据类型,而且必须`唯一`
-
创建map数据集合
-
new Map(参数)参数一般都是 一个二维数组 小数组中的第一个元素作为map中的键名,第二个元素作为map中的对应的值 let m = new Map([[1,'a'],['a','b'],[true,'buer'],[{},'dx']]);
-
常用方法和属性
-
sizem.size获取集合中数据的个数 -
set
m.set(键,值)
设置集合数据
get
m.get(键)
根据键名获取值
delete
m.delete(键)
根据键名删除
clear
m.clear()
清空
has
m.has(键名)
判断键名是否存在
forEach
遍历map结合数据
m.forEach((v,k,m1)=>{}
参数:
v:对应键值
k:对应键名
m1:map数据本身
keys()values()entries()
获取 map集合对应的迭代器
for-of
可以使用for-of遍历
for (const [k,v] of m) {}
参数:
k:对应键值
v:对应键名
弱映射
```js
弱映射: WeakMap和WeakSet 中的数据都是弱引用
之前的数组,对象,set,map等集合中的数据都是强引用
let o = {
a: 1
}
let arr = [];
arr[0] = o;
// // 将o赋值为null
o = null;
console.log(arr[0])
let obj = {
a: 1
}
let ws = new WeakSet()
console.log(ws)
ws.add(obj);
// // 将obj赋值为null
obj = null;
setTimeout(() => {
console.log(ws)
}, 3000)
```
生成器函数
常规的函数执行 都是单一的返回值,或者没有
但是 Generator 函数(生成器函数) 可以逐段执行代码 返回多个值
-
语法:
function* 函数名(){ yield 代码1; 代码2 代码3 } 在generator函数中可以使用yield关键字 语法1: yield 表达式; 语法2: yield* 可迭代的数据; yield 可以阻断代码的执行(从上一个yield执行到当前的这个) yield 也可以将表达式的结果返回到函数外,作为对应next()方法执行的结果 yield 其实是一个双向通道,除了可以将函数内表达式的结果返回到函数外给next()作为返回值之外,也可以 将next(实参)的实参传递给 yield 前面定义的变量 generator函数的执行,生成器函数调用不会执行函数内的代码,而是返回一个迭代器,迭代器就有next方法,当返回的迭代器调用next方法的时候才会开始执行函数中的代码 var gen = 函数名() gen.next() // 返回值: {value:值,done:布尔值} 返回值对象中的 value值就是对应的yield表示的结果,done表示函数执行是否结束 生成器函数返回值gen 还有一个return方法可以结束生成器函数的执行举个🌰: function* fn() { console.log('fn开始') yield 1; yield 2; yield 3; yield* ['a', 'b', 'c']; console.log('fn结束') } let gen = fn(); console.log(gen.next()); // {value: 1, done: false} console.log(gen.next()); // {value: 2, done: false} console.log(gen.next()); // {value: 3, done: false} console.log(gen.next()); // {value: 'a', done: false} console.log(gen.next()); // {value: 'b', done: false} console.log(gen.next()); // {value: 'c', done: false} console.log(gen.next()); // {value: undefined, done: true}也就是说 可以执行使用forof遍历gen function* fn() { console.log('fn开始') yield 1; yield 2; yield 3; yield* ['a', 'b', 'c']; console.log('fn结束') } let gen = fn(); for (const v of gen) { console.log(v) }yield* 的使用 function* fn() { yield 3; yield 4; } function* ff() { yield 1; yield 2; yield* fn(); yield 5; } let gen = ff(); console.log(gen.next()) // {value: 1, done: false} console.log(gen.next()) // {value: 2, done: false} console.log(gen.next()) // {value: 3, done: false} console.log(gen.next()) // {value: 4, done: false} console.log(gen.next()) // {value: 5, done: false} console.log(gen.next()) // {value: undefined, done: true}使用generator函数实现 普通对象的 for-of let obj = { name: 'zf', age: 18, like: 'ctrl', className: 'gp9', *[Symbol.iterator]() { // 等价于 [Symbol.iterator]: function* (){} // let keys = Object.keys(this); // 获取到对象的属性组成的数组 // for (let i = 0; i < keys.length; i++) { // yield this[keys[i]] // } // // let values = Object.values(this); // 获取到对象的属性值组成的数组 yield* Object.values(this); } } for (const v of obj) { console.log(v) }举个🌰: function* fn() { console.log('start') let r1 = yield 1; console.log(r1) let r2 = yield 2; console.log(r2) let r3 = yield 3; console.log(r3) console.log('end') } let gen = fn(); console.log(gen.next()) console.log(gen.next(111)) // console.log(gen.return())// 如果执行return,则表示函数执行结束 console.log(gen.next(222)) console.log(gen.next(333)) 'next方法第二次调用的 传递的实参传递第一个yield前的变量,yield前面的变量结构next调用传递的参数' 第一次调用: 输出‘start’,输出‘{value: 1, done: false}’ 第二次调用: 将实参‘111’传递给第一个yield前的变量, 输出‘111’,输出‘{value: 2, done: false}’ 第三次调用: 将实参‘222’传递给第一个yield前的变量, 输出‘222’,输出‘{value: 3, done: false}’ 第四次调用: 将实参‘333’传递给第一个yield前的变量, 输出‘333’,输出‘end’,输出‘{value: undefined, done: true}’ -
使用生成器函数解决回调地狱
function* fn() { let r1 = yield ajax({ // 第一次请求 url: 'http://localhost:8888/test/second', json: 'json', success(res) { gen.next(res)// 第一次请求结束,让函数继续执行 } }) console.log(r1) let r2 = yield ajax({ // 第二次请求 url: 'http://localhost:8888/test/third', data: { name: 'zf', age: r1.age }, json: 'json', success(res) { gen.next(res) } }) console.log(r2) let { info: { name, age } } = r2 let r3 = yield ajax({ // 第三次请求 url: 'http://localhost:8888/test/fourth', data: { name, age }, json: 'json', method: 'post', success(res) { gen.next(res) } }) console.log(r3) } var gen = fn(); gen.next(); // 第一次请求
设计模式
- 设计模式:前人总结的,针对特定问题提交的简洁而优化的方案(23种)
- 单例设计模式
- 组合设计模式
- 代理设计模式
- 工厂设计模式
- 适配器设计模式
- 策略设计模式
- 发布订阅设计模式
- 观察者设计模式
单例设计模式
类或者构造函数 一生执行实例化一次
如果类实例化的多个对象中的方法和属性,实现效果功能都一样,那么在使用中,及不需要是实例化多个对象
组合设计模式
多个实例对象需要启动,我们统一来启动
代理设计模式
使用代理对象来完成对象的操作
工厂设计模式
工厂可以创造产品, 让工厂来创建对象,可以让一个工厂创建多种类型的对象
适配器设计模式
一些旧类中的API,已经不能完成适应现在的应用环境,需要使用一个适配器来让API可以继承正常使用
举个栗子:
苹果手机的充电线,只能给苹果手机充电,如果你换手机了,只有原来的苹果充电线,此时你需要一个可以将苹果充电线头转为安卓手机可用的充电头(转接头适配器)
策略设计模式
同一个问题,有多个处理方案,也有可能随时增加处理方案
举个栗子:
比如商场的折扣
已有的 折扣,满100减10块
已有的 折扣,满200减30块
已有的 折扣,满300减50块
发布订阅设计模式
对一个事物一直监视着, 当事物发生变化的时候,则找到对应函数执行的操作
`div.addEventListener('click',function(){})`
点击事件(浏览器)一直监视着用户 在页面中的行为, 如果用户发生点击行为则触发函数执行
发布订阅模式,就是模拟给页面元素绑定事件,而是给数据对象绑定自定义的事件
给data绑定一个abc事件
需要关注,事件如何绑定,事件如何触发,事件如何取消
如何绑定事件
可以通过bind方法给data添加绑定一个事件
传递参数中需要有事件类型,对应的处理函数
如何存储? 可以将data的绑定的事件和多个处理函数存储在对象中
{'abc':[处理函数1,处理函数2,处理函数3...]}
可以通过exec方法让data触发对应的事件
传递一个参数事件类型---表示该类型事件触发了
执行对应的处理函数
循环处理函数的数组,执行里面的函数
通过unbind方法来移除事件处理函数
传入事件类型及处理函数
去到对应的数组中删除处理函数
清空事件处理函数 clear
如果绑定的事件处理函数是匿名函数,则无法移除
所有添加一个清空所有事件处理函数
观察者模式
观察者设计模式 与 发布订阅模式 有一些区别
观察者设计模式 是有观察者 与 被观察者
fetch
-
fetch 是原生的 新一代浏览器发送请求的工具,优点配置简单
-
缺点: 所有IE都不兼容,所有pc端项目慎用
-
语法:
fetch(url,options)url:请求的地址,必填options:请求的配置信息,选填
-
请求的响应
fetch请求响应的数据是一个promise对象 返回的对象有两个方法 text() || json() let response = await fetch(url) 获取响应的两个方法 response.text() 响应回来的数据不需要JSON.parse()转换 response.json() 响应回来的数据需要JSON.parse()转换 两个方法的返回值还是promise对象举个🌰: //请求测试接口/test/first async function clickFn1() { let response = await fetch('http://localhost:8888/test/first') let res = await response.text(); console.log(res) } //请求测试接口/test/second async function clickFn2() { let response = await fetch('http://localhost:8888/test/second') let res = await response.json(); console.log(res) } //请求测试接口/test/third async function clickFn3() { // 请求需要携带参数 get请求携带的参数 拼接在地址?后 let response = await fetch('http://localhost:8888/test/third?name=zs&age=18') let res = await response.json(); console.log(res) } //请求测试接口/test/fourth async function clickFn4() { // 请求方式为post // 需要设置 第二个参数配置对象 /* { headers:{} // 设置请求头 method:'get|post' // 设置请求方式 默认是get请求 body: , // 请求携带的数据,按照要求格式传递 } */ let options = { // 请求的配置 method: 'post', headers: { 'content-type': 'application/x-www-form-urlencoded', }, body:'name=zs&age=18' } let response = await fetch('http://localhost:8888/test/fourth', options); let res = await response.json(); console.log( res ) }
axios
-
axios 是第三方封装好的发起网络请求的工具, 基于xhr封装
-
使用
- 下载 使用:引入在线的文件,npm安装
- 直接使用,创建实例使用
-
直接使用
- 语法:
axios(url,options) - 语法:
axios(options)axios()调用请求返回一个pormise对象- 响应数据 在
response.data
- 语法:
-
创建实例使用
- 语法:
axios.create(options)- options 配置对象
- 一般都是本页面中 公共的请求信息
- options 配置对象
举个🌰: //请求测试接口/test/first async function clickFn1() { let response = await axios('http://localhost:8888/test/first') console.log( response.data ) } //请求测试接口/test/second async function clickFn2() { let options = { url:'http://localhost:8888/test/second', // 请求地址 method:'get', // 默认 dataType:'josn', } let response = await axios(options) console.log( response.data ) } //请求测试接口/test/third async function clickFn3() { // 请求需要携带参数 get请求携带的参数 拼接在地址?后 let options = { url:'http://localhost:8888/test/third?name=zs&age=18', // 请求地址 method:'get', // 默认 dataType:'josn', } let response = await axios(options); console.log( response.data ); } //请求测试接口/test/fourth async function clickFn4() { // 请求方式为post let options = { // axios自动设置了请求头 urlencoded url:'http://localhost:8888/test/fourth', // 请求地址 method:'post', // 默认 dataType:'josn', // data:'name=zs&age=18', data:{ name:'zs', age:17 }, transformRequest:[function (data) { // 会在发送请求之间,对请求携带的数据格式化 let str = ''; for (const key in data) { str += `${key}=${data[key]}&`; } return str.slice(0,-1); }] } let response = await axios( options); console.log( response.data ); } - 语法:
-
创建axios实例发送请求
let myAxios = axios.create({ baseURL: 'http://localhost:8888', // 请求的基准地址 }) // myAxios.defaults.baseURL = 'https://api.example.com'; // console.log( myAxios ) //请求测试接口/test/first async function clickFn1() { let response = await myAxios({ url: '/test/first', // baseURL:'https://api' }) console.log(response.data) } //请求测试接口/test/second async function clickFn2() { let options = { url: 'test/second', // 请求地址 method: 'get', // 默认 dataType: 'josn', } let response = await myAxios(options) console.log(response.data) } //请求测试接口/test/third async function clickFn3() { // 请求需要携带参数 get请求携带的参数 拼接在地址?后 let options = { url: 'test/third?name=zs&age=18', // 请求地址 method: 'get', // 默认 dataType: 'josn', } let response = await myAxios(options); console.log(response.data); } //请求测试接口/test/fourth async function clickFn4() { // 请求方式为post let options = { // myAxios自动设置了请求头 urlencoded url: 'test/fourth', // 请求地址 method: 'post', // 默认 dataType: 'josn', // data:'name=zs&age=18', data: { name: 'zs', age: 17 }, transformRequest: [function (data) { // 会在发送请求之间,对请求携带的数据格式化 let str = ''; for (const key in data) { str += `${key}=${data[key]}&`; } return str.slice(0, -1); }] } let response = await myAxios(options); console.log(response.data); }
node.js
node介绍
-
为什么浏览器可以解析javascript的语法(ES)?
因为浏览器具有js的解析引擎,所以可以解析js的语法, DOM和BOM都是浏览器自身的内容有一群好事者,将浏览器中可以解析js语法的引擎(chrome的v8引擎)单独提取出来,然后做了一个软件,就可以单独安装在电脑上,这个软件就是node---我们说的nodejs其实就是脱离了浏览器,使用node软件来解析的js代码,node运行的js因为不用兼顾浏览器的问题,可以操作文件,可以创建操作服务器,可以操作数据库---->就是后端语法 -
node定义
nodejs的官方的定义: 是一个基于chrome V8引擎的js运行时(环境) nodejs的私人解释: 就是可以解析js语法的软件 -
node中的js和前端js的区别和侧重点
- 前端js
- 注重页面特效
- 注意兼容问题
- 后端js
- 注重后端逻辑
- (因为没有浏览器)没有浏览器兼容问题
- 前端js
-
node的安装
- 一直
下一步,直到安装完成
- 一直
-
检测是否安装成功:
-
打开命令行窗口,输入指令
node -v出现版本号则安装成功-
在node命令行中
在命令行中,输入node按回车 此处书写的代码不能保存(不推荐使用) 退出:连按两次ctrl+c -
通过node指令执行js文件
在命令行中切换到要执行js文件的目录中 指令: node 要执行的js文件
-
-
-
打开命令行(黑窗口)
win+r=> 输入cmd=>回车- 在文件夹目录的地址栏中输入
cmd=>回车 -
- 在vscode中
ctrl + ~<=>ctrl + (shift + ~)
- 在vscode中
DOS指令
-
DOS指令:操作系统提供给用户可以操作系统内容的指令
- 比如: 文件,文件夹,文件内容......
-
指令:
1. 盘符: 切换盘符 2. cd 文件目录地址 切换目录 3. cd .. 切换到上一级目录 4. cls 清屏 (clear) 5. ipconfig 查看网络配置 6. systeminfo 查看电脑配置 7. md 文件夹名 创建文件夹 8. rd 文件夹 删除目录 rd /s /q 目录名 /s表会递归删除 /q是否提示 9. xcpoy 文件夹 新文件夹 复制文件夹 10. type nul>文件名 创建新文件 11. echo 内容>文件名 写入内容到文件中(如果文件不存在则创建)---覆盖写入 12. echo 内容>>文件名 追加内容 13. ren 文件名 新文件名 文件重命名 14. del 文件名 删除文件 15. move 文件 新位置的文件 文件移动 16. dir 查看目录中的所有文件 17. tree 查看所有目录并且树状展示 18. copy 文件名 新文件名 复制文件并另起新名 19. move 文件路径 新的路径 将文件移动到新的目录中
node中的模块化
-
node中的模块化:一个文件就是一个模块,一个文件就是一个模块作用域
-
nodejs模块化是遵循了 COMMONJS 的规范实现的
-
模块化的优点
- 方便代码的复用
- 提升了分工合作
- 便于代码维护
-
导入语法:
-
语法1:
require(相对路径文件名或绝对路径)根据相对路径找文件,如果文件不存在则报错 -
语法2:
require(模块名)模块名很多时候就是文件名 此时node会自动去当前目录下的 node_modules文件夹中查找,找到了就导入 找不到则去上一级目录中找node_modules目录中找, 如果一直到盘符根目录都没有,则去node安装的全局目录中查找,如果还找不到则报错 注意: 找不到文件的时候,如果有同名的目录也会去看看, 看同名目录中的package.json文件的中的main字符段(入口文件),导入该文件 注意: 1. 文件名的后缀可以省略 2. 导入的文件都会执行 3. 导入语法的返回值,是导入文件中暴露的内容 4. 可以理解为导入是单例的 5. 因为是同步导入,所以可以在需要的时候导入使用
-
-
导出语法:
- 导出目的: 是为了向外部暴露该模块中的数据
- 每一个模块中都有一个module对象
- 每一个模块会自动 导出 module.exports 中的数据
- 语法1:
module.exports.属性名 = 值或者module.exports = {数据} - 语法2:
exports.属性 = 值相当于module.exports.属性名 = 值- exports = {数据} // 无效
在node中模块分为三类
内置模块(node安装的时候,自动创建的一些模块)
自定义模块(自己的写一些模块)
第三方模块(别人写好的上传到远程地址的模块)
内置模块-fs
-
fs:file system 操作文件的模块
-
使用:
- 导入然后使用(因为是内置模块,只需要写模块名就行)
- 主要使用 该模块导出的 方法
-
导入
const fs = require('fs') -
readFile()异步读取文件内容-
语法:
fs.readFile(文件名<,读取文件的显示编码(可选)>,回调函数)🌰: fs.readFile('./fs-File/read.js', (err, data) => { // 如果读取失败,则err为失败信息,data为空 // 如果读取成功,则err为空,data读取的内容 // 报错优先 if (err) return console.log(err); console.log( 'ok' ) // 默认读取的数据: 是一个十六进制显示的Buffer类型的数据 console.log( data ) }) 🌰: fs.readFile('./fs-File/read.js','utf-8', (err, data) => { if (err) return console.log(err); console.log( 'ok' ) console.log( data ) })
-
-
readFileSync()同步读取文件内容-
语法:
fs.readFileSync(文件名<,读取文件的显示编码(可选)>) -
返回值: 读取成功的数据,读取失败就报错
🌰: let res = fs.readFileSync('./fs-File/read.js','utf-8') let res = fs.readFileSync('./fs-F/read.js','utf-8') // 目录路径有误 console.log( res )
-
-
writeFile()异步写文件内容-
语法:
fs.writeFile(文件名,写入的内容,回调函数) -
覆盖式的写入, 如果文件名不存在,则会自动创建该文件并写入
-
如果目录路径错误,则报错
🌰: fs.writeFile('./fs-File/write1.js', 'console.log("hello world")', (err) => { // 如果写入失败,则err为失败信息 // 如果写入成功,则err为空 if (err) return console.log(err); console.log('ok') })
-
-
writeFileSync()同步写文件内容-
语法:
fs.writeFileSync(文件名,写入的内容) -
覆盖式的写入, 如果文件名不存在,则会自动创建该文件并写入
-
如果目录路径错误,则报错
🌰: fs.writeFileSync('./fs-File/write1.js', 'console.log("hello node")')
-
-
appendFile()异步追加文件内容-
语法:
fs.appendFile(文件名,写入的内容,回调函数)🌰: fs.appendFile('./fs-File/writ.js', '\nconsole.log("hello girls")', err => { //'\n'为换行 if (err) return console.log(err); console.log('ok') })
-
-
existsSync()判断文件是否存在-
语法:
fs.existsSync(文件名) -
返回值:布尔值
let res = fs.existsSync('./fs-File/write2.js'); console.log( res )
-
-
stat()获取文件(夹)信息-
语法:
fs.stat(文件名,回调函数)stats.isFile()判断读取的是否是文件stats.isDirectory()判断读取的是否是目录stats.size判断读取的是否是目录
🌰: fs.stat('./fs-File/write2.js',(err,stats)=>{ if(err) return console.log( err ) console.log( stats.isFile() ); // 判断读取的是否是文件 console.log( stats.isDirectory() ); // 判断读取的是否是目录 console.log( stats.size ); // 判断读取的是否是目录 })
-
-
mkdir()异步创建文件夹-
语法:
fs.mkdir(创建的文件夹,回调函数)fs.mkdir('./fs-File/t',err=>{ if(err) return console.log(err); console.log('ok'); })
-
-
node中的两个预定义变量
__dirname表示当前目录的绝对地址__filename表示当前目录的地址包含文件名
内置模块-os
-
内置模块-os:获取系统信息
-
导入
const os = require('os'); -
获取计算机内核
-
os.cpus()console.log(os.cpus()); // 获取计算机内核 console.log(os.cpus().length); // 8
-
-
获取计算机的剩余运行内存
-
os.freemem()console.log(os.freemem()); console.log(os.freemem()/1024/1024/1024);
-
-
获取计算机运行内存内存
-
os.totalmem()console.log(os.totalmem()); console.log(os.totalmem()/1024/1024/1024); // 15.897911071777344
-
-
获取操作系统的版本
-
os.type()console.log(os.type()); // Windows_NT
-
-
系统换行符
-
os.EOLconsole.log('6'+os.EOL+'7');
-
内置模块-path
-
内置模块-path:处理地址的模块
-
导入
let path = require('path') -
解析地址信息
-
parse()let pathStr = '/fs-File/read.js'; console.log(path.parse(pathStr)); /* { root: '', // 根目录 dir: './fs-File', // 文件的当前目录 base: 'read.js', // 完整文件名 ext: '.js', // 后缀名 name: 'read' // 文件名 } */
-
-
获取文件名
-
basename()console.log(path.basename(pathStr)); // read.js
-
-
获取文件后缀
-
extname()console.log(path.extname(pathStr)); // .js
-
-
获取目录地址
-
dirname()console.log(path.dirname(pathStr));
-
-
相对地址拼接
-
join()console.log(path.join('/xxx','./yyy','./index.html')); // \xxx\yyy\index.html console.log(path.join('/xxx','./yyy','../index.html')); // \xxx\index.html
-
-
相对地址转为绝对地址
-
resolve()console.log(path.resolve('./index.hmtl')); // D:\GP9\06-week\26-day\02-代码\index.hmt console.log(path.resolve('./xxx','./yyy','../index.hmtl')); // D:\GP9\06-week\26-day\02-代码\xxx\index.hmtl
-
内置模块-url
-
内置模块-url:获取url地址信息
-
导入
let url = require('url'); -
解析url地址
-
parse()let urlStr = 'https://nodejs.org:443/dist/latest-v16.x/docs/api/url.html?a=1&b=2&c=3#12345'; console.log(url.parse(urlStr)); console.log(url.parse(urlStr,true)); // 会将查询字符串转为一个对象 /* protocol: 'https:', 协议 host: 'nodejs.org:443', 主机域名 port: '443', 端口 hostname: 'nodejs.org', 域名 hash: '#12345', 哈希字符串 search: '?a=1&b=2&c=3', 查询字符串 query: 'a=1&b=2&c=3', 查询字符串 // query: [Object: null prototype] { a: '1', b: '2', c: '3' }, pathname: '/dist/latest-v16.x/docs/api/url.html', 请求地址 path: '/dist/latest-v16.x/docs/api/url.html?a=1&b=2&c=3', 携带数据的请求地址 href: 'https://nodejs.org:443/dist/latest-v16.x/docs/api/url.html?a=1&b=2&c=3#12345' */
-
-
拼接地址
-
resolve()console.log(url.resolve(urlStr,'./index.html')); // https://nodejs.org/dist/latest-v16.x/docs/api/index.html
-
内置模块-querystring
-
内置模块-querystring:可以将查询字符串和对象进行转换
-
导入
let qs = require('querystring'); -
将对象转为查询字符串
-
stringify()let obj = {a:1,b:2,c:3}; console.log(qs.stringify(obj)); // a=1&b=2&c=3
-
-
将查询字符串转给对象
-
parse()console.log(qs.parse('name=zs&age=17&a=1&b=2')); //{ name: 'zs', age: '17', a: '1', b: '2' }
-