HTML5和ES6新特性
封面画师:唏嘘的星辰 p站ID:13312138
语义化
语义化: 看到它就知道它是干啥的
语义化标签: 使用单词作为标签,使标签更容易被识别
常见的语义化标签
结构化标签: header, main, footer, nav, aside, hr
文本标签: p, h1-h6, strong, i, del
语义化概念
什么是HTML语义化?HTML语义化的好处是什么?html语义化
让页面的内容结构化
,结构更清晰
,便于对浏览器、搜索引擎解析
;
即使在没有样式CSS情况下也以一种文档格式显示,并且是容易阅读的;
搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,利于SEO;
使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
什么是SEO? 如何做SEO优化?
SEO,是英文SearchEngineOptimization的缩写,中文的意思: 搜索引擎优化。SEO的目的就是让网站或者网页在搜索结果中,处于靠前的位置。
实现SEO优化的方式有:
- 合理的title、description、keywords:搜索对着三项的权重逐个减小,
title
值强调重点即可;description
把页面内容高度概括,不可过分堆砌关键词;keywords
列举出重要关键词。 - 语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页
- 重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,保证重要内容一定会被抓取
- 重要内容不要用js输出:爬虫不会执行js获取内容
- 少用iframe:搜索引擎不会抓取iframe中的内容
- 非装饰性图片必须加alt
- 提高网站速度:
网站速度
是搜索引擎排序的一个重要指标。
音视频标签
音视频标签的四个属性:
src 属性设置音视频资源路径
controls 属性设置播放控制器
autoplay 属性设置自动播放, 需要开启浏览器授权
loop 属性设置循环播放
<audio src="https://music.163.com/song/media/outer/url?id=28263184.mp3" controls loop>此浏览器不支持播放</audio>
<video src="./demo.mp4" controls loop></video>
audio演示:
弹性布局
流式布局: 网页默认的布局方式, 元素按照行标签从左向右,块标签从上向下依次排列, 尽量不使标签重叠
弹性布局: html5新增的网页布局方式, 需要手动设置, 有如下特点:
- 1,弹性布局总是给父元素设置, 对第一级子元素起效
- 2,弹性布局元素默认从左向右排列,不换行,高度为100%(包含margin)
- 3,弹性布局元素没有行标签块标签之分,可以正常设置宽距和宽高
- 4,弹性布局元素不能使用浮动, 可以使用定位
弹性布局中的属性设置:
弹性容器 的属性设置:display: flex;
设置弹性容器flex-wrap: wrap;
设置换行justify-content: space-evenly;
设置说水平对齐方式align-items: center;
设置单行对齐方式align-content: flex-start;
设置多行对齐方式
弹性元素 的属性设置:align-self: flex-end;
弹性元素单独对齐order: 1;
弹性元素的排列顺序flex-grow: 2
元素扩展比例flex-shrink: 0;
元素压缩比例
布局 适配 兼容
响应式布局
概念: 在浏览器窗口尺寸变化时, 使一个网页在不同宽度的窗口上显示不同的排版样式和内容
使用场景: 一般用于PC端网页
实现原理: 使用媒体查询
浏览器适配
概念: 在浏览器窗口尺寸变化时, 使一个网页在不同宽度的窗口上显示相同的排版样式和内容
使用场景: 一般用于移动端网页
实现原理: 视口单位 vw / vh
浏览器兼容
概念: 在不同内核的浏览器上, 使一个网页在不同浏览器的上显示相同的排版样式和内容
使用场景: 在不同内核和不同版本浏览器上的兼容(兼容低版本浏览器)
实现原理: 代码优化
媒体查询
媒体查询的结构:
@media 媒体类型 and (媒体特性) {
条件满足时,渲染的css样式
}
媒体类型: 此样式会在什么设备上生效 all / screen
媒体特性: 此样式渲染的条件, min-width / max-height
移动端适配
适配: 在不同的设备上显示相同的样式
viewport
: 视口, 可视窗口, 一般指移动端屏幕窗口- 移动端视口中有两个长度单位
vw
和vh
vw: viewportWidth
视口宽度 100vw == 视口宽度vh: viewportHeight
视口高度 100vh == 视口高度
100% 和 100vw 的区别
- 100% 指的是父标签宽度
- 100vw 指得是宽口宽度
注意: 移动端竖向滚动条不占宽度, 所以移动端 100vw == 100%
结论: 建议PC端用100%, 移动端用100vw
ES6
es6: ECMA script 6
ECMAScript
是javascript的一种语法标准es6
第六版js语法ECMA
: European Computer Manufacturers 2Xf2E6aEU7n685eHEbXGYHrmWn2y7a62UWBrtZzodVdD
截止目前,各大浏览器都已经实现了es6标准的90%以上。nodejs几乎完全支持es6。
参考教程: http://jsrun.net/t/cZKKp
requestAnimationFrame
requestAnimationFrame
和 setTimeout
类似, 都是一个短暂不重复的计时器
- setTimeout可自定义计时时间
- requestAnimationFrame 的计时时间是默认的, 一帧的时间,约等于16.67ms
requestAnimationFrame
优点: 在保证动画流畅运行的前提下, 最大限度的节约性能消耗
由于收到设备性能的影响, 网页的刷新率(帧率)不一定准确维持在60帧/s, 可能偏高或偏低, 所以16.67ms的帧时间是一个大概的值,不是定值
// 使用setInterval实现小球d1运动
var y1 = 0
setInterval(() => {
y1 += 1
if(y1 >= 600) y1 = 0
d1.style.top = y1 + "px"
}, 16.67);
// 使用setTimeout实现小球d2运动
var y2 = 0
function move(){
y2 += 1
if(y2 >= 600) y2 = 0
d2.style.top = y2 + "px"
setTimeout(move, 16.67); // 递归
}
setTimeout(move, 16.67);
// 使用requestAnimationFrame实现小球d3运动
var y3 = 0
function move1(){
y3 += 1
if(y3 >= 600) y3 = 0
d3.style.top = y3 + "px"
requestAnimationFrame(move1); // 递归
}
requestAnimationFrame(move1);
地理定位
navigator.geolocation
是H5新增的API,用于浏览器获取位置信息
window.navigator.geolocation.getCurrentPosition(res=>{
console.log(res.coords)
// accuracy: 55 定位精度
//
// altitude: 0 海拔高度
//
// altitudeAccuracy: 0 海拔精确度
//
// heading: null 移动方向
//
// latitude: 34.72326699999999 纬度
//
// longitude: 113.75165477777777 经度
//
// speed: null 移动速度
}) // 开启位置监视后, 直接获取位置失效
// 开始监控位置
var watch = navigator.geolocation.watchPosition(res=>{
console.log("位置更新", res)
})
// 取消监听
setTimeout(() => {
navigator.geolocation.clearWatch(watch)
}, 10000);
百度地图
<!-- 引用百度地图API3.0文件 -->
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=你自己の密钥"></script>
<script type="text/javascript" src="https://api.map.baidu.com/api?v=1.0&&type=webgl&ak=你自己の密钥">
</script>
<!-- 创建地图容器元素 -->
<div id="container"></div>
<button onclick="getEarth()">开启地球模式</button>
<script>
// 创建地图实例
var map = new BMap.Map("container");
// 设置中心点坐标
var point = new BMap.Point(116.404, 39.915);
// 地图初始化,同时设置地图展示级别
map.centerAndZoom(point, 15);
// 获取当前位置
navigator.geolocation.getCurrentPosition(res=>{
// panTo()方法将让地图平滑移动至新中心点
map.panTo(new BMap.Point(res.coords.longitude, res.coords.latitude));
// 逆地址解析 // 创建地理编码实例, 并配置参数获取乡镇级数据
var myGeo = new BMap.Geocoder({extensions_town: true});
// 根据坐标得到地址描述
myGeo.getLocation(new BMap.Point(res.coords.longitude, res.coords.latitude), function(result){
if (result){
alert(result.address);
}
});
})
//开启鼠标滚轮缩放
map.enableScrollWheelZoom(true);
// 添加平移缩放控件
map.addControl(new BMap.NavigationControl());
// 比例尺控件
map.addControl(new BMap.ScaleControl());
// 缩略图控件
map.addControl(new BMap.OverviewMapControl());
// 地图类型
map.addControl(new BMap.MapTypeControl());
// 添加路况图层
var traffic = new BMap.TrafficLayer();
map.addTileLayer(traffic);
// 圆形区域搜索
var local = new BMap.LocalSearch(map,{
renderOptions:{
map: map,
autoViewport: true
}
});
// 在天安门附近搜索银行
local.searchNearby("漫展","上海");
</script>
<script>
// 点击按钮切换地球模式
function getEarth(){
console.log(123)
var map = new BMapGL.Map("container");
var point = new BMapGL.Point(116.404, 39.915);
map.centerAndZoom(point, 1);
map.enableScrollWheelZoom(true);
map.setMapType(BMAP_EARTH_MAP);
}
</script>
canvas画布
canvas绘图Api
在script标签中添加如下注释,即可自动提示canvas API/** @type {HTMLCanvasElement} 添加canvas API语法提示*/
(一) 方法:
getContext("2d")
创建绘图对象
beginPath();
开始绘制路径
closePath();
结束绘制路径,闭合线框
moveTo(x,y);
移动到绘制起点
lineTo(x,y);
画线段到指定点
arc(x,y,r,start,end);
绘制圆形(弧度单位)
quadraticCurveTo(x,y,endX,endY)
二次贝塞尔曲线
bezierCurveTo(x1,y1,x2,y2,endX,endY)
三次贝塞尔曲线
stroke();
线条类型
fill();
填充类型
fillRect(x,y,w,h)
填充矩形
strokeRect(x,y,w,h)
线框矩形
clearRect(x,y,w,h)
清除矩形
strokeText(string,x,y)
绘制线条文字(空心)
fillText(string,x,y)
绘制填充文字(实心)
drawImage(img,x,y,width,height)
绘制图片(需在img的load函数中)
save()
保存当前状态设定
restore()
恢复到保存之前的状态设定
rotate()
旋转画布角度(弧度制)
(二) 属性:
fillStyle
填充颜色(支持rgb(),#fff,red)
strokeStyle
线条颜色
lineWidth
线条宽度(整数值,默认单位px)
lineJoin
折线拐角类型 (round表示圆角)
font
支持多个值, 例如”10px 宋体”
textBaseline
文字基点(默认bottom左下为基准点, top为左上)
globalCompositeOperation
绘制合成(具体值参考附件)
canvas语法
<style>
canvas{
border: 1px solid;
/* css设置尺寸只能缩放画布,不能改变画布实际尺寸 */
/* height: 600px; */
}
</style>
<body>
<!-- 属性height/width设置的才是画布的实际尺寸 -->
<canvas id="myCanvas" height="600"></canvas>
<script>
// 获取canvas标签元素
/** @type {HTMLCanvasElement} 添加canvas API语法提示*/
var canvas = document.getElementById("myCanvas")
// 通过canvas标签获取一个绘图对象
var ctx = canvas.getContext("2d")
// 1, 绘制线段
ctx.moveTo(20, 20); // 设置绘制起点
ctx.lineTo(120, 20); // 绘制线段到终点
ctx.lineTo(120, 120); // 绘制线段到另一个终点
ctx.lineTo(20, 120);
ctx.lineTo(20, 20);
ctx.moveTo(20,70); // 设置另一个绘制起点
ctx.lineTo(120,70);
ctx.moveTo(70, 20); // 设置另一个绘制起点
ctx.lineTo(70,120);
ctx.stroke(); // 执行绘制线段
// 2, 绘制线框矩形
ctx.strokeRect(150, 20, 100, 60);
// 绘制填充矩形
ctx.fillRect(150, 100, 100, 60);
// 清理矩形区域
ctx.clearRect(180, 130, 20, 20);
// 3, 绘制弧形和扇形
// ctx.moveTo(120, 200) // 先移动移动到圆弧起点
ctx.beginPath() // 开始另一个绘制路径
ctx.arc(70, 200, 50, 0, Math.PI);
// ctx.stroke() // 圆弧
ctx.fill() // 扇形区域
ctx.closePath() // 结束另一个绘制路径
// 4, 绘制曲线
// 二次贝塞尔曲线
ctx.moveTo(20, 300); // 曲线起点
ctx.quadraticCurveTo(100, 200, 240, 300);
ctx.stroke()
// 三次内塞尔曲线
ctx.beginPath()
ctx.strokeStyle = "yellow";
ctx.lineWidth = 2;
ctx.moveTo(20, 400);// 曲线起点
ctx.bezierCurveTo(100, 200, 200, 500, 240, 330);
ctx.stroke()
ctx.closePath()
// 5, 绘制文字
ctx.font = "50px 楷体";
ctx.strokeStyle = "red";
ctx.lineWidth = 1
ctx.fillStyle = "green"
ctx.strokeText("饕餮", 20, 500);
ctx.fillText("王粲博", 150, 500);
// 6, 绘制图片
var img = document.createElement("img")
img.src = "./item338.gif"
// 不能直接绘制图片,需要在img的load事件中绘制
img.onload = function(){
ctx.drawImage(img, 20, 520, 260, 80)
}
</script>
</body>
实例:刮刮乐
<style>
canvas{
position: relative;
background-image: url(item338.gif);
background-size: 100% 100%;
}
</style>
<body>
<canvas id="canvas"></canvas>
<script>
/** @type {HTMLCanvasElement} 添加canvas API语法提示*/
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
// 先绘制灰色图层
ctx.fillStyle = 'gray';
ctx.fillRect(0,0,300,150);
// 再绘制提示文字
ctx.font = "30px 黑体"
ctx.fillStyle = "green"
ctx.fillText('刮开有惊喜', 60, 80);
// 图层叠加 (目标图像是已经绘制的图层, 源图像是即将绘制的图像)
ctx.globalCompositeOperation = "destination-out"
// 刮图层的函数
function gua(e){
ctx.beginPath()
// e.pageX 相对于网页, e.layerX 相对于非静态定位元素
ctx.arc(e.layerX, e.layerY, 20, 0, Math.PI*2)
ctx.fill()
}
// 鼠标按下,开始绘制圆形
canvas.onmousedown = function(){
canvas.addEventListener("mousemove", gua)
}
// 鼠标抬起, 停止绘制
canvas.onmouseup = function(){
canvas.removeEventListener("mousemove", gua)
}
</script>
</body>
WebWoker
概念进程
: 一个应用的代码执行流程, 一个应用有且仅有一个进程线程
: 也叫分线程, 可以独立于主线程执行, 通过代码创建, 可以没有也可以有多个主线程
: 同 进程分线程
: 同 线程
总结:
分线程使用的API:new Worker()
创建分线程postMessage()
线程之间发送数据onmessage
事件,接收线程发送的数据
注意事项:
1, 分线程不能调用界面相关API,只有主线程可以操作界面
2, 线程之能同源访问, 跨域无法引入分线程脚本, 网页不能使用file协议本地打开
3, 分线程和主线程作用域相互隔离,不能直接调用数据, 需要使用postMessage和onmessage发送和接收
4, 分线程不能执行界面相关的alert()等API,但可以执行与界面无关的ajax请求
5, 分线程无法读取本地文件, 分线程中的数据都要来源于网络
参考教程: http://www.ruanyifeng.com/blog/2018/07/web-worker.html
块级作用域
var
关键字定义变量有两个作用域 全局作用域 和 局部作用域let
关键字定义变量有三个作用域 全局作用域, 局部作用域 和 块级作用域全局变量
: 在全局范围内定义的变量局部变量
: 在函数内部定义的变量块级变量
: 在语法块中定义的变量 比如循环,判断语法块
举例: 使用在语法块中定义的变量,只能在语法块中使用
if(true){
var d = 4;
let e = 5;
}
// console.log(d) // 4
// console.log(e) // undefined
举例: 在循环中, 实现延时打印
for(let i = 0; i < 5; i++){
// 在循环中使用var定义变量, 这个变量是全局变量, 每次循环的count共享同一个作用域, 所以下次循环的count把替换上次循环的count, 循环之后,全局只有一个count,值为4
var count = i;
// console.log(count) // 0,1,2,3,4
setTimeout(() => {
console.log(count) // 4,4,4,4,4
}, 100);
// 在循环中使用let定义变量, 这个变量是块级变量, 每次循环都会在一个新的块级作用域中定义一个独立块级变量number, 循环结束后, 就有5个块级变量number,他们的值各不相同, 互补影响
let number = i;
setTimeout(() => {
console.log(number) // 0,1,2,3,4
}, 200);
}
// console.log(i) // undefined 循环变量i建议使用let定义, 再循环之外无法调用, 也就避免了全局作用域污染
如果要在循环中添加计时器或事件绑定, 需要保留每次循环的变量值, 有以下两种解决方案,
- 使用let定义块级变量
for(let i = 0; i < 5; i ++){ setTimeout(()=>{ console.log(i) }, 300); }
- 使用闭包, 保持count局部变量
function outter(count){ return function inner(){ console.log(count) } } for(var i = 0; i < 5; i ++){ setTimeout(outter(i), 300); }
let和var的两点主要区别:
- var 在同一作用域可以重复定义同一个变量, let不行
- var 没有块级作用域, 在循环中定义的变量都是全局的, 会相互覆盖, let在循环中定义的变量都是独立的,互不影响
const常量
ES6新增了两个关键字
- let 定义变量
- const 定义常量
const
定义值类型数据, 绝对不能改const
定义引用类型数据, 数据内容(对象/数组中的数据)可以改, 引用类型本身(内存地址)不能改
字符串模板
如果要把字符串和变量拼接到一起, 有两种写法
var year = 2020, month = 4, day = 20;
// 1, ES5字符串拼接
console.log(year + "年" + month + '月' + day + "日")
// 2, es6模板字符串
console.log(`${year}年${month}月${day}日`)
// 由于模板字符串支持换行, 所以可以用来渲染标签字符串
var htmlStr = `
<ul>
<li>${year}年</li>
<li>${month}月</li>
<li>${day}日</li>
</ul>
`
console.log(htmlStr)
箭头函数
箭头函数用法:
- 在事件函数或计时器或异步回调函数中可以保留this上下文指向
- 箭头函数在参数和返回值处,满足条件时,都可以简化
- 在对象中有一个简化的函数写法
// 普通的function函数不能转化为箭头函数
function add(){ }
// 只有匿名函数可以转化为箭头函数
var add = function(){ }
var add = ()=>{}
// 在node环境下, this默认指向空对象
console.log(1, this) // {}
// 可以修改这个对象
this.name = "zhangsan"
console.log(2, this) // {name: "zhangsan"}
// setTimeout会把this指向修改为Timeout对象
setTimeout(function(){
console.log(3, this) // Timeout
}, 1000);
// 箭头函数会保留this的上下文指向, 使this指向和setTimeout外部相同
setTimeout(() => {
console.log(4, this) // {name: "zhangsan"}
}, 1000);
// 箭头函数的简化
var add = (count)=>{
// return count ++
return ++ count
}
console.log(add(5))
// 如果箭头函数只有一个参数, 可以省略小括号
var add = count => { return ++ count; }
// 如果箭头函数的函数体中只有一句return返回, return和{}可同时省略
var add = count => ++count;
console.log(add(100))
// 箭头函数在对象中的写法, 对象中的函数有以下三种写法
var student = {
name: "张三",
age: 20,
// ES5函数的写法, 其中的this是当前对象
eat: function(){
console.log(this.name+"吃饭")
},
// ES6箭头函数写法, 其中的this是对象上下文(全局作用域)指向
drink: ()=>{
console.log(this.name+"喝水")
},
// 简化写法, 其中this的指向还是当前对象
sleep(){
console.log(this.name+"睡觉")
}
}
student.eat()
student.drink()
student.sleep()
数组对象
数组解构: var [count1,count2,...count3] = array
数组赋值1: array.push(…array2)
数组赋值2: array = […array, …array2, 5,6,7]
对象解构: var { age } = student
对象赋值: {…student, height:120}
// 数组的解构
var array = [1,2,3,4]
// 一般,使用数组中的数据要使用索引取值
console.log(array[1])
// 数组解构允许我们直接使用变量读取数组中的数据
let [count1, count2, count3, count4] = array
console.log(count3)
// 注意: 由于数组有顺序,在解构时,一般前边变量个数和数组中数据个数要一一对应
// 可以使用 ... 语法解构数组中的一部分数据(靠后的好几条数据)
let [num1, ...num2] = array
console.log(num2)
// 数组的赋值
var array2 = ["a", "b", "c"]
// 需求: 把array2拼接到array中
// 方法1: 循环array2把array2的每一条数据加入array中
// array2.forEach(item=>{
// array.push(item)
// })
// console.log(array)
// 方法2: 使用concat数组拼接API
// array = array.concat(array2)
// console.log(array)
// 方法3: 使用 ... 数组赋值
array.push(...array2)
console.log(array)
// 数组也可以如下拼接
array = [...array, ...array2, 5,6,7]
console.log(array)
// 对象的解构
var student = {
name: "张三",
age: 12,
sex: "男",
phone: "110"
}
console.log(student)
// 如果要获取一个对象中的某个字段, 直接用对象打点调用即可
console.log(student.phone)
// 也可以使用对象解构写法获取某个字段值
var { age, sex } = student;
console.log(age, sex)
// 注意: 由于对象中的数据没有顺序, 所有对象解构无需把所有字段都写上,可以按需定义变量读取对象中的某一个或几个字段即可, 但必须保证变量名和字段名相同
// 把student这个对象中的数据拼接到people对象中
var people = {
height: 120,
weight: 180,
...student
}
console.log(people)
// 字符串解构, 和数组解构类似
var string = "ABC"
var [a,b,c] = string;
console.log(a,b,c)
数组对象拼接
数组拼接var array1 = [1,2,3]
var array2 = [4,5,6]
console.log([...array1, ...array2, 7,8,9])
[
1, 2, 3, 4, 5,
6, 7, 8, 9
]
对象拼接var obj1 = { age: 20 }
var obj2 = { sex: true }
console.log({...obj1, ...obj2})
{ age: 20, sex: true }
参数的默认值
function add1(a,b){
console.log(a+b)
}
add1() // NaN
add1(3) // NaN
add1(3,4) // 7
add1(3,4,5) // 7
// js函数定义时有两个参数, 调用时就必须传入至少两个参数,
// 如果传入的实参少于形参的个数,为防止错误, 可以给形参设置默认值
// ES5中使用 || 设置默认值
function add2(a,b){
a = a || 0; //设置默认值
b = b || 0; //设置默认值
console.log( a + b )
}
add2() // 0
add2(3) // 3
add2(3,4) // 7
add2(3,4,5) // 7
// ES6中使用 初始化形参 设置默认值
function add3(a=0, b=0){
console.log( a + b )
}
add3() // 0
add3(3) // 3
add3(3,4) // 7
add3(3,4,5) // 7
// 关于函数的参数, 如果参数个数不确定, 如果获取所有参数
// ES5中通过arguments字段获取参数数组
function add4(){
// arguments是一个类数组, 里边放了所有参数
console.log(arguments, Array.from(arguments))
}
add4() // {} []
add4(3) // {"0": 3} [3]
add4(3,4) // {"0":3, "1": 4} [3,4]
add4(3,4,5) // {'0': 3, '1': 4, '2': 5 } [3,4,5]
// ES6中通过形参...arr获取参数数组
function add5(...array){
// 形参array是所有参数所在的数组
console.log(array)
}
add5() // []
add5(3) // [3]
add5(3,4) // [3,4]
add5(3,4,5) // [3,4,5]
Promise
Promise的由来
js代码执行过程中,分为同步执行
和异步执行
的两种代码执行逻辑同步执行
: 代码总是从上向下依次执行
,只有上一句执行完,才会执行下一句, 默认js是通过异步执行
: 特殊形况下,异步代码可以和其他代码同时执行,相互不影响,不严格按照顺序执行, 常见的异步执行逻辑有 计时器, js事件, ajax, promise调用
异步代码的标志性特点
是: 回调函数
结论: 一段逻辑代码中,总是先执行同步代码
, 再执行异步代码
上代码:
// 这是正常的函数返回值调用
function getRandom(){
return Math.random()
}
var random = getRandom()
console.log(1, random)
// 当函数中有异步操作时,能否return返回异步结果
function getRandom2(){
setTimeout(() => {
console.log(3)
return Math.random()// 异步函数中的return是无效的
}, 100);
}
var random2 = getRandom2()
console.log(2, random2) // 2 undefined
Promise的语法
// Promise 在es6中属于一个 类 要通过new创建promise对象使用, 如下
// 新建一个promise对象, 参数是一个同步函数, 同步函数的参数是成功和失败的回调函数
var p = new Promise(function(resolve, reject){
// 使用new创建promise对象这个操作是同步操作还是异步操作? 同步的
console.log(1)
// 在promise对象中执行异步任务
setTimeout(() => {
console.log(3)
var random = Math.random()
if(random > 0.5){
resolve("成功")
}else{
reject("失败")
}
}, 100);
})
console.log(2)
// 使用promise对象调用函数then拿成功的数据, 调用catch拿失败的数据
p.then(data=>{
console.log(4, data)
})
p.catch(err=>{
console.log(4, err)
})
// promise支持链式调用
p.then().catch().then().then(data=>{
console.log(5, data)
}).then()
// promise无论何时,何地,多次调用then,总能拿到成功时的数据
// 注意: Promise对象的创建过程是同步的, then函数的调用是异步的, 简单说peomise是异步的
Promise示例
// 需求: 在四个异步操作中打印四句话,而且要保证打印顺序
// setTimeout(() => {
// console.log("窗前明月光")
// }, Math.random()*1000);
// setTimeout(() => {
// console.log("疑似地上霜")
// }, Math.random()*1000);
// setTimeout(() => {
// console.log("举头望明月")
// }, Math.random()*1000);
// setTimeout(() => {
// console.log("低头鞋两双")
// }, Math.random()*1000);
// 方法一: 四个计时器嵌套, 上一个执行后再执行下一个
// setTimeout(() => {
// console.log("窗前明月光");
// setTimeout(() => {
// console.log("疑似地上霜");
// setTimeout(() => {
// console.log("举头望明月");
// setTimeout(() => {
// console.log("低头鞋两双")
// }, Math.random()*1000);
// }, Math.random()*1000);
// }, Math.random()*1000);
// }, Math.random()*1000);
// 此方法有两个缺点:
// 1, 代码多层嵌套造成结构复杂,可读性差
// 2, 四句打印的计时时间累加起来了, 降低效率
// 首先, 我们使用promise解决异步任务多层嵌套问题
// new Promise(function(resolve){
// setTimeout(() => {
// console.log("窗前明月光"); resolve() // 调用resolve以执行then
// }, Math.random()*1000);
// }).then(function(){
// return new Promise(function(resolve){
// setTimeout(() => {
// console.log("疑似地上霜"); resolve() // 调用resolve以执行then
// }, Math.random()*1000);
// })
// }).then(function(){
// return new Promise(function(resolve){
// setTimeout(() => {
// console.log("举头望明月"); resolve() // 调用resolve以执行then
// }, Math.random()*1000);
// })
// }).then(function(){
// return new Promise(function(resolve){
// setTimeout(() => {
// console.log("低头鞋两双"); resolve() // 调用resolve以执行then
// }, Math.random()*1000);
// })
// })
// 然后, 我们使用promise解决 多异步任务并发 的顺序问题
var p1 = new Promise(function(resolve){
setTimeout(() => {
resolve("窗前明月光")
}, Math.random()*1000);
})
var p2 = new Promise(function(resolve){
setTimeout(() => {
resolve("疑似地上霜")
}, Math.random()*1000);
})
var p3 = new Promise(function(resolve){
setTimeout(() => {
resolve("举头望明月")
}, Math.random()*1000);
})
var p4 = new Promise(function(resolve){
setTimeout(() => {
resolve("低头鞋两双")
}, Math.random()*1000);
})
// Promise有一个类函数all, 可以把多个promise对象合并成一个
var p = Promise.all([p1,p2,p3,p4])
// 当合并前的所有promise对象都调用resolve后, 才会调用合并后的then
p.then(function(array){
// all合并后的then回调函数参数是一个数组, 数组中按照合并顺序依次获取每一个promise对象的成功数据
console.log(array)
})
// 类函数: 就是使用类名调用的函数 如 all()
// 实例函数: 就是使用对象调用的函数 如 then()
// 总结: 说一下你对promise的理解?
// promise 是es6新增的一个类,主要用于解决项目开发中常见异步回掉地域问题,原理上是对回调函数的封装, 开发中经常用于多异步任务嵌套时和多异步任务并发时
async和await
// 在es6新增了一个关键字 叫 async 用于放在函数function前,修饰函数
function a (){
console.log('a')
}
a()
// 在函数前添加async标记可以使此函数变为异步函数
async function b(){
console.log('b')
}
b()
// 箭头函数也可以定义为异步函数
var c = async ()=>{
console.log('c')
return "彬哥" // 即使有返回值, 调用得到的也是promise
}
c()
// async异步函数的返回值总是一个promise对象
console.log(a())
console.log(b())
console.log(c())
async function d(){
var p = new Promise(resolve=>{
setTimeout(() => {
resolve("成功")
}, 1000);
})
// 调用then函数,在异步回调函数中获取数据
p.then(data=>{
console.log(1,data)
})
// await关键字一般放在promise对象前边, 等待promise对象成功的结果,一旦成功, 无需调用then, 而是通过同步返回值的形式拿到promise中的异步结果
var res = await p
console.log(2,res)
// await 的作用就是让异步回调结构改成同步返回值结构,依然能拿到数据, 好处是简化代码结构, 减少嵌套, 优化语法结构
// await is only valid in async function await只能在async函数中使用
}
console.log(d())
ES6模块化
模块化
: 把一个整体的js文件或代码块通过功能性分割成多个js文件或代码块, 这样每一个js文件都称之为一个模块
, 然后通过固定语法导入/导出把多个js文件联系到一起, 这种操作,我们称之为模块化
html文件中使用script导入js算不算模块化?
答: 不算, 因为模块化有一个显著特点,是作用域隔离, 各个模块用于各自独立的作用, 互不干涉, 用script导入js相当于把所有js拼到一起了,共享了同一个作用域,这种不能称之为模块化模块化语法:
导出:export default data
导入:import data from "path"
从模块myModule中导入数据myModule.js
var count = 100
// 使用模块化语法把数据导出
export default count
index.js
import count from "./myModule.js"
// Cannot use import statement outside a module
// 不识别import这个语法, 因为es6模块化语法是不能直接在node或浏览器执行的
console.log(count)
es6模块化
一般用于vue框架
或react框架
中