1、利用Promise封装setTimeout
function sleep(duration) {
return new Promise(function(resolve, reject) {
console.log("b");
setTimeout(resolve,duration);
})
}
console.log("a");
sleep(5000).then(()=>console.log("c"));
2、利用async和await
function sleep(duration) {
return new Promise(function(resolve, reject) {
setTimeout(resolve,duration);
})
}
async function foo(){
console.log("a")
await sleep(2000).then(()=>console.log("b"))
console.log("c")
}
foo()
function sleep(duration) {
return new Promise(function(resolve, reject) {
setTimeout(resolve,duration);
})
}
async function foo(name){
await sleep(2000)
console.log(name)
}
async function foo2(){
await foo("a");
await foo("b");
}
foo2() // 每两秒输出 a, b
// 应避免使用generator/iterator实现异步
const _this = this
let iterator = generator()
async function init() {
// console.log(await initData(this, "中国", false))
_this.$store.commit("setChinaValue", await initData(_this, "中国"))
iterator.next() // 进下一个yield
}
function* generator() {
yield init()
yield _this.isLoading = false
}
iterator.next()
3、立即执行函数的代替
// 可以避免()前不加分号 导致上一行的函数执行
void function(){
var a;
console.log(a,1111111)
//code
}();
4、改变this
// 注意: 箭头、class 不接受 this 的函数类型。使用call、apply、bind无效,但不报错
function foo(a, b, c){
console.log(this);
console.log(a, b, c);
}
foo.call({}, 1, 2, 3); // 函数.call, 改变函数内的this指向 参数对象;同时会传参,执行函数
foo.apply({}, [1, 2, 3]);
foo.bind({}, 1, 2, 3)(); // bind不会执行函数,需要自己执行
5、try return finally
// Completion Record
function foo(){
try{
return 0;
} catch(err) {
} finally { // finally 先执行, 如果在这里return,会覆盖try中的return
console.log("a")
}
}
console.log(foo()); // a 0
// Completion Record 表示一个语句执行完之后的结果,它有三个字段:
// - [[type]] 表示完成的类型,有 break continue return throw 和 normal 几种类型;
// - [[value]] 表示语句的返回值,如果语句没有,则是 empty;** Chrome显示的就是它 **
// - [[target]] 表示语句的目标,通常是一个 JavaScript 标签
// 利用Completion Record 的target,可以用于声明语句 跳出多层循环
outer: while(true) {
inner: while(true) {
break outer;
}
}
console.log("finished")
6、异步的 for of
// for..of 用于可迭代对象,如Array、Set等,也包括generator
// 如下 异步生成器函数配备了异步的 for of
function sleep(duration) {
return new Promise(function(resolve, reject) {
setTimeout(resolve,duration);
})
}
async function* foo(){ // 异步生成器函数每隔一秒生成一个数字
i = 0;
while(true) {
await sleep(1000);
yield i++;
}
}
for await(let e of foo()) // 使用 for await of 来访问这个异步生成器函数的结果
console.log(e); // 形成了一个每隔一秒打印一个数字的无限循环
7、try 配合 throw
// try 部分用于标识捕获异常的代码段
// catch 部分则用于捕获异常后做一些处理
// finally 则是用于执行后做一些必须执行的清理工作。
try {
throw new Error("error");
} catch(e) {
console.log(e);
} finally {
console.log("finally");
}
8、html语义化标签
// em 斜体, 表示重音,与strong不同
今天我吃了一个<em>苹果</em>。
今天我吃了<em>一个</em>苹果。
// ruby rt rp 这是汉字 上面加注释
<ruby>
这<rt>zhe</rt>是<rt>shi</rt>汉<rt>han</rt>字<rt>zi</rt>
</ruby>
// p标签是block的*
// 典型组织结构
<body>
<header>
<nav>
……
</nav>
</header>
<aside>
<nav>
……
</nav>
</aside>
<section>……</section>
<section>……</section>
<section>……</section>
<footer>
<address>……</address>
</footer>
</body>
// 多文章结构
<body>
<header>……</header>
<article>
<header>……</header>
<section>……</section>
<section>……</section>
<section>……</section>
<footer>……</footer>
</article>
<article>
……
</article>
<article>
……
</article>
<footer>
<address></address>
</footer>
</body>
9、css的 at 规则(即高级用法)
// charset 用于提示 CSS 文件使用的字符编码方式,必须出现在最前面
@charset "utf-8";
// import 用于引入一个 CSS 文件
@import "mystyle.css";
@import url("mystyle.css");
// media 就是大名鼎鼎的 media query 使用的规则了,它能够对设备的类型进行一些判断
@media print {
body { font-size: 10pt }
}
// page 用于分页媒体访问网页时的表现设置,设置周围的结构
@page {
size: 8.5in 11in;
margin: 10%;
@top-left {
content: "Hamlet";
}
@top-right {
content: "Page " counter(page);
}
}
// counter-style 产生一种数据,用于定义列表项的表现。
@counter-style triangle {
system: cyclic;
symbols: ‣;
suffix: " ";
}
// keyframes 产生一种数据,用于定义动画关键帧
@keyframes diagonal-slide {
from {
left: 0;
top: 0;
}
to {
left: 100px;
top: 100px;
}
}
// fontface 用于定义一种字体,icon font 技术就是利用这个特性来实现的。
@font-face {
font-family: Gentium;
src: url(http://example.com/fonts/Gentium.woff);
}
p { font-family: Gentium, serif; }
// @support 检查环境的特性,它与 media 比较类似。
// @namespace 用于跟 XML 命名空间配合的一个规则,表示内部的 CSS 选择器全都带上特定命名空间。
// @viewport 用于设置视口的一些特性,不过兼容性目前不是很好,多数时候被 HTML 的 meta 代替
除去 at 规则 就是 qualified 规则(普通规则),由 选择器 和 声明列表 组成
- 选择器:标签,id、class、属性和伪类
- 选择器连接符号:空格、>、+、~、||
- 声明属性值——函数:calc(), min(), max(), clamp()可选范围, toggle()可切换
10、head html元信息类标签
meta 标签是一组键值对,它是一种通用的元信息表示标签
一般的 meta 标签由 name 和 content 两个属性来定义。name 表示元信息的名,content 则用于表示元信息的值。 name 是一种比较自由的约定,HTTP 标准规定了一些 name 作为大家使用的共识,也鼓励大家发明自己的 name 来使用。
charset: 添加了 charset 属性的 meta 标签无需再有 name 和 content。
一般情况下,HTTP 服务端会通过 http 请求头来指定正确的编码方式,但是有些特殊的情况如使用 file 协议打开一个 HTML 文件,则没有 http 头,这种时候,charset meta 就非常重要了。
<meta charset="UTF-8">
http-equiv:具有 http-equiv 属性的 meta 标签,表示执行一个命令,这样的 meta 标签可以不需要 name 属性了。
接收的值:
content-type 添加http 请求头,并指定编码方式;
x-ua-compatible 模拟 http 头 x-ua-compatible,声明 ua 兼容性;
content-language 指定内容的语言;
default-style 指定默认样式表;
refresh 刷新;
set-cookie 模拟 http 头 set-cookie,设置 cookie;
content-security-policy 模拟 http 头 content-security-policy,声明内容安全策略。
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
name 为 viewport 的 meta:用于 移动端开发标准 key-value形式
如下指定了 宽度和缩放。width 可以是device-width,跟设备宽度相等;height,device-height;initial-scale:初始缩放比例;minimum-scale:最小缩放比例;maximum-scale:最大缩放比例;user-scalable:是否允许用户缩放。
<meta name="viewport" content="width=500, initial-scale=1">
对于已经做好了移动端适配的网页,应该把用户缩放功能禁止掉,宽度设为设备宽度,一个标准的 meta 如下:
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
meta 标签可以被自由定义,只要写入和读取的双方约定好 name 和 content 的格式就可以了。
11、css选择器
“根据 id 选单个元素”“class 和 class 的组合选成组元素”“tag 选择器确定页面风格”这样的简单原则来使用选择器,不要搞出过于复杂的选择器。
类型选择器
// svg的a标签 和 a标签 用namespace 区分
@namespace svg url(http://www.w3.org/2000/svg);
@namespace html url(http://www.w3.org/1999/xhtml);
svg|a { stroke:blue; stroke-width:1;}
html|a { font-size:40px}
属性选择器
1. [att] 直接在方括号中放入属性名,是检查元素是否具有这个属性,只要元素有这个属性,不论属性是什么值,都可以被选中。
2. [att=val] 精确匹配,检查一个元素属性的值是否是 val。
3. [att~=val]多种匹配,检查一个元素的值是否是若干值之一,这里的 val 不是一个单一的值了,可以是用空格分隔的一个序列。
4. [att|=val]开头匹配,检查一个元素的值是否是以 val 开头,它跟精确匹配的区别是属性只要以 val 开头即可,后面内容不管。
html中的特殊字符,可以把 val 用引号括起来,形成一个 CSS 字符串。CSS 字符串允许使用单双引号来规避特殊字符,也可以用反斜杠转义
伪类选择器 以冒号开头
// 树结构关系
:empty 伪类表示没有子节点的元素,空白文本也行
:nth-child 和 :nth-last-child, :nth-child(even)选偶数,:nth-child(4n)选4的倍数
:first-child :last-child
:only-child
of-type 系列 变形的语法糖,S:nth-of-type(An+B) 是:nth-child(|An+B| of S) 的另一种写法。
// 链接与行为
:any-link 表示任意的链接,包括 a、area 和 link 标签
:link 表示未访问过的链接, :visited 表示已经访问过的链接
:hover 表示鼠标悬停在上的元素
:active 表示用户正在激活这个元素,如用户按下按钮,鼠标还未抬起时,这个按钮就处于激活状态。
:focus 表示焦点落在这个元素之上。
:target 用于选中浏览器 URL 的 hash 部分所指示的元素。
// 逻辑伪类
:not 支持不好
// 其他
12、伪元素
兼容好的只有四种
::first-line 和 ::first-letter 是比较类似的伪元素,一个表示元素的第一行,一个表示元素的第一个字母。
// 可用于首字母大写,并向左浮动
p::first-letter {
text-transform: uppercase;
font-size:2em;
float:left;
}
// CSS 标准规定了 first-line 必须出现在最内层的块级元素之内, 一些坑https://time.geekbang.org/column/article/84633
::before 表示在元素内容之前插入一个虚拟的元素,::after 则表示在元素内容之后插入。
// 这两个伪元素 必须指定 content 属性才会生效
p.classname::before {
display: block;
content: "pseudo! ";
}
// content 可以为 counter ,实现在前面加序号
<p class="special">I'm real element</p>
p.special::before {
display: block;
content: counter(chapno, upper-roman) ". ";
}
有了这两个伪元素,一些修饰性元素,可以使用纯粹的 CSS 代码添加进去,这能够很好地保持 HTML 代码中的语义,既完成了显示效果,又不会让 DOM 中出现很多无语义的空元素。
13、iframe
iframe,这个标签能够嵌入一个完整的网页。
不过,在移动端,iframe 受到了相当多的限制,它无法指定大小,里面的内容会被完全平铺到父级页面上。
同时很多网页也会通过 http 协议头禁止自己被放入 iframe 中。
iframe 标签也是各种安全问题的重灾区。opener、window.name、甚至 css 的 opacity 都是黑客可以利用的漏洞。
// 以前的用法
<iframe src="http://time.geekbang.org"></iframe>
// 在新标准中,为 iframe 加入了 sandbox 模式和 srcdoc 属性。解决跨域
<iframe sandbox srcdoc="<p>Yeah, you can see it <a href="/gallery?mode=cover&amp;page=1">in my gallery</a>."></iframe>
使用 srcdoc 属性创建了一个新的文档,嵌入在 iframe 中展示,并且使用了 sandbox 来隔离。这样,这个 iframe 就不涉及任何跨域问题了。
14、CSS 三大经典问题:垂直居中,两列等高,自适应宽
flex实现
// 两列等高,即一列定高,使另一列高度相同
// 路是创建一个只有一行的 flexbox,然后用 stretch 属性让每个元素高度都等于行高。
<div class="parent">
<div class="child" style="height:300px;">
</div>
<div class="child">
</div>
</div>
<br/>
<div class="parent">
<div class="child" >
</div>
<div class="child" style="height:300px;">
</div>
</div>
.parent {
display:flex;
width:300px;
justify-content:center;
align-content:center;
align-items:stretch;
}
.child {
width:100px;
outline:solid 1px;
}
// 自适应宽
// 给要自适应的元素添加 flex 属性即可
<div class="parent">
<div class="child1">
</div>
<div class="child2">
</div>
</div>
.parent {
display:flex;
width:300px;
height:200px;
background-color:pink;
}
.child1 {
width:100px;
background-color:lightblue;
}
.child2 {
width:100px;
flex:1;
outline:solid 1px;
}
15、!DOCTYPE html
其他参考:https://time.geekbang.org/column/article/92227
这些复杂的 DTD 写法并没有什么实际作用(浏览器根本不会用 SGML 引擎解析它们),因此,到了 HTML5,干脆放弃了 SGML 子集这项坚持,规定了一个简单的,大家都能记住的 DTD
<!DOCTYPE html>
16、aria 无障碍
ARIA 全称为 Accessible Rich Internet Applications,它表现为一组属性,是用于可访问性的一份标准。
可访问性其实是一个相当大的课题,它的定义包含了各种设备访问、各种环境、各种人群访问的友好性。不单单是永久性的残障人士需要用到可访问性,健康的人也可能在特定时刻处于需要可访问性的环境。
ARIA 给 HTML 元素添加的一个核心属性就是 role
// 给一个 span 添加了 checkbox 角色,这样,表示我们这个 span 被用于 checkbox,这意味着,我们可能已经用 JS 代码绑定了这个 span 的 click 事件,并且以 checkbox 的交互方式来处理用户操作。
<span role="checkbox" aria-checked="false" tabindex="0" aria-labelledby="chk1-label">
</span> <label id="chk1-label">Remember my preferences</label>
// 同时,ARIA 系统还提供了一系列 ARIA 属性给 checkbox 这个 role,这意味着,我们可以通过 HTML 属性变化来理解这个 JavaScript 组件的状态,读屏软件等三方客户端,就可以理解我们的 UI 变化,这正是 ARIA 标准的意义。
role 的定义是一个树形的继承关系:widget 表示一些可交互的组件,structure 表示文档中的结构,window 则代表窗体
17、浏览器工作
HTTP 请求, DOM 树构建、CSS 计算、渲染、合成、绘制 是一条流水线
即不需要等到上一步骤完全结束,就开始处理上一步的输出,这样我们在浏览网页时,才会看到逐步出现的页面。
构建DOM树:(具体见 https://time.geekbang.org/column/article/80260)
栈顶元素就是当前节点;
遇到属性,就添加到当前节点;
遇到文本节点,如果当前节点是文本节点,则跟文本节点合并,否则入栈成为当前节点的子节点;
遇到注释节点,作为当前节点的子节点;
遇到 tag start 就入栈一个节点,当前节点就是这个节点的父节点;遇到 tag end 就出栈一个节点(还可以检查是否匹配)。
18、DOM API
节点:DOM 树形结构中的节点相关 API:
Document , DocumentFrangment ,Element,..;表示在 DOM 树中的关系:parentNode childNodes firstChild lastChild nextSibling previousSibling;操作 DOM 树的 API:appendChild insertBefore removeChild replaceChild;其他高级 API:compareDocumentPosition 是一个用于比较两个节点中关系的函数,contains 检查一个节点是否包含另一个节点的函数,isEqualNode 检查两个节点是否完全相同,isSameNode 检查两个节点是否是同一个节点,实际上在 JavaScript 中可以用“===”,cloneNode 复制一个节点,如果传入参数 true,则会连同子元素做深拷贝。
元素和属性API:可以把元素的 Attribute 当作字符串来看待,这样就有以下的 API:getAttribute setAttribute removeAttribute hasAttribute;如果你追求极致的性能,还可以把 Attribute 当作节点:getAttributeNodeset AttributeNode
查找元素:querySelector 系列的 API 不如 getElement 系列的 API。
事件:触发和监听事件相关 API
Range:操作文字范围相关 API:
Range API 是一个比较专业的领域,如果不做富文本编辑类的业务,不需要太深入
// 创建 Range 一般是通过设置它的起止来实现
var range = new Range(),
firstText = p.childNodes[1],
secondText = em.firstChild
range.setStart(firstText, 9) // do not forget the leading space
range.setEnd(secondText, 4)
// 此外,通过 Range 也可以从用户选中区域创建,这样的 Range 用于处理用户选中区域:
var range = document.getSelection().getRangeAt(0);
// 更改 Range 选中区段内容的方式主要是取出和插入,分别由 extractContents 和 insertNode 来实现。
var fragment = range.extractContents()
range.insertNode(document.createTextNode("aaaa"))
// 下面完整的例子 展示了如何使用 range 来取出元素和在特定位置添加新元素
var range = new Range(),
firstText = p.childNodes[1],
secondText = em.firstChild
range.setStart(firstText, 9) // do not forget the leading space
range.setEnd(secondText, 4)
var fragment = range.extractContents()
range.insertNode(document.createTextNode("aaaa"))
遍历:遍历 DOM 需要的 API:
DOM API 中还提供了 NodeIterator 和 TreeWalker 来遍历树,比起直接用属性来遍历,NodeIterator 和 TreeWalker 提供了过滤功能,还可以把属性节点也包含在遍历之内。
// NodeIterator用法 不常用
// 第二个参数是掩码,这两个设计都是传统 C 语言里比较常见的用法。
var iterator = document.createNodeIterator(document.body, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT, null, false);
var node;
while(node = iterator.nextNode()) // 以 nextNode 返回 null 来标志结束
{
console.log(node);
}
// TreeWalker 的用法
// 比起 NodeIterator,TreeWalker 多了在 DOM 树上自由移动当前节点的能力,一般来说,这种 API 用于“跳过”某些节点,或者重复遍历某些节点。
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, null, false)
var node;
while(node = walker.nextNode())
{
if(node.tagName === "p")
node.nextSibling();
console.log(node);
}
// 这两个用起来不太方便,建议需要遍历 DOM 的时候,直接使用递归和 Node 的属性。
19、CSSOM View的 API
没有element.width这样的DOM API,是因为正如 HTML 和 CSS 分别承担了语义和表现的分工,DOM 和 CSSOM 也有语义和表现的分工。
CSSOM 是 CSS 的对象模型,在 W3C 标准中,它包含两个部分:描述样式表和规则等 CSS 的模型部分(CSSOM),和跟元素视图相关的 View 部分(CSSOM View)。
// CSSOM API
// document 的 styleSheets 属性表示文档中的所有样式表,这是一个只读的列表
// 样式表只能使用 style 标签或者 link 标签创建
// 虽然无法用 CSSOM API 来创建样式表,但是我们可以修改样式表中的内容
document.styleSheets[0].insertRule("p { color:pink; }", 0)
document.styleSheets[0].removeRule(0)
// 更进一步,我们可以获取样式表中特定的规则(Rule),并且对它进行一定的操作,具体来说,就是使用它的 cssRules 属性来实现
document.styleSheets[0].cssRules
CSSOM View这一部分的 API,可以视为 DOM API 的扩展(BOM?),添加了三个部分功能:窗口部分 window,滚动部分和布局部分
// 窗口 API 用于操作浏览器窗口的位置、尺寸等
// 一些浏览器出于安全考虑没有实现,也不适用于移动端浏览器
// moveTo(x, y) 窗口移动到屏幕的特定坐标;
// moveBy(x, y) 窗口移动特定距离;
// resizeTo(x, y) 改变窗口大小到特定尺寸;
// resizeBy(x, y) 改变窗口大小特定尺寸。
window.open("about:blank", "_blank" ,"width=100,height=100,left=100,right=100" )
// 滚动API
// scrollX 是视口的属性,表示 X 方向上的当前滚动距离,有别名 pageXOffset;
// scroll(x, y) 使得页面滚动到特定的位置,有别名 scrollTo,支持传入配置型参数 {top, left};
// ..
// 通过这些属性和方法,我们可以读取视口的滚动位置和操纵视口滚动。不过,要想监听视口滚动事件,我们需要在 document 对象上绑定事件监听函数:
document.addEventListener("scroll", function(event){
//......
})
// 视口滚动 API 是页面的顶层容器的滚动,大部分移动端浏览器都会采用一些性能优化,它和元素滚动不完全一样
// 元素滚动 API
// scrollTop 元素的属性,表示 Y 方向上的当前滚动距离。
// ...
// 可滚动的元素也支持 scroll 事件,我们在元素上监听它的事件即可
element.addEventListener("scroll", function(event){
//......
})
// 布局 API
// 是整个 CSSOM 中最常用到的部分,我们同样要分成全局 API 和元素上的 API
// 如下图 window.screen等等,主要使用的是 innerHeight、innerWidth 和 devicePixelRatio 三个属性
20、浏览器事件:捕获和冒泡
事件来自输入设备,键盘;鼠标;触摸屏。
我们现代的 UI 系统,都源自 WIMP 系统。WIMP 即 Window Icon Menu Pointer 四个要素,它最初由施乐公司研发,后来被微软和苹果两家公司应用在了自己的操作系统上。
一般认为我们能够“点击一个按钮”,实际上并非如此,我们只能够点击鼠标上的按钮或者触摸屏,是操作系统和浏览器把这个信息对应到了一个逻辑上的按钮,再使得它的视图对点击事件有反应。
实际上点击事件来自触摸屏或者鼠标,鼠标点击并没有位置信息,但是一般操作系统会根据位移的累积计算出来,跟触摸屏一样,提供一个坐标给浏览器。那么,把这个坐标转换为具体的元素上事件的过程,就是捕获过程了。而冒泡过程,则是符合人类理解逻辑的:当你按电视机开关时,你也按到了电视机。
// 捕获和冒泡 示例
<body>
<input id="i"/>
</body>
document.body.addEventListener("mousedown", () => {
console.log("key1")
}, true)
document.getElementById("i").addEventListener("mousedown", () => {
console.log("key2")
}, true)
document.body.addEventListener("mousedown", () => {
console.log("key11")
}, false)
document.getElementById("i").addEventListener("mousedown", () => {
console.log("key22")
}, false)
// 输出:“key1”“key2”“key22”“key11”
// addEventListener 有三个参数:事件名称;事件处理函数;捕获还是冒泡。
// 第三个参数不一定是 bool 值,也可以是个对象,它提供了更多选项。once:只执行一次。passive:承诺此事件监听不会调用 preventDefault,这有助于性能。useCapture:是否捕获(否则冒泡)。
// 建议默认不传第三个参数, 除非是开发 组件 或者 库
焦点 focues
// Tab 键被用来切换到下一个可聚焦的元素,焦点系统占用了 Tab 键,但是可以用 JavaScript 来阻止这个行为。
document.body.focus();
document.body.blur();
自定义事件
var evt = new Event("look", {"bubbles":true, "cancelable":false});
document.dispatchEvent(evt);
// 旧的自定义事件方法(使用 document.createEvent 和 initEvent)已经被废弃。