第四章 JavaScript DOM
4.1、DOM概述
当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。
HTML DOM 模型被结构化为 对象树 :
通过这个对象模型,JavaScript 获得创建动态 HTML 的所有力量:
- JavaScript 能改变页面中的所有 HTML 元素
- JavaScript 能改变页面中的所有 HTML 属性
- JavaScript 能改变页面中的所有 CSS 样式
- JavaScript 能删除已有的 HTML 元素和属性
- JavaScript 能添加新的 HTML 元素和属性
- JavaScript 能对页面中所有已有的 HTML 事件作出反应
- JavaScript 能在页面中创建新的 HTML 事件
换言之:HTML DOM 是关于如何获取、更改、添加或删除 HTML 元素的标准。
4.2、DOM文档节点
4.2.1、节点概述
节点Node,是构成我们网页的最基本的组成部分,网页中的每一个部分都可以称为是一个节点。
比如:html标签、属性、文本、注释、整个文档等都是一个节点。
虽然都是节点,但是实际上它们的具体类型是不同的。
比如:标签我们称为元素节点、属性称为属性节点、文本称为 文本节点、文档称为文档节点。
节点的类型不同,属性和方法也都不尽相同。
节点:Node——构成HTML文档最基本的单元。
常用节点分为四类:
- 文档节点:整个HTML文档
- 元素节点:HTML文档中的HTML标签
- 属性节点:元素的属性
- 文本节点:HTML标签中的文本内容
4.2.2、节点属性
4.2.3、文档节点
文档节点(Document)代表的是整个HTML文 档,网页中的所有节点都是它的子节点。
document对象作为window对象的属性存在的,我们不用获取可以直接使用。
通过该对象我们可以在整个文档访问内查找节点对象,并可以通过该对象创建各种节点对象。
4.2.4、元素节点
HTML中的各种标签都是元素节点(Element),这也是我们最常用的一个节点。
浏览器会将页面中所有的标签都转换为一个元素节点, 我们可以通过document的方法来获取元素节点。
例如:document.getElementById()
,根据id属性值获取一个元素节点对象。
4.2.5、属性节点
属性节点(Attribute)表示的是标签中的一个一个的属 性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。可以通过元素节点来获取指定的属性节点。
例如:元素节点.getAttributeNode("属性名")
,根据元素节点的属性名获取一个属性节点对象。
注意:我们一般不使用属性节点。
4.2.6、文本节点
文本节点(Text)表示的是HTML标签以外的文本内容,任意非HTML的文本都是文本节点,它包括可以字面解释的纯文本内容。文本节点一般是作为元素节点的子节点存在的。获取文本节点时,一般先要获取元素节点,在通过元素节点获取文本节点。
例如:元素节点.firstChild;
,获取元素节点的第一个子节点,一般为文本节点。
4.3、DOM文档操作
文档对象代表您的网页,,如果您希望访问 HTML 页面中的任何元素,那么您总是从访问 document 对象开始。
下面是一些如何使用 document 对象来访问和操作 HTML 的实例。
4.3.1、查找 HTML 元素
方法 | 描述 |
---|---|
document.getElementById(id) | 通过元素 id 来查找元素。 |
document.getElementsByTagName(name) | 通过标签名来查找元素。 |
document.getElementsByClassName(name) | 通过类名来查找元素。 |
document.querySelector(CSS选择器) | 通过CSS选择器选择一个元素。 |
document.querySelectorAll(CSS选择器) | 通过CSS选择器选择多个元素。 |
需求描述:创建一个按钮,通过id获取按钮节点对象
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
console.log(btn);
</script>
</body>
</html>
需求描述:创建一个按钮,通过标签名获取按钮节点对象数组
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button>我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementsByTagName("button");
console.log(btn);
</script>
</body>
</html>
需求描述:创建一个按钮,通过类名获取按钮节点对象数组
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button class="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementsByClassName("btn");
console.log(btn);
</script>
</body>
</html>
需求描述:创建一个按钮,通过CSS选择器选择该按钮
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button class="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.querySelector(".btn");
console.log(btn);
</script>
</body>
</html>
需求描述:创建一个无序列表,通过CSS选择器选择该列表的所有li
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul class="list">
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var list = document.querySelectorAll(".list li");
console.log(list);
</script>
</body>
</html>
4.3.2、获取 HTML 的值
方法 | 描述 |
---|---|
元素节点.innerText | 获取 HTML 元素的 inner Text。 |
元素节点.innerHTML | 获取 HTML 元素的 inner HTML。 |
元素节点.属性 | 获取 HTML 元素的属性值。 |
元素节点.getAttribute(attribute) | 获取 HTML 元素的属性值。 |
元素节点.style.样式 | 获取 HTML 元素的行内样式值。 |
需求描述:创建一个按钮,然后获取按钮的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
console.log(btn.innerText);
</script>
</body>
</html>
需求描述:创建一个div,然后在div中插入一个h1标题,获取div中的html代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box">
<h1>我是Box中的大标题</h1>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
console.log(box.innerHTML);
</script>
</body>
</html>
需求描述:创建一个超链接,默认为空,设置href属性为https://www.baidu.com
,使用JavaScript代码读取href属性
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a id="a" href="https://www.baidu.com">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
console.log(a.href);
</script>
</body>
</html>
需求描述:创建一个超链接,默认为空,设置href属性为https://www.baidu.com
,使用JavaScript代码读取href属性
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a id="a" href="https://www.baidu.com">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
console.log(a.getAttribute("href"));
</script>
</body>
</html>
需求描述:创建一个正方形div,默认颜色为红色,使用JavaScript代码获取div的宽度
注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color,需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写,我们通过style属性设置的样式都是行内样式,同样的获取也是行内样式,而行内样式有较高的优先级,所以通过JS修改的样式往往会立即显示,但是如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加!important
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div style="width: 100px;height: 100px;background: red;" id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
console.log(box.style.width);
</script>
</body>
</html>
拓展知识1:
通过style属性设置和读取的都是内联样式,无法读取样式表中的样式或者说正在应用的样式,如果想要读取当前正在应用的样式属性我们可以使用元素.currentStyle.样式名
来获取元素的当前显示的样式,它可以用来读取当前元素正在显示的样式,如果当前元素没有设置该样式,则获取它的默认值,但是currentStyle只有IE浏览器支持,其它的浏览器都不支持,在其它浏览器中可以使用getComputedStyle()这个方法来获取元素当前的样式,这个方法是window的方法,可以直接使用,但是需要两个参数:
- 第一个参数:要获取样式的元素
- 第二个参数:可以传递一个伪元素,一般都传null
该方法会返回一个对象,对象中封装了当前元素对应的样式,可以通过 对象.样式名
来读取样式,如果获取的样式没有设置,则会获取到真实的值,而不是默认值,比如:没有设置width,它不会获取到auto,而是一个长度,但是该方法不支持IE8及以下的浏览器。通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,如果要修改必须通过style属性,因此,我们可以写一个适配各个浏览器的读取元素样式的方法。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
/*样式表的样式*/
#box {
width: 200px;
height: 200px;
background-color: green;
}
</style>
</head>
<body>
<div style="width: 100px;height: 100px;" id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/*通用的获取元素样式的方法*/
function getStyle(obj, name) {
if (window.getComputedStyle) {
//正常浏览器的方式,具有getComputedStyle()方法
return getComputedStyle(obj, null)[name];
} else {
//IE8的方式,没有getComputedStyle()方法
return obj.currentStyle[name];
}
}
var box = document.getElementById("box");
console.log(getStyle(box, "width"));
console.log(getStyle(box, "height"));
console.log(getStyle(box, "background-color"));
</script>
</body>
</html>
谷歌浏览器:
火狐浏览器:
IE9-IE11 浏览器:
IE5-IE8 浏览器:
有同学会说,在IE5-IE8 浏览器中,老师这不是还不一样吗?因此我们建议设置颜色数值一般采用rgb或者rgba,最好不要采用英文单词的这种颜色值,这样最终展示出来的效果就一致了。
拓展知识2:编写一段兼容性代码,用来获取任意标签的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
console.log(getInnerText(a));
/*获取任意标签的内容*/
function getInnerText(element) {
// 判断浏览器是否支持textContent,如果支持,则使用textContent获取内容,否则使用innerText获取内容。
if(typeof element.textContent == "undefined") {
return element.innerText;
} else {
return element.textContent;
}
}
</script>
</body>
</html>
谷歌浏览器:
火狐浏览器:
IE5-IE11浏览器:
4.3.3、改变 HTML 的值
方法 | 描述 |
---|---|
元素节点.innerText = new text content | 改变元素的 inner Text。 |
元素节点.innerHTML = new html content | 改变元素的 inner HTML。 |
元素节点.属性 = new value | 改变 HTML 元素的属性值。 |
元素节点.setAttribute(attribute, value) | 改变 HTML 元素的属性值。 |
元素节点.style.样式 = new style | 改变 HTML 元素的行内样式值。 |
需求描述:创建一个按钮,然后改变按钮的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
btn.innerText = "我是JavaScript的按钮";
console.log(btn);
</script>
</body>
</html>
需求描述:创建一个div,然后在div中插入一个h1标题
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
box.innerHTML = "<h1>我是Box中的大标题</h1>";
console.log(box);
</script>
</body>
</html>
**需求描述:创建一个超链接,默认为空,使用JavaScript代码设置href属性为https://www.baidu.com
**
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a id="a" href="">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
a.href="https://www.baidu.com";
console.log(a);
</script>
</body>
</html>
**需求描述:创建一个超链接,默认为空,使用JavaScript代码设置href属性为https://www.baidu.com
**
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a id="a" href="">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
a.setAttribute("href", "https://www.baidu.com");
console.log(a);
</script>
</body>
</html>
需求描述:创建一个正方形div,默认颜色为红色,使用JavaScript代码改变为绿色
注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color,需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写,我们通过style属性设置的样式都是行内样式,同样的获取也是行内样式,而行内样式有较高的优先级,所以通过JS修改的样式往往会立即显示,但是如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加!important
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div style="width: 100px;height: 100px;background: red;" id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
box.style.background = "green";
console.log(box);
</script>
</body>
</html>
拓展知识1:
修改节点的内容除了常用的innerHTML和innerText之外,还有insertAdjacentHTML和insertAdjacentText方法,可以在指定的地方插入内容。insertAdjacentText方法与insertAdjacentHTML方法类似,只不过是插入纯文本,参数相同。
语法说明:
object.insertAdjacentHTML(where,html);
object.insertAdjacentText(where,text)
参数说明:
-
where:
- beforeBegin:插入到开始标签的前面
- beforeEnd:插入到结束标签的前面
- afterBegin:插入到开始标签的后面
- afterEnd:插入到结束标签的后面
-
html:一段html代码
-
text:一段文本值
注意事项:
- 这两个方法必须等文档加载好后才能执行,否则会出错。
- insertAdjacentText只能插入普通文本,insertAdjacentHTML插入html代码。
- 使用insertAdjacentHTML方法插入script脚本文件时,必须在script元素上定义defer属性。
- 使用insertAdjacentHTML方法插入html代码后,页面上的元素集合将发生变化。
- insertAdjacentHTML方法不适用于单个的空的元素标签(如img,input等)。
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="insert">
<p>你是我的小丫小苹果</p>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div = document.getElementById("insert");
div.insertAdjacentHTML('beforeBegin', '你是我的小丫小苹果');
</script>
</body>
</html>
拓展知识2:编写一段兼容性代码,用来设置任意标签的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
setInnerText(a, "你要打开百度吗?");
console.log(getInnerText(a));
/*获取任意标签的内容*/
function getInnerText(element) {
// 判断浏览器是否支持textContent,如果支持,则使用textContent获取内容,否则使用innerText获取内容。
if (typeof element.textContent == "undefined") {
return element.innerText;
} else {
return element.textContent;
}
}
/*设置任意标签的内容*/
function setInnerText(element, text) {
// 判断浏览器是否支持textContent,如果支持,则使用textContent设置内容,否则使用innerText设置内容。
if (typeof element.textContent == "undefined") {
return element.innerText = text;
} else {
return element.textContent = text;
}
}
</script>
</body>
</html>
谷歌浏览器:
火狐浏览器:
IE5-IE11浏览器:
4.3.4、修改 HTML 元素
方法 | 描述 |
---|---|
document.createElement(element) | 创建 HTML 元素节点。 |
document.createAttribute(attribute) | 创建 HTML 属性节点。 |
document.createTextNode(text) | 创建 HTML 文本节点。 |
元素节点.removeChild(element) | 删除 HTML 元素。 |
元素节点.appendChild(element) | 添加 HTML 元素。 |
元素节点.replaceChild(element) | 替换 HTML 元素。 |
元素节点.insertBefore(element) | 在指定的子节点前面插入新的子节点。 |
案例演示1:创建一个ul列表,然后在该列表中追加4个li标签
第一种方法:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.createElement("ul");
var li1 = document.createElement("li");
var text1 = document.createTextNode("列表项1");
li1.appendChild(text1);
ul.appendChild(li1);
var li2 = document.createElement("li");
var text2 = document.createTextNode("列表项2");
li2.appendChild(text2);
ul.appendChild(li2);
var li3 = document.createElement("li");
var text3 = document.createTextNode("列表项3");
li3.appendChild(text3);
ul.appendChild(li3);
var li4 = document.createElement("li");
var text4 = document.createTextNode("列表项4");
li4.appendChild(text4);
ul.appendChild(li4);
document.getElementsByTagName("body")[0].appendChild(ul);
</script>
</body>
</html>
第二种方法:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.createElement("ul");
var li1 = document.createElement("li");
li1.innerHTML = "列表项1";
ul.appendChild(li1);
var li2 = document.createElement("li");
li2.innerHTML = "列表项2";
ul.appendChild(li2);
var li3 = document.createElement("li");
li3.innerHTML = "列表项3";
ul.appendChild(li3);
var li4 = document.createElement("li");
li4.innerHTML = "列表项4";
ul.appendChild(li4);
document.getElementsByTagName("body")[0].appendChild(ul);
</script>
</body>
</html>
第三种方法:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.createElement("ul");
var li1 = "<li>列表项1</li>";
var li2 = "<li>列表项2</li>";
var li3 = "<li>列表项3</li>";
var li4 = "<li>列表项4</li>";
ul.innerHTML = li1 + li2 + li3 + li4;
document.getElementsByTagName("body")[0].appendChild(ul);
</script>
</body>
</html>
案例演示2:创建一个ul列表,里边有四个li子元素,删除第一个li,替换最后一个li
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="ul">
<li id="first">列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li id="last">列表项4</li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.getElementById("ul");
var first = document.getElementById("first");
var last = document.getElementById("last");
/*删除第一个*/
ul.removeChild(first);
/*替换最后一个*/
var replaceLi = document.createElement("li");
replaceLi.innerHTML = "列表4的替换";
ul.replaceChild(replaceLi, last);
</script>
</body>
</html>
案例演示3:创建一个ul列表,里边有四个li子元素,在第一个li前边插入一个id为zero的li
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="ul">
<li id="first">列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.getElementById("ul");
var first = document.getElementById("first");
var zero = document.createElement("li");
zero.innerHTML = "列表0的新增";
ul.insertBefore(zero, first);
</script>
</body>
</html>
拓展知识:
动态判断、添加、删除、切换样式,支持IE5-IE11,谷歌浏览器、火狐浏览器等
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
.b1 {
width: 100px;
height: 100px;
background-color: red;
}
.b2 {
width: 300px;
height: 300px;
background-color: yellow;
}
</style>
</head>
<body>
<button id="btn0">判断b2样式</button>
<button id="btn1">添加b2样式</button>
<button id="btn2">删除b2样式</button>
<button id="btn3">切换b2样式</button>
<br>
<br>
<div id="box" class="b1"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn0 = document.getElementById("btn0");
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var btn3 = document.getElementById("btn3");
var box = document.getElementById("box");
btn0.onclick = function () {
alert(hasClass(box, "b2"));
};
btn1.onclick = function () {
addClass(box, "b2");
};
btn2.onclick = function () {
removeClass(box, "b2");
};
btn3.onclick = function () {
toggleClass(box, "b2");
};
/*
* 判断一个元素中是否含有指定的class属性值
* 如果有该class,则返回true,没有则返回false
*/
function hasClass(obj, cn) {
var reg = new RegExp("\\b" + cn + "\\b");
return reg.test(obj.className);
}
/*
* 向一个元素中添加指定的class属性值
* 参数:
* obj 要添加class属性的元素
* cn 要添加的class值
*/
function addClass(obj, cn) {
// 检查obj中是否含有cn
if (!hasClass(obj, cn)) {
obj.className += " " + cn;
}
}
/*
* 删除一个元素中的指定的class属性
*/
function removeClass(obj, cn) {
var reg = new RegExp("\\b" + cn + "\\b");
obj.className = obj.className.replace(reg, "");
}
/*
* toggleClass可以用来切换一个类
* 如果元素中具有该类,则删除
* 如果元素中没有该类,则添加
*/
function toggleClass(obj, cn) {
// 判断obj中是否含有cn
if (hasClass(obj, cn)) {
// 存在,则删除
removeClass(obj, cn);
} else {
// 没有,则添加
addClass(obj, cn);
}
}
</script>
</body>
</html>
4.3.5、查找 HTML 父子
方法 | 描述 |
---|---|
元素节点.parentNode | 返回元素的父节点。 |
元素节点.parentElement | 返回元素的父元素。 |
元素节点.childNodes | 返回元素的一个子节点的数组(包含空白文本Text节点)。 |
元素节点.children | 返回元素的一个子元素的集合(不包含空白文本Text节点)。 |
元素节点.firstChild | 返回元素的第一个子节点(包含空白文本Text节点)。 |
元素节点.firstElementChild | 返回元素的第一个子元素(不包含空白文本Text节点)。 |
元素节点.lastChild | 返回元素的最后一个子节点(包含空白文本Text节点)。 |
元素节点.lastElementChild | 返回元素的最后一个子元素(不包含空白文本Text节点)。 |
元素节点.previousSibling | 返回某个元素紧接之前节点(包含空白文本Text节点)。 |
元素节点.previousElementSibling | 返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)。 |
元素节点.nextSibling | 返回某个元素紧接之后节点(包含空白文本Text节点)。 |
元素节点.nextElementSibling | 返回指定元素的后一个兄弟元素(相同节点树层中的下一个元素节点)。 |
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box">
<ul id="ul">
<li><a href="https://www.baidu.com">我是超链接1</a></li>
<li id="two"><a href="https://www.baidu.com">我是超链接2</a></li>
<li><a href="https://www.baidu.com">我是超链接3</a></li>
<li><a href="https://www.baidu.com">我是超链接4</a></li>
</ul>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
var ul = document.getElementById("ul");
var two = document.getElementById("two");
console.log(ul.parentNode);
console.log(ul.parentElement);
console.log("===============");
console.log(box.childNodes);
console.log(box.children);
console.log("===============");
console.log(ul.firstChild);
console.log(ul.firstElementChild);
console.log(ul.lastChild);
console.log(ul.lastElementChild);
console.log("===============");
console.log(two.previousSibling);
console.log(two.previousElementSibling);
console.log(two.nextSibling);
console.log(two.nextElementSibling);
</script>
</body>
</html>
兼容性方法:
/*获取任意一个父级元素的第一个子元素*/
function getfirstElementChild(element) {
if(element.firstElementChild) {
return element.firstElementChild;
} else {
var node = element.firstChild;
while(node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
/*获取任意一个父级元素的最后一个子元素*/
function getLastElementChild(element) {
if(element.lastElementChild) {
return element.lastElementChild;
} else {
var node = element.lastChild;
while(node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的前一个兄弟元素*/
function getPreviousElementSibling(element) {
if(element.previousElementSibling) {
return element.previousElementSibling;
} else {
var node = element.previousSibling;
while(node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的后一个兄弟元素*/
function getNextElementSibling(element) {
if(element.nextElementSibling) {
return element.nextElementSibling;
} else {
var node = element.nextSibling;
while(node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="container">
<p>前面的P标签</p>
<b>加粗文本</b>
<a href="https://www.baidu.com" id="a">百度一下</a>
<i>斜体文本</i>
<p>最后的P标签</p>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/*第一个子元素*/
var firstNode = getfirstElementChild(document.getElementById("container"));
console.log(firstNode.innerHTML);
/*最后一个子元素*/
var lastNode = getLastElementChild(document.getElementById("container"));
console.log(lastNode.innerHTML);
/*指定元素的前一个子元素*/
var node1 = getPreviousElementSibling(document.getElementById("a"));
console.log(node1.innerHTML);
/*指定元素的后一个子元素*/
var node2 = getNextElementSibling(document.getElementById("a"));
console.log(node2.innerHTML);
/*获取任意一个父级元素的第一个子元素*/
function getfirstElementChild(element) {
if (element.firstElementChild) {
return element.firstElementChild;
} else {
var node = element.firstChild;
while (node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
/*获取任意一个父级元素的最后一个子元素*/
function getLastElementChild(element) {
if (element.lastElementChild) {
return element.lastElementChild;
} else {
var node = element.lastChild;
while (node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的前一个兄弟元素*/
function getPreviousElementSibling(element) {
if (element.previousElementSibling) {
return element.previousElementSibling;
} else {
var node = element.previousSibling;
while (node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的后一个兄弟元素*/
function getNextElementSibling(element) {
if (element.nextElementSibling) {
return element.nextElementSibling;
} else {
var node = element.nextSibling;
while (node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
</script>
</body>
</html>
谷歌浏览器:
火狐浏览器:
IE5-IE11浏览器:
4.4、DOM文档事件
4.4.1、事件概述
HTML事件可以触发浏览器中的行为,比方说当用户点击某个 HTML 元素时启动一段 JavaScript。
4.4.2、窗口事件
由窗口触发该事件 (同样适用于 <body> 标签):
属性 | 描述 |
---|---|
onblur | 当窗口失去焦点时运行脚本。 |
onfocus | 当窗口获得焦点时运行脚本。 |
onload | 当文档加载之后运行脚本。 |
onresize | 当调整窗口大小时运行脚本。 |
onstorage | 当 Web Storage 区域更新时(存储空间中的数据发生变化时)运行脚本。 |
案例演示1:当窗口失去焦点时,输出“窗口失去焦点”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onblur = function () {
console.log("窗口失去焦点");
};
</script>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
案例演示2:当窗口获取焦点时,输出“窗口获取焦点”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onfocus = function () {
console.log("窗口获取焦点");
};
</script>
</body>
</html>
案例演示3:当页面文档加载完成后,输出"Hello, World"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onload = function () {
console.log("Hello,World");
};
</script>
</body>
</html>
案例演示4:当调整窗口大小时,输出"窗口大小正在改变"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onresize = function () {
console.log("窗口大小正在改变");
};
</script>
</body>
</html>
4.4.3、表单事件
表单事件在HTML表单中触发 (适用于所有 HTML 元素,但该HTML元素需在form表单内):
属性 | 描述 |
---|---|
onblur | 当元素失去焦点时运行脚本。 |
onfocus | 当元素获得焦点时运行脚本。 |
onchange | 当元素改变时运行脚本。 |
oninput | 当元素获得用户输入时运行脚本。 |
oninvalid | 当元素无效时运行脚本。 |
onselect | 当选取元素时运行脚本。 |
onsubmit | 当提交表单时运行脚本。 |
案例演示1:当文本框获取焦点,文本框背景为红色,当文本框失去焦点,文本框背景为黄色
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text">
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当文本框获取焦点,文本框背景为红色 */
textInput.onfocus = function () {
this.style.background = "red";
};
/* 当文本框失去焦点,文本框背景为绿色 */
textInput.onblur = function () {
this.style.background = "green";
};
</script>
</body>
</html>
注意:这里为什么要用this,你不用this也可以,就直接
textInput.style.background = "red";
也不是不可以的,但是方法的调用规则就是谁调用this,this就指向谁,这样我们就可以简化代码了
案例演示2:当文本框内容改变时,鼠标离开文本框,自动将文本框的内容输出到控制台
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text">
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当文本框内容改变时,鼠标离开文本框,自动将文本框的内容输出到控制台 */
textInput.onchange = function () {
console.log(this.value);
};
</script>
</body>
</html>
案例演示3:当文本框内容改变时,立即将改变的内容输出到控制台
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text">
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当文本框内容改变时,立即将改变的内容输出到控制台 */
textInput.oninput = function () {
console.log(this.value);
};
</script>
</body>
</html>
案例演示4:如果单击“submit”,则不填写文本字段,将发生警报消息
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text" required>
<input type="submit" value="submit">
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 如果单击“submit”,则不填写文本字段,将发生警报消息 */
textInput.oninvalid = function () {
console.log("请您完善表单内容!");
};
</script>
</body>
</html>
案例演示5:当选中文本框的内容时,输出“您已经选择了文本框内容!”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text">
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当选中文本框的内容时,输出“您已经选择了文本框内容!” */
textInput.onselect = function () {
console.log("您已经选择了文本框内容!");
};
</script>
</body>
</html>
案例演示6:当提交表单的时候,在控制台输出“表单提交”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form id="myform">
<input type="submit" id="submit">
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var myform = document.getElementById("myform");
/* 当提交表单的时候,在控制台输出“表单提交” */
myform.onsubmit = function () {
console.log("表单提交");
return false;/* 用来阻止表单提交的,你不写它会跳转请求 */
};
</script>
</body>
</html>
4.4.4、键盘事件
通过键盘触发事件,类似用户的行为:
属性 | 描述 |
---|---|
onkeydown | 当按下按键时运行脚本。 |
onkeyup | 当松开按键时运行脚本。 |
onkeypress | 当按下并松开按键时运行脚本。 |
案例演示1:当键盘按下判断当前的按键是不是 a ,如果是就输出true,否则输出false
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/* 当键盘按下判断当前的按键是不是 a ,如果是就输出true,否则输出false */
window.onkeydown = function (event) {
/* 解决兼容性问题 */
event = event || window.event;
if (event.keyCode == 65) {
console.log("true");
} else {
console.log("false");
}
};
</script>
</body>
</html>
案例演示2:使div可以根据不同的方向键向不同的方向移动
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box" style="width: 100px;height: 100px;background: red;position: absolute;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
//为document绑定一个按键按下的事件
document.onkeydown = function (event) {
event = event || window.event;
// 定义移动速度
var speed = 10;
// 选择移动方向
switch (event.keyCode) {
case 37:
box.style.left = box.offsetLeft - speed + "px";
break;
case 39:
box.style.left = box.offsetLeft + speed + "px";
break;
case 38:
box.style.top = box.offsetTop - speed + "px";
break;
case 40:
box.style.top = box.offsetTop + speed + "px";
break;
}
};
</script>
</body>
</html>
拓展知识:
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数。
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标的状态。
在IE8中,响应函数被触发时,浏览器不会传递事件对象,在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的。
解决事件对象的兼容性问题:event = event || window.event;
键鼠属性:
属性 | 描述 |
---|---|
ctrlKey | 返回当事件被触发时,“CTRL” 键是否被按下。 |
altKey | 返回当事件被触发时,“ALT” 是否被按下。 |
shiftKey | 返回当事件被触发时,“SHIFT” 键是否被按下。 |
clientX | 返回当事件被触发时,鼠标指针的水平坐标。 |
clientY | 返回当事件被触发时,鼠标指针的垂直坐标。 |
screenX | 返回当某个事件被触发时,鼠标指针的水平坐标。 |
screenY | 返回当某个事件被触发时,鼠标指针的垂直坐标。 |
4.4.5、鼠标事件
通过鼠标触发事件,类似用户的行为:
属性 | 描述 |
---|---|
onclick | 当单击鼠标时运行脚本。 |
ondblclick | 当双击鼠标时运行脚本。 |
onmousedown | 当按下鼠标按钮时运行脚本。 |
onmouseup | 当松开鼠标按钮时运行脚本。 |
onmousemove | 当鼠标指针移动时运行脚本。 |
onmouseover | 当鼠标指针移至元素之上时运行脚本,不可以阻止冒泡。 |
onmouseout | 当鼠标指针移出元素时运行脚本,不可以阻止冒泡。 |
onmouseenter | 当鼠标指针移至元素之上时运行脚本,可以阻止冒泡。 |
onmouseleave | 当鼠标指针移出元素时运行脚本,可以阻止冒泡。 |
onmousewheel | 当转动鼠标滚轮时运行脚本。 |
onscroll | 当滚动元素的滚动条时运行脚本。 |
案例演示1:创建一个正方形div,默认颜色为黑色,当鼠标移入div,背景颜色变为红色,当鼠标移出div,背景颜色变为绿色
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box" style="width: 100px;height: 100px;background: black;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
/* 当鼠标移入div,背景颜色变为红色 */
box.onmouseenter = function () {
this.style.background = "red";
};
/* 当鼠标移出div,背景颜色变为绿色 */
box.onmouseleave = function () {
this.style.background = "green";
};
</script>
</body>
</html>
案例演示2:编写一个通用的拖拽元素函数,创建两个div,进行拖拽演示,要求兼容IE8、火狐、谷歌等主流浏览器
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box1" style="width: 100px;height: 100px;background: red;position: absolute;"></div>
<div id="box2" style="width: 100px;height: 100px;background: green;position: absolute;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
drag(box1);
drag(box2);
/*
* 提取一个专门用来设置拖拽的函数
* 参数:开启拖拽的元素
*/
function drag(obj) {
//当鼠标在被拖拽元素上按下时,开始拖拽
obj.onmousedown = function (event) {
// 解决事件的兼容性问题
event = event || window.event;
// 设置obj捕获所有鼠标按下的事件
/**
* setCapture():
* 只有IE支持,但是在火狐中调用时不会报错,
* 而如果使用chrome调用,它也会报错
*/
obj.setCapture && obj.setCapture();
// obj的偏移量 鼠标.clentX - 元素.offsetLeft
// obj的偏移量 鼠标.clentY - 元素.offsetTop
var ol = event.clientX - obj.offsetLeft;
var ot = event.clientY - obj.offsetTop;
// 为document绑定一个鼠标移动事件
document.onmousemove = function (event) {
// 解决事件的兼容性问题
event = event || window.event;
// 当鼠标移动时被拖拽元素跟随鼠标移动
// 获取鼠标的坐标
var left = event.clientX - ol;
var top = event.clientY - ot;
// 修改obj的位置
obj.style.left = left + "px";
obj.style.top = top + "px";
};
// 为document绑定一个鼠标松开事件
document.onmouseup = function () {
// 取消document的onmousemove事件
document.onmousemove = null;
// 取消document的onmouseup事件
document.onmouseup = null;
// 当鼠标松开时,取消对事件的捕获
obj.releaseCapture && obj.releaseCapture();
};
/*
* 当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,
* 此时会导致拖拽功能的异常,这个是浏览器提供的默认行为,
* 如果不希望发生这个行为,则可以通过return false来取消默认行为,
* 但是这招对IE8不起作用
*/
return false;
};
}
</script>
</body>
</html>
4.4.6、媒体事件
通过视频(videos),图像(images)或音频(audio) 触发该事件。
属性 | 描述 |
---|---|
onabort | 当发生中止事件时运行脚本。 |
oncanplay | 当媒介能够开始播放但可能因缓冲而需要停止时运行脚本。 |
oncanplaythrough | 当媒介能够无需因缓冲而停止即可播放至结尾时运行脚本。 |
ondurationchange | 当媒介长度改变时运行脚本。 |
onemptied | 当媒介资源元素突然为空时(网络错误、加载错误等)运行脚本。 |
onended | 当媒介已抵达结尾时运行脚本。 |
onerror | 当在元素加载期间发生错误时运行脚本。 |
onloadeddata | 当加载媒介数据时运行脚本。 |
onloadedmetadata | 当媒介元素的持续时间以及其它媒介数据已加载时运行脚本。 |
onloadstart | 当浏览器开始加载媒介数据时运行脚本。 |
onpause | 当媒介数据暂停时运行脚本。 |
onplay | 当媒介数据将要开始播放时运行脚本。 |
onplaying | 当媒介数据已开始播放时运行脚本。 |
onprogress | 当浏览器正在取媒介数据时运行脚本。 |
onratechange | 当媒介数据的播放速率改变时运行脚本。 |
onreadystatechange | 当就绪状态(ready-state)改变时运行脚本。 |
onseeked | 当媒介元素的定位属性不再为真且定位已结束时运行脚本。 |
onseeking | 当媒介元素的定位属性为真且定位已开始时运行脚本。 |
onstalled | 当取回媒介数据过程中(延迟)存在错误时运行脚本。 |
onsuspend | 当浏览器已在取媒介数据但在取回整个媒介文件之前停止时运行脚本。 |
ontimeupdate | 当媒介改变其播放位置时运行脚本。 |
onvolumechange | 当媒介改变音量亦或当音量被设置为静音时运行脚本。 |
onwaiting | 当媒介已停止播放但打算继续播放时运行脚本。 |
4.4.7、其它事件
属性 | 描述 |
---|---|
onshow | 当 <menu> 元素在上下文显示时触发。 |
ontoggle | 当用户打开或关闭 <details> 元素时触发。 |
4.4.8、事件冒泡
事件的冒泡(Bubble):所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发,在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡。
案例演示1:创建两个div,叠放在一起,分别绑定单击事件,点击最里边的div,会触发两个div的单击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<div id="div1">
我是DIV1
<div id="div2">
我是DIV2
</div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
// 为div1绑定单击事件
div1.onclick = function () {
console.log("div1 的单击事件触发了!");
};
// 为div2绑定单击事件
div2.onclick = function () {
console.log("div2 的单击事件触发了!");
};
</script>
</body>
</html>
案例演示2:创建两个div,叠放在一起,分别绑定单击事件,点击最里边的div,不会触发两个div的单击事件,只会触发自己的单击事件,这时候我们可以取消事件冒泡
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<div id="div1">
我是DIV1
<div id="div2">
我是DIV2
</div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
// 为div1绑定单击事件
div1.onclick = function () {
console.log("div1 的单击事件触发了!");
stopBubble();
};
// 为div2绑定单击事件
div2.onclick = function () {
console.log("div2 的单击事件触发了!");
stopBubble();
};
// 取消事件冒泡
function stopBubble(event) {
// 如果提供了事件对象,则这是一个非IE浏览器
if (event && event.stopPropagation) {
// 因此它支持W3C的stopPropagation()方法
event.stopPropagation();
} else {
// 否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
</script>
</body>
</html>
案例演示3:当点击a标签的时候,阻止a标签的默认跳转事件,采用事件阻止
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
// 为a绑定单击事件
a.onclick = function () {
stopDefault();
};
// 阻止浏览器的默认行为
function stopDefault(event) {
if (event && event.preventDefault) {
// 阻止默认浏览器动作(W3C)
event.preventDefault();
} else {
// IE中阻止函数器默认动作的方式
window.event.returnValue = false;
}
return false;
}
</script>
</body>
</html>
4.4.9、事件委派
我们希望只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的,我们可以尝试将其绑定给元素的共同的祖先元素,也就是事件的委派。事件的委派,是指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。事件委派是利用了事件冒泡,通过委派可以减少事件绑定的次数,提高程序的性能。
案例演示:为ul列表中的所有a标签都绑定单击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="u1">
<li><a href="javascript:;" class="link">超链接一</a></li>
<li><a href="javascript:;" class="link">超链接二</a></li>
<li><a href="javascript:;" class="link">超链接三</a></li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var u1 = document.getElementById("u1");
// 为ul绑定一个单击响应函数
u1.onclick = function (event) {
event = event || window.event;
// 如果触发事件的对象是我们期望的元素,则执行,否则不执行
if (event.target.className == "link") {
console.log("我是ul的单击响应函数");
}
};
</script>
</body>
</html>
4.4.10、事件绑定
我们以前绑定事件代码只能一个事件绑定一个函数,那我们要是想一个事件对应多个函数,并且不存在兼容性的问题该如何解决呢?
接下来,我会直接提供两个已经编写好的事件绑定和事件解绑的兼容性代码,如下:
/*为元素绑定事件兼容性代码*/
function addEventListener(element, type, fn) {
if(element.addEventListener) {
element.addEventListener(type, fn, false);
} else if(element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
element["on" + type] = fn;
}
}
/*为元素解绑事件兼容性代码*/
function removeEventListener(element, type, fnName) {
if(element.removeEventListener) {
element.removeEventListener(type, fnName, false);
} else if(element.detachEvent) {
element.detachEvent("on" + type, fnName);
} else {
element["on" + type] = null;
}
}
案例演示:为按钮1的单击事件绑定两个函数,然后点击按钮2取消按钮1的单机事件绑定函数f1
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function f1() {
console.log("output1 ...");
};
function f2() {
console.log("output2 ...");
};
// 为按钮1的单击事件绑定两个函数
addEventListener(document.getElementById("btn1"), "click", f1);
addEventListener(document.getElementById("btn1"), "click", f2);
// 点击按钮2取消按钮1的单机事件绑定函数f1
document.getElementById("btn2").onclick = function () {
removeEventListener(document.getElementById("btn1"), "click", f1);
};
/*为元素绑定事件兼容性代码*/
function addEventListener(element, type, fn) {
if (element.addEventListener) {
element.addEventListener(type, fn, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
element["on" + type] = fn;
}
}
/*为元素解绑事件兼容性代码*/
function removeEventListener(element, type, fnName) {
if (element.removeEventListener) {
element.removeEventListener(type, fnName, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, fnName);
} else {
element["on" + type] = null;
}
}
</script>
</body>
</html>
谷歌浏览器:
火狐浏览器:
IE8-IE11浏览器:
4.4.11、事件传播
事件的传播:关于事件的传播网景公司和微软公司有不同的理解
微软公司认为事件应该是由内向外传播,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就说事件应该在冒泡阶段执行。
网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后在向内传播给后代元素。
W3C综合了两个公司的方案,将事件传播分成了三个阶段:
- 捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
- 目标阶段:事件捕获到目标元素,捕获结束开始在目标元素上触发事件
- 冒泡阶段:事件从目标元素向它的祖先元素传递,依次触发祖先元素上的事件
注意:如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true,一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false,并且注意,IE8及以下的浏览器中没有捕获阶段,我们可以使用
event.stopPropagation();
取消事件传播。
第五章 JavaScript BOM
5.1、BOM概述
浏览器对象模型(BOM)使 JavaScript 有能力与浏览器"对话"。
浏览器对象模型(Browser Object Model (BOM))尚无正式标准。
由于现代浏览器已经(几乎)实现了 JavaScript 交互性方面的相同方法和属性,因此常被认为是BOM的方法和属性。
浏览器对象模型(BOM)可以使我们通过JS来操作浏览器,在BOM中为我们提供了一组对象,用来完成对浏览器的操作,常见的BOM对象如下:
- Window:代表的是整个浏览器的窗口,同时window也是网页中的全局对象
- Navigator:代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器
- Location:代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息,或者操作浏览器跳转页面
- History:代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录,由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页,而且该操作只在当次访问时有效
- Screen:代表用户的屏幕的信息,通过该对象可以获取到用户的显示器的相关的信息
这些BOM对象在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用。
5.2、Window对象
5.2.1、弹出框
JavaScript 有三种类型的弹出框:警告框、确认框和提示框。
如果要确保信息传递给用户,通常会使用警告框。当警告框弹出时,用户将需要单击“确定”来继续。
语法
window.alert("sometext");
- 1
注意:window.alert() 方法可以不带 window 前缀来写。
实例
alert("我是一个警告框!");
- 1
如果您希望用户验证或接受某个东西,则通常使用“确认”框。
当确认框弹出时,用户将不得不单击“确定”或“取消”来继续进行。
如果用户单击“确定”,该框返回 true。如果用户单击“取消”,该框返回 false。
语法
window.confirm("sometext");
- 1
注意:window.confirm() 方法可以不带 window 前缀来编写。
实例
var r = confirm("请按按钮");
if (r == true) {
x = "您按了确认!";
} else {
x = "您按了取消!";
}
如果您希望用户在进入页面前输入值,通常会使用提示框。
当提示框弹出时,用户将不得不输入值后单击“确定”或点击“取消”来继续进行。
如果用户单击“确定”,该框返回输入值。如果用户单击“取消”,该框返回 NULL。
语法
window.prompt("sometext","defaultText");
- 1
window.prompt() 方法可以不带 window 前缀来编写。
实例
var person = prompt("请输入您的姓名", "比尔盖茨");
if (person != null) {
console.log(person);
}
5.2.2、定时事件
JavaScript 可以在时间间隔内执行,这就是所谓的定时事件( Timing Events)。
window 对象允许以指定的时间间隔执行代码,这些时间间隔称为定时事件。
通过 JavaScript 使用的有两个关键的方法:
-
setTimeout(function, milliseconds)
在等待指定的毫秒数后执行函数。
-
setInterval(function, milliseconds)
等同于 setTimeout(),但持续重复执行该函数。
setTimeout() 和 setInterval() 都属于 window 对象的方法。
setTimeout() 方法:延时器
window.setTimeout(function, milliseconds);
- 1
注意:window.setTimeout() 方法可以不带 window 前缀来编写。
- 第一个参数是要执行的函数。
- 第二个参数指示执行之前的毫秒数。
案例演示:单击按钮,等待 3 秒,然后控制台会输出 "Hello"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
btn.onclick = function () {
// 创建延时器
var timer = setTimeout(function () {
console.log("Hello");
}, 3000);
// 清除延时器
// clearTimeout(timer);
};
</script>
</body>
</html>
setInterval() 方法:定时器
setInterval() 方法在每个给定的时间间隔重复给定的函数。
window.setInterval(function, milliseconds);
- 1
注意:window.setInterval() 方法可以不带 window 前缀来写。
- 第一个参数是要执行的函数。
- 第二个参数每个执行之间的时间间隔的长度。
案例演示:单击按钮,每隔一秒向控制台输出 "Hello"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
btn.onclick = function () {
// 创建定时器
var timer = setInterval(function () {
console.log("Hello");
}, 1000);
// 清除定时器
// clearInterval(timer);
};
</script>
</body>
</html>
拓展知识:
做一个通用移动函数来实现小汽车(黑色方块)移动的效果
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
/*控制器样式*/
.controller {
width: 600px;
height: 50px;
line-height: 50px;
}
.controller button {
outline: none;
border: none;
margin: 0px;
padding: 0px;
width: 200px;
height: 50px;
font-size: 16px;
line-height: 50px;
text-align: center;
background-color: #E9E9E9;
cursor: pointer;
float: left;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border: 2px solid #F0F0F0;
}
.controller button:hover {
background-color: #F9F9F9;
}
/*公路样式*/
.road {
width: 100%;
height: 100px;
position: relative;
margin-top: 50px;
background: #3DB1FF;
opacity: .90;
}
.road800 {
width: 800px;
height: 100px;
background: pink;
position: absolute;
top: 0px;
left: 0px;
z-index: 1000;
opacity: .75;
}
.road1200 {
width: 1200px;
height: 100px;
background: orange;
position: absolute;
top: 0px;
left: 0px;
z-index: 500;
}
/*小汽车样式*/
div#car {
width: 135px;
height: 100px;
display: block;
background: black;
position: absolute;
top: 0px;
left: 0px;
z-index: 1500;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
/*border: 1px solid #F0F0F0;*/
}
</style>
</head>
<body>
<div class="controller">
<button id="btn1">移动到800PX</button>
<button id="btn2">移动到1200PX</button>
<button id="btn3">回家</button>
</div>
<div class="road">
<div class="road800"></div>
<div class="road1200"></div>
<div id="car"></div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
document.getElementById("btn1").onclick = function () {
move(document.getElementById("car"), 800);
};
document.getElementById("btn2").onclick = function () {
move(document.getElementById("car"), 1200);
};
document.getElementById("btn3").onclick = function () {
move(document.getElementById("car"), 0);
};
/*移动函数*/
function move(element, target) {
// 先清理定时器
clearInterval(element.timeId);
// 一会要清理定时器(只产生一个定时器)
element.timeId = setInterval(function () {
// 获取对象当前的位置
var current = element.offsetLeft;
// 每次移动多少像素
var step = 10;
// 判断是往正方向走还是往相反方向走
step = current < target ? step : -step;
// 每次移动后的距离
current += step;
// 判断当前移动后的位置是否到达目标位置
if (Math.abs(target - current) > Math.abs(step)) {
element.style.left = current + "px";
} else {
// 清理定时器
clearInterval(element.timeId);
element.style.left = target + "px";
}
}, 20);
}
</script>
</body>
</html>
5.2.3、常用窗口属性
两个属性可用用于确定浏览器窗口的尺寸。
这两个属性都以像素返回尺寸:
- window.innerHeight - 浏览器窗口的内高度(以像素计)
- window.innerWidth - 浏览器窗口的内宽度(以像素计)
浏览器窗口(浏览器视口)不包括工具栏和滚动条。
对于 Internet Explorer 8, 7, 6, 5:
- document.documentElement.clientHeight
- document.documentElement.clientWidth
或
- document.body.clientHeight
- document.body.clientWidth
一个实用的 JavaScript 解决方案(包括所有浏览器):该例显示浏览器窗口的高度和宽度(不包括工具栏和滚动条)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var w = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
console.log(w);
console.log(h);
</script>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
5.2.4、其它窗口方法
-
window.open() :打开新的窗口
语法介绍:
window.open(URL,name,specs,replace);
- 1
参数介绍:
案例演示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <button onclick="openWin()">打开窗口</button> <!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 --> <script> function openWin() { myWindow = window.open('', '', 'width=200,height=100'); myWindow.document.write("<p>这是新建窗口</p>"); } </script> </body> </html>
-
window.close() :关闭当前窗口
语法介绍:
window.close();
- 1
案例演示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <button onclick="openWin()">打开窗口</button> <button onclick="closeWin()">关闭窗口</button> <!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 --> <script> function openWin() { myWindow = window.open('', '', 'width=200,height=100'); myWindow.document.write("<p>这是新建窗口</p>"); } function closeWin() { myWindow.close(); } </script> </body> </html>
-
window.moveTo() :移动当前窗口
语法介绍:
window.moveTo(x,y);
- 1
案例演示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <button onclick="openWin()">打开窗口</button> <button onclick="moveWin()">移动窗口</button> <!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 --> <script> function openWin() { myWindow = window.open('', '', 'width=200,height=100'); myWindow.document.write("<p>这是新建窗口</p>"); } function moveWin() { myWindow.moveTo(300, 300); myWindow.focus(); } </script> </body> </html>
-
window.resizeTo() :调整当前窗口
语法介绍:
window.resizeTo(width,height);
- 1
案例演示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <button onclick="openWin()">打开窗口</button> <button onclick="resizeWin()">调整窗口</button> <!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 --> <script> function openWin() { myWindow = window.open('', '', 'width=200,height=100'); myWindow.document.write("<p>这是新建窗口</p>"); } function resizeWin() { myWindow.resizeTo(300, 300); myWindow.focus(); } </script> </body> </html>
5.3、Navigator对象
Navigator代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器,由于历史原因,Navigator对象中的大部分属性都已经不能帮助我们识别浏览器了,一般我们只会使用userAgent来判断浏览器的信息,userAgent是一个字符串,这个字符串中包含有用来描述浏览器信息的内容,不同的浏览器会有不同的userAgent,如下代码:
var ua = navigator.userAgent;
console.log(ua);
- 1
- 2
谷歌浏览器:
- Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
火狐浏览器:
- Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
IE11浏览器:
- Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; rv:11.0) like Gecko
IE10浏览器:
- Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
IE9浏览器:
- Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
IE8浏览器:
- Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
IE7浏览器:
- Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
于是乎,我们就可以实现对浏览器类型的判断:
var ua = navigator.userAgent;
if (/firefox/i.test(ua)) {
alert("你是火狐浏览器");
} else if (/chrome/i.test(ua)) {
alert("你是谷歌浏览器");
} else if (/msie/i.test(ua)) {
alert("你是IE5-IE10浏览器");
} else if ("ActiveXObject" in window) {
alert("你是IE11浏览器");
}
注意:在IE11中已经将微软和IE相关的标识都已经去除了,所以我们基本已经不能通过UserAgent来识别一个浏览器是否是IE了,如果通过UserAgent不能判断,还可以通过一些浏览器中特有的对象,来判断浏览器的信息,比如:ActiveXObject
5.4、Location对象
Location对象中封装了浏览器的地址栏的信息,如果直接打印location,则可以获取到地址栏的信息(当前页面的完整路径)
5.4.1、常用属性
常用属性:
console.log(location); //输出location对象
console.log(location.href); //输出当前地址的全路径地址
console.log(location.origin); //输出当前地址的来源
console.log(location.protocol); //输出当前地址的协议
console.log(location.hostname); //输出当前地址的主机名
console.log(location.host); //输出当前地址的主机
console.log(location.port); //输出当前地址的端口号
console.log(location.pathname); //输出当前地址的路径部分
console.log(location.search); //输出当前地址的?后边的参数部分
修改地址:
location = "https://www.baidu.com";
- 1
location.href = "https://www.baidu.com";
- 1
5.4.2、常用方法
assign():用来跳转到其它的页面,作用和直接修改location一样
location.assign("https://www.baidu.com");
- 1
reload():用于重新加载当前页面,作用和刷新按钮一样,如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面
location.reload(true);
- 1
replace():可以使用一个新的页面替换当前页面,调用完毕也会跳转页面,它不会生成历史记录,不能使用回退按钮回退
location.replace("https://www.baidu.com");
- 1
5.5、History对象
History对象可以用来操作浏览器向前或向后翻页
5.5.1、常用属性
console.log(history); //输出history对象
console.log(history.length); //可以获取到当成访问的链接数量
- 1
- 2
5.5.2、常用方法
back():可以回退到上一个页面,作用和浏览器的回退按钮一样
history.back();
- 1
forward():可以跳转到下一个页面,作用和浏览器的前进按钮一样
history.forward();
- 1
go():可以用来跳转到指定的页面,它需要一个整数作为参数
- 1:表示向前跳转一个页面,相当于forward()
- 2:表示向前跳转两个页面
- -1:表示向后跳转一个页面,相当于back()
- -2:表示向后跳转两个页面
history.go(-2);
- 1
5.6、Screen对象
Screen 对象包含有关客户端显示屏幕的信息。
注意:没有应用于 screen 对象的公开标准,不过所有浏览器都支持该对象。
5.6.1、Screen对象描述
每个 Window 对象的 screen 属性都引用一个 Screen 对象。Screen 对象中存放着有关显示浏览器屏幕的信息。JavaScript 程序将利用这些信息来优化它们的输出,以达到用户的显示要求。例如,一个程序可以根据显示器的尺寸选择使用大图像还是使用小图像,它还可以根据显示器的颜色深度选择使用 16 位色还是使用 8 位色的图形。另外,JavaScript 程序还能根据有关屏幕尺寸的信息将新的浏览器窗口定位在屏幕中间。
5.6.2、Screen对象属性
属性 | 描述 |
---|---|
availHeight | 返回显示屏幕的高度 (除 Windows 任务栏之外)。 |
availWidth | 返回显示屏幕的宽度 (除 Windows 任务栏之外)。 |
bufferDepth | 设置或返回调色板的比特深度。 |
colorDepth | 返回目标设备或缓冲器上的调色板的比特深度。 |
deviceXDPI | 返回显示屏幕的每英寸水平点数。 |
deviceYDPI | 返回显示屏幕的每英寸垂直点数。 |
fontSmoothingEnabled | 返回用户是否在显示控制面板中启用了字体平滑。 |
height | 返回显示屏幕的高度。 |
logicalXDPI | 返回显示屏幕每英寸的水平方向的常规点数。 |
logicalYDPI | 返回显示屏幕每英寸的垂直方向的常规点数。 |
pixelDepth | 返回显示屏幕的颜色分辨率(比特每像素)。 |
updateInterval | 设置或返回屏幕的刷新率。 |
width | 返回显示器屏幕的宽度。 |
第六章 JavaScript高级语法
6.1、Exception
6.1.1、异常概述
在ES3之前JavaScript代码执行的过程中,一旦出现错误,整个JavaScript代码都会停止执行,这样就显的代码非常的不健壮。
在Java或C#等一些高级语言中,都提供了异常处理机制,可以处理出现的异常,而不会停止整个应用程序。
从ES3开始,JavaScript也提供了类似的异常处理机制,从而让JavaScript代码变的更健壮,即使执行的过程中出现了异常,也可以让程序具有了一部分的异常恢复能力。
当错误发生时,JavaScript 提供了错误信息的内置 error 对象。
error 对象提供两个有用的属性:name
和 message
。
Error 对象属性
属性 | 描述 |
---|---|
name | 设置或返回错误名 |
message | 设置或返回错误消息(一条字符串) |
Error Name Values
error 的 name 属性可返回六个不同的值:
错误名 | 描述 |
---|---|
EvalError | 已在 eval() 函数中发生的错误 |
RangeError | 已发生超出数字范围的错误 |
ReferenceError | 已发生非法引用 |
SyntaxError | 已发生语法错误 |
TypeError | 已发生类型错误 |
URIError | 在 encodeURI() 中已发生的错误 |
6.1.2、异常捕捉
ES3开始引入了 try-catch 语句,是 JavaScript 中处理异常的标准方式。
语法格式:
try {
// 可能发生异常的代码
} catch (error) {
// 发生错误执行的代码
} finally {
// 无论是否出错都会执行的代码
}
在 try…catch 中,try 中一旦出现错误则其它语句不能执行,如果不出现错误则 catch 中的语句不会执行。
Javascript 参考其它编程语言,也提供了一种 finally 语句:不管 try 中的语句有没有错误,在最后都会执行 finally 中的语句。也就是说,try 中语句不发生错误执行完毕后会执行 finally 中的语句,try 中的语句发生错误,则执行 catch中的语句,catch 中的语句执行完毕后也会执行 finally 中的语句。
案例演示:
try {
console.log(a);
console.log("a未定义肯定报错,你看不见我");
} catch (error) {
// 发生错误执行的代码
console.log(error);
} finally {
// 无论是否出错都会执行的代码
console.log("finally 执行了 ...")
}
在JavaScript中,如果添加了 finally 语句,则 catch 语句可以省略。但是如果没有 catch 语句,则一旦发生错误就无法捕获这个错误,所以在执行完 finally 中的语句后,程序就会立即停止了。所以,在实际使用中,最好一直带着 catch 语句。如果你写了 catch 语句,则finally 语句也是可以省略的。
6.1.3、异常演示
EvalError 指示 eval() 函数中的错误。更新版本的 JavaScript 不会抛出任何 EvalError,请使用 SyntaxError 代替。
案例演示:
try {
eval("alert('Hello)"); // 缺少 ' 会产生错误
} catch (error) {
console.log(error)
}
RangeError 会在您使用了合法值的范围之外的数字时抛出。
案例演示:您不能将数字的有效位数设置为 500。
var num = 1;
try {
num.toPrecision(500); // 数无法拥有 500 个有效数
} catch (error) {
console.log(error)
}
假如您使用(引用)了尚未声明的变量,则 ReferenceError 会被抛出:
案例演示:
var x;
try {
x = y + 1; // y 无法被引用(使用)
} catch (error) {
console.log(error)
}
假如您计算带语法错误的代码,会 SyntaxError 被抛出:
案例演示:
try {
eval("alert('Hello)"); // 缺少 ' 会产生错误
} catch (error) {
console.log(error)
}
假如您使用的值不在期望值的范围之内,则 TypeError 被抛出:
案例演示:
var num = 1;
try {
num.toUpperCase(); // 您无法将数字转换为大写
} catch (error) {
console.log(error)
}
假如您在 URI 函数中使用非法字符,则 URIError 被抛出:
案例演示:
try {
decodeURI("%%%"); // 您无法对这些百分号进行 URI 编码
} catch (error) {
console.log(error)
}
6.1.4、异常抛出
在大部分的代码执行过程中,都是出现错误的时候,由浏览器(javascript引擎)抛出异常,然后程序或者停止执行或被try…catch 捕获。
然而有时候我们在检测到一些不合理的情况发生的时候也可以主动抛出错误,请使用 throw 关键字抛出来主动抛出异常。
注意事项:
- thow后面就是我们要抛出的异常对象,在以前的时候都是出现错误的时候浏览器抛出异常对象,只是现在是我们自己主动抛出的异常对象。
- 只要有异常对象抛出,不管是浏览器抛出的,还是代码主动抛出,都会让程序停止执行。如果想让程序继续执行,则有也可以用try…catch来捕获。
- 每一个错误类型都可以传入一个参数,表示实际的错误信息。
- 我们可以在适当的时候抛出任何我们想抛出的异常类型。
throw new SyntaxError("语法错误...");
/*该函数接收一个数字,返回它的平方。*/
function foo(num) {
if (typeof num == "number") {
return num * num;
} else {
throw new TypeError("您输入的是一个非法数字!")
}
}
console.log(foo(4));
console.log(foo("abc"));
我们不仅仅可以抛出js内置的错误类型的对象,也可以自定义错误类型,然后抛出自定义错误类型的对象。
如果要自定义错误类型,只需要继承任何一个自定义错误类型都可以,一般直接继承Error即可。
/*自定义错误*/
function MyError(message) {
this.message = "注意:这是自定义的错误"
this.name = "自定义错误";
}
MyError.prototype = new Error();
try {
throw new MyError("注意:这是自定义错误类型")
} catch (error) {
console.log(error.message)
}
6.2、JSON
6.2.1、JSON概述
JSON:JavaScript Object Notation(JavaScript 对象标记法),它是一种存储和交换数据的语法。
当数据在浏览器与服务器之间进行交换时,这些数据只能是文本,JSON 属于文本并且我们能够把任何 JavaScript 对象转换为 JSON,然后将 JSON 发送到服务器。我们也能把从服务器接收到的任何 JSON 转换为 JavaScript 对象。以这样的方式,我们能够把数据作为 JavaScript 对象来处理,无需复杂的解析和转译。
6.2.2、JSON语法
在json中,每一个数据项,都是由一个键值对(或者说是名值对)组成的,但是键必须是字符串,且由双引号包围,而值必须是以下数据类型之一:
- 字符串(在 JSON 中,字符串值必须由双引号编写)
- 数字
- 对象(JSON 对象)
- 数组
- 布尔
- null
JSON 的值不可以是以下数据类型之一:
- 函数
- 日期
- undefined
因为 JSON 语法由 JavaScript 对象标记法衍生而来,所以很少需要其它额外的软件来处理 JavaScript 中的 JSON。
通过 JavaScript,您能够创建对象并向其分配数据,就像这样:
var person = {"name": "zhangsan", "age": 62, "city": "BeiJing"};
console.log(person);
- 1
- 2
6.2.3、JSON数据类型
JSON 中的字符串必须用双引号包围。
{"name": "John"}
- 1
JSON 中的数字必须是整数或浮点数。
{"age": 30}
- 1
JSON 中的值可以是对象,JSON 中作为值的对象必须遵守与 JSON 对象相同的规则。
{
"employee": {"name": "Bill Gates", "age": 62, "city": "Seattle"}
}
JSON 中的值可以是数组。
{
"employees": ["Bill", "Steve", "David"]
}
JSON 中的值可以是 true/false。
{"sale": true}
- 1
JSON 中的值可以是 null。
{"middlename": null}
- 1
6.2.4、JSON字符串转JS对象
JSON.parse():可以将以JSON字符串转换为JS对象,它需要一个JSON字符串作为参数,会将该字符串转换为JS对象并返回
案例演示:
var jsonStr = '{"name":"孙悟空","age":18,"gender":"男"}';
var obj = JSON.parse(jsonStr);
console.log(obj);
注意 :JSON这个对象在IE7及以下的浏览器中不支持,所以在这些浏览器中调用时会报错
6.2.5、JS对象转JSON字符串
JSON.stringify():可以将一个JS对象转换为JSON字符串,需要一个js对象作为参数,会返回一个JSON字符串
案例演示:
var obj = {name: "猪八戒", age: 28, gender: "男"};
var jsonStr = JSON.stringify(obj);
console.log(jsonStr);
注意 :JSON这个对象在IE7及以下的浏览器中不支持,所以在这些浏览器中调用时会报错
6.3、AJAX
6.3.1、AJAX概述
传统的web交互是用户触发一个http请求服务器,然后服务器收到之后,在做出响应到用户,并且返回一个新的页面,每当服务器处理客户端提交的请求时,客户都只能空闲等待,并且哪怕只是一次很小的交互、只需从服务器端得到很简单的一个数据,都要返回一个完整的HTML页,而用户每次都要浪费时间和带宽去重新读取整个页面。这个做法浪费了许多带宽,由于每次应用的交互都需要向服务器发送请求,应用的响应时间就依赖于服务器的响应时间,这导致了用户界面的响应比本地应用慢得多。
AJAX 的出现,刚好解决了传统方法的缺陷,AJAX 是一种用于创建快速动态网页的技术,通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新,这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
6.3.2、AJAX的XMLHttpRequest对象
AJAX 的核心是 XMLHttpRequest 对象。 所有现代浏览器都支持 XMLHttpRequest 对象。
XMLHttpRequest 对象用于幕后同服务器交换数据,这意味着可以更新网页的部分,而不需要重新加载整个页面。
所有现代浏览器(Chrom、IE7+、Firefox、Safari 以及 Opera)都有内建的 XMLHttpRequest 对象。
创建 XMLHttpRequest 的语法是:
variable = new XMLHttpRequest();
- 1
老版本的 Internet Explorer(IE5 和 IE6)使用 ActiveX 对象:
variable = new ActiveXObject("Microsoft.XMLHTTP");
- 1
为了应对所有浏览器,包括 IE5 和 IE6,请检查浏览器是否支持 XMLHttpRequest 对象。如果支持,创建 XMLHttpRequest 对象,如果不支持,则创建 ActiveX 对象:
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
但是需要注意的是,出于安全原因,现代浏览器不允许跨域访问,这意味着尝试加载的网页和 XML 文件都必须位于相同服务器上。
6.3.3、AJAX的XMLHttpRequest对象方法
方法 | 描述 |
---|---|
new XMLHttpRequest() | 创建新的 XMLHttpRequest 对象 |
abort() | 取消当前请求 |
getAllResponseHeaders() | 返回头部信息 |
getResponseHeader() | 返回特定的头部信息 |
open(method, url, async, user, psw) | 规定请求method:请求类型 GET 或 POST url:文件位置 async:true(异步)或 false(同步) user:可选的用户名称 psw:可选的密码 |
send() | 将请求发送到服务器,用于 GET 请求 |
send(string) | 将请求发送到服务器,用于 POST 请求 |
setRequestHeader() | 向要发送的报头添加标签/值对 |
6.3.4、AJAX的XMLHttpRequest对象属性
属性 | 描述 |
---|---|
onreadystatechange | 定义当 readyState 属性发生变化时被调用的函数 |
readyState | 保存 XMLHttpRequest 的状态。 0:请求未初始化 1:服务器连接已建立 2:请求已收到 3:正在处理请求 4:请求已完成且响应已就绪 |
responseText | 以字符串返回响应数据 |
responseXML | 以 XML 数据返回响应数据 |
status | 返回请求的状态号 200: "OK" 403: "Forbidden" 404: "Not Found" 如需完整列表请访问 Http 消息参考手册 |
statusText | 返回状态文本(比如 “OK” 或 “Not Found”) |
6.3.5、AJAX的GET请求
工程结构:
users.json
[
{"name":"孙悟空","age":18,"gender":"男"},
{"name":"猪八戒","age":19,"gender":"男"},
{"name":"唐僧","age":20,"gender":"男"},
{"name":"沙和尚","age":21,"gender":"男"}
]
index.html
//步骤一:创建异步对象
var ajax = new XMLHttpRequest();
//步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url
ajax.open("get", "users.json");
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
//步骤五:如果能够进到这个判断,说明数据完美的回来了,并且请求的页面是存在的
console.log(ajax.responseText);//输入响应的内容
}
};
控制台:
6.3.6、AJAX的POST请求
工程结构:
users.json
[
{"name":"孙悟空","age":18,"gender":"男"},
{"name":"猪八戒","age":19,"gender":"男"},
{"name":"唐僧","age":20,"gender":"男"},
{"name":"沙和尚","age":21,"gender":"男"}
]
index.html
//步骤一:创建异步对象
var ajax = new XMLHttpRequest();
//步骤二:设置请求的类型及url,注意:post请求一定要添加请求头才行不然会报错
ajax.open("post", "users.json");
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
//步骤五:如果能够进到这个判断,说明数据完美的回来了,并且请求的页面是存在的
if (ajax.readyState == 4 && ajax.status == 200) {
console.log(ajax.responseText);//输入响应的内容
}
};
控制台:
6.3.7、AJAX的请求整合
工程结构:
users.json
[
{"name":"孙悟空","age":18,"gender":"男"},
{"name":"猪八戒","age":19,"gender":"男"},
{"name":"唐僧","age":20,"gender":"男"},
{"name":"沙和尚","age":21,"gender":"男"}
]
index.html
var Ajax = {
get: function (url, fn) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
fn.call(this, xhr.responseText);
}
};
xhr.send();
},
post: function (url, data, fn) {
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
fn.call(this, xhr.responseText);
}
};
xhr.send(data);
}
};
// 演示GET请求
Ajax.get("users.json", function (response) {
console.log(response);
});
// 演示POST请求
Ajax.post("users.json", "", function (response) {
console.log(response);
});
控制台:
6.4、Cookie
6.4.1、Cookie概述
Cookie 是一些数据,存储于你电脑上的文本文件中,当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息,Cookie 的作用就是用于解决 “如何记录客户端的用户信息”:
- 当用户访问 web 页面时,它的名字可以记录在 cookie 中。
- 在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录。
Cookie 以名/值对形式存储,如下所示:
username=zhangsan
- 1
当浏览器从服务器上请求 web 页面时, 属于该页面的 cookie 会被添加到该请求中,服务端通过这种方式来获取用户的信息。
JavaScript 可以使用 document.cookie 属性来创建 、读取、及删除 Cookie。
6.4.2、Cookie创建
JavaScript 中,创建 cookie 如下所示:
document.cookie = "username=zhangsan";
- 1
您还可以为 cookie 添加一个过期时间(以 UTC 或 GMT 时间)。默认情况下,cookie 在浏览器关闭时删除。
document.cookie = "username=zhangsan; expires=Thu, 18 Dec 2043 12:00:00 GMT";
- 1
您可以使用 path 参数告诉浏览器 cookie 的路径。默认情况下,cookie 属于当前页面。
document.cookie = "username=zhangsan; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
- 1
6.4.3、Cookie读取
JavaScript 中,读取 cookie 如下所示:
document.cookie 将以字符串的方式返回所有的 cookie,类型格式: cookie1=value; cookie2=value; cookie3=value;
document.cookie = "username=zhangsan";
var cookies = document.cookie;
console.log(cookies);
- 1
- 2
- 3
6.4.4、Cookie修改
JavaScript 中,修改 cookie 如下所示:
使用 document.cookie 将旧的 cookie 将被覆盖就是修改。
document.cookie = "username=zhangsan";
document.cookie = "username=lisi";
var cookies = document.cookie;
console.log(cookies);
6.4.5、Cookie删除
JavaScript 中,删除 cookie 如下所示:
删除 cookie 非常简单,您只需要设置 expires 参数为以前的时间即可,如下所示,设置为 Thu, 01 Jan 1970 00:00:00 GMT:
document.cookie = "username=zhangsan";
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
var cookies = document.cookie;
console.log(cookies);
6.4.6、Cookie值设置函数
/**
* Cookie值设置函数
* @param cname cookie名称
* @param cvalue cookie值
* @param exdays 过期天数
*/
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
6.4.7、Cookie值获取函数
/**
* Cookie值获取函数
* @param cname cookie名称
* @returns {string}
*/
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
}
return "";
}
6.5、WebStorage
6.5.1、WebStorage概述
WebStorage是HTML5中本地存储的解决方案之一,在HTML5的WebStorage概念引入之前除去IE User Data、Flash Cookie、Google Gears等看名字就不靠谱的解决方案,浏览器兼容的本地存储方案只有使用Cookie。有同学可能会问,既然有了Cookie本地存储,为什么还要引入WebStorage的概念?那就要说一说Cookie的缺陷了:
- 数据大小:作为存储容器,Cookie的大小限制在4KB左右这是非常坑爹的,尤其对于现在复杂的业务逻辑需求,4KB的容量除了存储一些配置字段还简单单值信息,对于绝大部分开发者来说真的不知指望什么了。
- 安全性问题:由于在HTTP请求中的Cookie是明文传递的(HTTPS不是),带来的安全性问题还是很大的。
- 网络负担:我们知道Cookie会被附加在每个HTTP请求中,在HttpRequest和HttpResponse的header中都是要被传输的,所以无形中增加了一些不必要的流量损失。
虽然WebStorage是HTML5新增的本地存储解决方案之一,但并不是为了取代Cookie而制定的标准,Cookie作为HTTP协议的一部分用来处理客户端和服务器通信是不可或缺的,session正是依赖于实现的客户端状态保持。WebStorage的意图在于解决本来不应该Cookie做,却不得不用Cookie的本地存储的应用场景。
6.5.2、WebStorage分类
Web Storage又分为两种: sessionStorage 和localStorage ,即这两个是Storage的一个实例。从字面意思就可以很清楚的看出来,sessionStorage将数据保存在session中,浏览器关闭也就没了;而localStorage则一直将数据保存在客户端本地; 不管是sessionStorage,还是localStorage,使用的API都相同。
localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理,低版本IE可以使用json2.js
6.5.3、localStorage方法
对象介绍:
localStorage在本地永久性存储数据,除非显式将其删除或清空。
常见方法:
- 保存单个数据:localStorage.setItem(key,value);
- 读取单个数据:localStorage.getItem(key);
- 删除单个数据:localStorage.removeItem(key);
- 删除所有数据:localStorage.clear();
- 获取某个索引的key:localStorage.key(index);
案例演示:
// 保存数据
localStorage.setItem("username", "zhangsan");
// 读取单个数据
console.log(localStorage.getItem("username"));
console.log("===============");
// 删除单个数据
localStorage.removeItem("username");
console.log(localStorage.getItem("username"));
console.log("===============");
// 保存两个数据
localStorage.setItem("age", 18);
localStorage.setItem("sex", "男");
console.log("age=" + localStorage.getItem("age"));
console.log("sex=" + localStorage.getItem("sex"));
console.log("===============");
// 使用for-in循环来迭代localStorage中的键值对、属性和方法:
for (var key in localStorage) {
console.log(key + "=" + localStorage[key]);
}
console.log("===============");
// 使用for循环来迭代localStorage中的键值对:
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var value = localStorage.getItem(key);
console.log(key + "=" + value);
}
console.log("===============");
// 删除所有数据
localStorage.clear();
控制台:
6.5.4、sessionStorage方法
对象介绍:
sessionStorage对象存储特定于某个对话的数据,也就是它的生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。存储在sessionStorage中的数据可以跨越页面刷新而存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可以使用(注意:Firefox和Weblit都支持,IE则不行)。
因为sessionStorage对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的。存储在sessionStorage中的数据只能由最初给对象存储数据的页面访问到,所以对多页面应用有限制。
不同浏览器写入数据方法略有不同。Firefox和Webkit实现了同步写入,所以添加到存储空间中的数据是立刻被提交的。而IE的实现则是异步写入数据,所以在设置数据和将数据实际写入磁盘之间可能有一些延迟。
常见方法:
- 保存单个数据:sessionStorage.setItem(key,value);
- 读取单个数据:sessionStorage.getItem(key);
- 删除单个数据:sessionStorage.removeItem(key);
- 删除所有数据:sessionStorage.clear();
- 获取某个索引的key:sessionStorage.key(index);
案例演示:
// 保存数据
sessionStorage.setItem("username", "zhangsan");
// 读取单个数据
console.log(sessionStorage.getItem("username"));
console.log("===============");
// 删除单个数据
sessionStorage.removeItem("username");
console.log(sessionStorage.getItem("username"));
console.log("===============");
// 保存两个数据
sessionStorage.setItem("age", 18);
sessionStorage.setItem("sex", "男");
console.log("age=" + sessionStorage.getItem("age"));
console.log("sex=" + sessionStorage.getItem("sex"));
console.log("===============");
// 使用for-in循环来迭代sessionStorage中的键值对、属性和方法:
for (var key in sessionStorage) {
console.log(key + "=" + sessionStorage[key]);
}
console.log("===============");
// 使用for循环来迭代sessionStorage中的键值对:
for (var i = 0; i < sessionStorage.length; i++) {
var key = sessionStorage.key(i);
var value = sessionStorage.getItem(key);
console.log(key + "=" + value);
}
console.log("===============");
// 删除所有数据
sessionStorage.clear();
控制台:
6.6、Closure
6.6.1、闭包引入
需求信息:点击某个按钮,提示"点击的是第n个按钮"
第一种解决方法:将btn所对应的下标保存在btn上
var btns = document.getElementsByTagName('button');
//将btn所对应的下标保存在btn上
for (var i = 0, length = btns.length; i < length; i++) {
var btn = btns[i];
btn.index = i;
btn.onclick = function () {
alert('第' + (this.index + 1) + '个');
}
}
第二种解决方法:利用闭包延长局部变量的生命周期
var btns = document.getElementsByTagName('button');
// 利用闭包延长局部变量的生命周期
for (var i = 0, length = btns.length; i < length; i++) {
(function (j) {
var btn = btns[j];
btn.onclick = function () {
alert('第' + (j + 1) + '个');
}
})(i);
}
6.6.2、闭包概念
- 如何产生闭包?
- 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
- 什么才是闭包?
- 理解一:闭包是嵌套的内部函数(绝大部分人认为)
- 理解二:包含被引用变量(函数)的对象(极少部分人认为)
- 闭包的作用?
- 它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中
6.6.3、闭包演示
function fun1() {
var a = 2;
function subFun() {
a++;
console.log(a);
}
return subFun;
}
var f1 = fun1();
f1();
f1();
console.log("===============");
function fun2() {
var a = 2;
function subFun() {
a--;
console.log(a);
}
return subFun;
}
var f2 = fun2();
f2();
f2();
console.log("===============");
6.6.4、闭包生命周期
生命周期:
- 产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
- 死亡:在嵌套的内部函数成为垃圾对象时就死亡了
演示说明:
function fn1() {
//此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
var a = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn1();
f(); // 3
f(); // 4
f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象)
6.6.5、闭包应用
闭包应用: 定义JS模块
- 具有特定功能的js文件
- 将所有的数据和功能都封装在一个函数内部(私有的)
- 只向外暴露一个包含n个方法的对象或函数
- 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
案例演示:
第一种格式:myModule.js
function myModule() {
//私有数据
var msg = 'Hello, World';
//操作数据的函数
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase());
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase());
}
//向外暴露对象(给外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}
第一种使用:index.html
var module = myModule();
module.doSomething();
module.doOtherthing();
- 1
- 2
- 3
第二种格式:myModule.js
(function (window) {
//私有数据
var msg = 'Hello, World';
//操作数据的函数
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase());
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase());
}
//向外暴露对象(给外部使用的方法)
window.myModule = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})(window);
第二种使用:index.html
myModule.doSomething();
myModule.doOtherthing();
- 1
- 2
第七章 JavaScript新特性
7.1、ECMAScript6新特性
7.1.1、let 关键字
let 关键字用来声明变量,使用 let 声明的变量有几个特点:
- 不允许重复声明
- 块儿级作用域
- 不存在变量提升
- 不影响作用域链
注意:以后声明变量使用 let 就对了
案例演示:创建四个div,单机每一个div让其变色
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
.item {
width: 100px;
height: 50px;
border: solid 1px rgb(42, 156, 156);
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
// 获取div元素对象
let items = document.getElementsByClassName('item');
// 遍历并绑定事件
for (let i = 0; i < items.length; i++) {
items[i].onclick = function () {
// 以前的做法:this.style.background = "pink";
items[i].style.background = "pink";
};
}
</script>
</body>
</html>
7.1.2、const 关键字
const 关键字用来声明常量,const 声明有以下特点:
- 不允许重复声明
- 块儿级作用域
- 声明必须赋初始值
- 值不允许修改
- 标识符一般为大写
注意:声明对象类型使用 const,非对象类型声明选择 let
// 声明常量
const MAX = 100;
console.log(MAX);
// 对于数组和对象的元素修改, 不算做对常量的修改, 不会报错
const TEAM1 = [1, 2, 3, 4];
const TEAM2 = [1, 2, 3, 4];
// 但是不能修改地址指向
// TEAM2 = TEAM1;
7.1.3、变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式
数组的解构赋值:
//数组的解构赋值
const arr = ["张学友", "刘德华", "黎明", "郭富城"];
let [zhang, liu, li, guo] = arr;
console.log(zhang);
console.log(liu);
console.log(li);
console.log(guo);
简单对象的解构赋值:
//对象的解构赋值
const lin = {
name: "林志颖",
tags: ["车手", "歌手", "小旋风", "演员"]
};
let {name, tags} = lin;
console.log(name);
console.log(tags);
复杂对象的解构赋值:
//复杂对象的解构赋值
let wangfei = {
name: "王菲",
age: 18,
songs: ["红豆", "流年", "暧昧"],
history: [
{name: "窦唯"},
{name: "李亚鹏"},
{name: "谢霆锋"}
]
};
let {name, age, songs: [one, two, three], history: [first, second, third]} = wangfei;
console.log(name);
console.log(age);
console.log(one);
console.log(two);
console.log(three);
console.log(first);
console.log(second);
console.log(third);
7.1.4、模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
- 字符串中可以出现换行符
- 可以使用 ${xxx} 形式输出变量
注意:当遇到字符串与变量拼接的情况使用模板字符串
字符串中可以出现换行符:
//定义字符串
let str = `<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>`;
console.log(str);
变量拼接:
//变量拼接
let name = '小可爱';
let result = `欢迎${name}访问我的文章`;
console.log(result);
7.1.5、简化对象写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,这样的书写更加简洁。
注意:对象简写形式简化了代码,所以以后用简写就对了
let name = "张三";
let age = 18;
let speak = function () {
console.log(this.name);
};
//属性和方法简写
let person = {
name,
age,
speak
};
console.log(person.name);
console.log(person.age);
person.speak();
7.1.6、箭头函数
ES6 允许使用「箭头」(=>)定义函数,通用写法如下:
let fn = (arg1, arg2, arg3) => {
return arg1 + arg2 + arg3;
}
箭头函数的注意点:
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
- 箭头函数 this 指向声明时所在作用域下 this 的值,箭头函数不会更改 this 指向,用来指定回调函数会非常合适
- 箭头函数不能作为构造函数实例化
- 不能使用 arguments 实参
省略小括号的情况:
let fn = num => {
return num * 10;
};
省略花括号的情况:
let fn = score => score * 20;
- 1
this 指向声明时所在作用域中 this 的值:
// this 指向声明时所在作用域中 this 的值
let fn = () => {
console.log(this);
}
fn();
let school = {
name: "张三",
getName() {
let subFun = () => {
console.log(this);
}
subFun();
}
};
school.getName();
7.1.7、rest 参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments 参数。
注意:rest 参数非常适合不定个数参数函数的场景
// 作用与 arguments 类似
function add(...args) {
console.log(args);
}
add(1, 2, 3, 4, 5);
// rest 参数必须是最后一个形参
function minus(a, b, ...args) {
console.log(a, b, args);
}
minus(100, 1, 2, 3, 4, 5, 19);
7.1.8、spread 扩展运算符
扩展运算符(spread)也是三个点(…),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
展开数组:
// 展开数组
let tfboys = ["德玛西亚之力", "德玛西亚之翼", "德玛西亚皇子"];
function fn() {
console.log(arguments);
}
fn(...tfboys);
展开对象:
// 展开对象
let skillOne = {
q: "致命打击"
};
let skillTwo = {
w: "勇气"
};
let skillThree = {
e: "审判"
};
let skillFour = {
r: "德玛西亚正义"
};
let gailun = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
console.log(gailun);
7.1.9、Symbol类型
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值,它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型,Symbol 特点如下:
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其它数据进行运算
- Symbol 定义的对象属性不能使用 for…in 循环遍 历 ,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
//创建 Symbol
let s1 = Symbol();
console.log(s1);
console.log(typeof s1);
//添加标识的 Symbol
let s2 = Symbol("张三");
let s2_2 = Symbol("张三");
console.log(s2);
console.log(s2_2);
console.log(s2 === s2_2);
//使用 Symbol for 定义
let s3 = Symbol.for("张三");
let s3_2 = Symbol.for("张三");
console.log(s3);
console.log(s3_2);
console.log(s3 === s3_2);
//在方法中使用 Symbol
let game = {
name: "狼人杀",
[Symbol('say')]: function () {
console.log("我可以发言")
},
[Symbol('zibao')]: function () {
console.log('我可以自爆');
}
};
console.log(game);
注意:遇到唯一性的场景时要想到 Symbol
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
内置值 | 描述 |
---|---|
Symbol.hasInstance | 当其它对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat()时, 是否可以展开 |
Symbol.species | 创建衍生对象时,会使用该属性 |
Symbol.match | 当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值 |
Symbol.replace | 当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值 |
Symbol.search | 当该对象被 str.search (myObject)方法调用时,会返回该方法的返回值 |
Symbol.split | 当该对象被 str.split(myObject)方法调用时,会返回该方法的返回值 |
Symbol.iterator | 当对象进行 for…of 循环时,会调用 Symbol.iterator 方法, 返回该对象的默认遍历器 |
Symbol.toPrimitive | 当对象被转为原始类型的值时,会调用这个方法,返 回该对象对应的原始类型值 |
Symbol. toStringTag | 当对象上面调用 toString 方法时,返回该方法的返 回值 |
Symbol. unscopables | 当对象指定了使用 with 关键字时,哪些属性会被 with 环境排除 |
Symbol.hasInstance演示:
class Person {
static [Symbol.hasInstance](param) {
console.log("我被用来检测类型了");
}
}
let o = {};
console.log(o instanceof Person);
Symbol.isConcatSpreadable演示:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = true;
console.log(arr1.concat(arr2));
const arr3 = [1, 2, 3];
const arr4 = [4, 5, 6];
arr4[Symbol.isConcatSpreadable] = false;
console.log(arr3.concat(arr4));
7.1.10、迭代器
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费,原生具备 iterator 接口的数据:
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
注意:需要自定义遍历数据的时候,要想到迭代器
工作原理:
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done 属性的对象
案例演示:遍历数组
//声明一个数组
const xiyou = ["唐僧", "孙悟空", "猪八戒", "沙僧"];
//使用 for...of 遍历数组
for (let v of xiyou) {
console.log(v);
}
console.log("===============");
//获取迭代器对象
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
案例演示:自定义遍历数据
//声明一个对象
const banji = {
name: "五班",
stus: [
"张三",
"李四",
"王五",
"小六"
],
[Symbol.iterator]() {
//索引变量
let index = 0;
let _this = this;
return {
next: function () {
if (index < _this.stus.length) {
const result = {value: _this.stus[index], done: false};
//下标自增
index++;
//返回结果
return result;
} else {
return {value: undefined, done: true};
}
}
};
}
}
//遍历这个对象
for (let v of banji) {
console.log(v);
}
7.1.11、生成器
生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
代码说明:
- * 的位置没有限制
- 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
- yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
- next 方法可以传递实参,作为 yield 语句的返回值
function * gen() {
/*代码1开始执行*/
console.log("代码1执行了");
yield "一只没有耳朵";
/*代码2开始执行*/
console.log("代码2执行了");
yield "一只没有尾巴";
/*代码3开始执行*/
console.log("代码3执行了");
return "真奇怪";
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log("===============");
//遍历
for (let v of gen()) {
console.log(v);
}
function * gen(arg) {
console.log(arg);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three);
}
//执行获取迭代器对象
let iterator = gen('AAA');
console.log(iterator.next());
//next方法可以传入实参
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));
案例演示:1s后控制台输出 111,2s后输出 222,3s后输出 333
function one() {
setTimeout(() => {
console.log(111);
iterator.next();
}, 1000)
}
function two() {
setTimeout(() => {
console.log(222);
iterator.next();
}, 2000)
}
function three() {
setTimeout(() => {
console.log(333);
iterator.next();
}, 3000)
}
function * gen() {
yield one();
yield two();
yield three();
}
//调用生成器函数
let iterator = gen();
iterator.next();
案例演示:模拟获取 ,用户数据 ,订单数据 ,商品数据
function getUsers() {
setTimeout(() => {
let data = "用户数据";
iterator.next(data);
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = "订单数据";
iterator.next(data);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = "商品数据";
iterator.next(data);
}, 1000);
}
function * gen() {
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
//调用生成器函数
let iterator = gen();
iterator.next();
7.1.12、Promise
Promise 是 ES6 引入的异步编程的新解决方案,语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
//实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
setTimeout(function () {
// 成功调用resolve()处理
let data = "数据读取成功";
resolve(data);
// 失败调用reject()处理
let err = "数据读取失败";
reject(err);
}, 1000);
});
//调用 promise 对象的 then 方法
p.then(function (value) {
console.log(value);
}, function (reason) {
console.error(reason);
});
案例演示:
// 接口地址: https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
//3. 发送
xhr.send();
//4. 绑定事件, 处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
//判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status < 300) {
//表示成功
resolve(xhr.response);
} else {
//如果失败
reject(xhr.status);
}
}
}
});
//指定回调
p.then(function (value) {
console.log(value);
}, function (reason) {
console.error(reason);
});
调用 then 方法,then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定,如果回调函数中返回的结果是 非 promise 类型的属性,状态为成功,返回值为对象的成功的值
//创建 promise 对象
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("用户数据");
}, 1000)
});
//链式调用+箭头函数
p.then(value => {
console.log(value);
return value;
}).then(value => {
console.log(value);
});
如果只想处理错误状态,我们可以使用 catch 方法
const p = new Promise((resolve, reject) => {
setTimeout(() => {
//设置 p 对象的状态为失败, 并设置失败的值
reject("出错啦!");
}, 1000);
});
p.catch(function (reason) {
console.error(reason);
});
7.1.13、Set
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
- size:返回集合的元素个数
- add():增加一个新元素,返回当前集合
- delete():删除元素,返回 boolean 值
- has():检测集合中是否包含某个元素,返回 boolean 值
- clear():清空集合,返回 undefined
//创建一个空集合
let s = new Set();
//创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);
//集合属性与方法
//返回集合的元素个数
console.log(s1.size);
//添加新元素
console.log(s1.add(4));
//删除元素
console.log(s1.delete(1));
//检测是否存在某个值
console.log(s1.has(2));
//清空集合
console.log(s1.clear());
7.1.14、Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属性和方法:
- size:返回 Map 的元素个数
- set():增加一个新元素,返回当前 Map
- get():返回键名对象的键值
- has():检测 Map 中是否包含某个元素,返回 boolean 值
- clear():清空集合,返回 undefined
//创建一个空 map
let m = new Map();
//创建一个非空 map
let m2 = new Map([
["name", "张三"],
["gender", "女"]
]);
//属性和方法
//获取映射元素的个数
console.log(m2.size);
//添加映射值
console.log(m2.set("age", 6));
//获取映射值
console.log(m2.get("age"));
//检测是否有该映射
console.log(m2.has("age"));
//清除
console.log(m2.clear());
7.1.15、class 类
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是 一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,它的一些如下:
- class:声明类
- constructor:定义构造函数初始化
- extends:继承父类
- super:调用父级构造方法
- static:定义静态方法和属性
//父类
class Phone {
//构造方法
constructor(brand, color, price) {
this.brand = brand;
this.color = color;
this.price = price;
}
//对象方法
call() {
console.log("我可以打电话!!!")
}
}
//子类
class SmartPhone extends Phone {
constructor(brand, color, price, screen, pixel) {
super(brand, color, price);
this.screen = screen;
this.pixel = pixel;
}
//子类方法
photo() {
console.log("我可以拍照!!");
}
playGame() {
console.log("我可以玩游戏!!");
}
//方法重写
call() {
console.log("我可以进行视频通话!!");
}
//静态方法
static run() {
console.log("我可以运行程序")
}
static connect() {
console.log("我可以建立连接")
}
}
//实例化对象
const Nokia = new Phone("诺基亚", "灰色", 230);
const iPhone6s = new SmartPhone("苹果", "白色", 6088, "4.7inch", "500w");
//调用子类方法
iPhone6s.playGame();
//调用重写方法
iPhone6s.call();
//调用静态方法
SmartPhone.run();
7.1.16、数值扩展
ES6 新增了二进制和八进制的表示方法
let b = 0b1010//二进制
let o = 0o777;//八进制
let d = 100;//十进制
let x = 0xff;//十六进制
console.log(b);
console.log(o);
console.log(d);
console.log(x);
Number.EPSILON:它是 JavaScript 表示的最小精度,EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON) {
return true;
} else {
return false;
}
}
console.log(0.1 + 0.2 === 0.3);
console.log(equal(0.1 + 0.2, 0.3));
Number.isFinite:检测一个数值是否为有限数
console.log(Number.isFinite(100));
console.log(Number.isFinite(100 / 0));
console.log(Number.isFinite(Infinity));
console.log(Number.isFinite(-Infinity));
Number.isNaN:检测一个数值是否为 NaN
console.log(Number.isNaN(123));
- 1
Number.parseInt:将一个字符串转换为整数
console.log(Number.parseInt("123abc"));
- 1
Number.parseFloat:将一个字符串转换为浮点数
console.log(Number.parseFloat("3.1415926神奇"));
- 1
Number.isInteger:判断一个数是否为整数
console.log(Number.isInteger(5));
console.log(Number.isInteger(2.5));
- 1
- 2
Math.trunc:将数字的小数部分抹掉
console.log(Math.trunc(3.5));
- 1
Math.sign:判断一个数到底为正数、负数、还是零
console.log(Math.sign(100));
console.log(Math.sign(0));
console.log(Math.sign(-20000));
7.1.17、对象扩展
ES6 新增了一些 Object 对象的方法,例如:
- Object.is:比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
- Object.assign:对象的合并,将源对象的所有可枚举属性,复制到目标对象
- __proto__、setPrototypeOf、 setPrototypeOf可以直接设置对象的原型
Object.is:判断两个值是否完全相等
console.log(Object.is(120, 120));// ===
console.log(Object.is(NaN, NaN));// ===
console.log(NaN === NaN);// ===
Object.assign:对象的合并,后边的对象会把前边对象的相同属性和方法覆盖,没有的属性和方法会合并
const config1 = {
host: "localhost",
port: 3306,
name: "zhangsan",
pass: "root",
test1: "test1"
};
const config2 = {
host: "127.0.0.1",
port: 3309,
name: "lisi",
pass: "root",
test2: "test2"
}
console.log(Object.assign(config1, config2));
- Object.setPrototypeOf:设置原型对象
- Object.getPrototypeof:获取原型对象
const school = {
name: "MySchool"
};
const cities = {
xiaoqu: ["北京", "上海", "深圳"]
};
Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
console.log(school);
7.1.18、模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
- 防止命名冲突
- 代码复用
- 高维护性
CommonJS => NodeJS、Browserify
AMD => requireJS
CMD => seaJS
模块功能主要由两个命令构成:export 和 import。
- export 命令用于规定模块的对外接口
- import 命令用于输入其它模块提供的功能
m1.js
//方式一:分别暴露
export let school = "华北理工大学";
export function study() {
console.log("我们要学习!");
}
m2.js
//方式二:统一暴露
let school = "华北理工大学";
function findJob() {
console.log("我们要找工作!");
}
export {school, findJob};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
m3.js
//方式三:默认暴露
export default {
school: "华北理工大学",
change: function () {
console.log("我们要改变自己!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script type="module">
// 引入 m1.js 模块内容
import * as m1 from "./m1.js";
// 引入 m2.js 模块内容
import * as m2 from "./m2.js";
// 引入 m3.js 模块内容
import * as m3 from "./m3.js";
m1.study();
m2.findJob();
m3.default.change();
</script>
</body>
</html>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script type="module">
// 引入 m1.js 模块内容
import {school, study} from "./m1.js";
// 引入 m2.js 模块内容
import {school as s, findJob} from "./m2.js";
// 引入 m3.js 模块内容
import {default as m3} from "./m3.js";
console.log(school);
study();
console.log(s);
findJob();
console.log(m3);
m3.change();
</script>
</body>
</html>
注意:针对默认暴露还可以直接
import m3 from "./m3.js"
7.1.19、浅拷贝和深拷贝
如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;如果B没变,那就是深拷贝,深拷贝与浅拷贝的概念只存在于引用数据类型。
var obj1 = {
name: "张三",
age: 20,
speak: function () {
console.log("我是" + this.name);
}
};
var obj2 = obj1;
// 当修改obj2的属性和方法的时候,obj1相应的属性和方法也会改变
obj2.name = "李四";
console.log(obj1);
console.log(obj2);
Array:slice()、concat()、Array.from()、… 操作符:只能实现一维数组的深拷贝
slice()方法演示:
var arr1 = [1, 2, 3, 4];
var arr2 = arr1.slice();
arr2[0] = 200;
console.log(arr1);
console.log(arr2);
concat()方法演示:
var arr1 = [1, 2, 3, 4];
var arr2 = arr1.concat();
arr2[0] = 200;
console.log(arr1);
console.log(arr2);
Array.from()方法演示:
var arr1 = [1, 2, 3, 4];
var arr2 = Array.from(arr1);
arr2[0] = 200;
console.log(arr1);
console.log(arr2);
… 操作符演示:
var arr1 = [1, 2, 3, 4];
var arr2 = [...arr1];
arr2[0] = 200;
console.log(arr1);
console.log(arr2);
Object:Object.assign()、… 操作符:只能实现一维对象的深拷贝
Object.assign()方法演示:
var obj1 = {
name: "张三",
age: 20,
speak: function () {
console.log("我是" + this.name);
}
};
var obj2 = Object.assign({}, obj1);
// 当修改obj2的属性和方法的时候,obj1相应的属性和方法不会改变
obj2.name = "李四";
console.log(obj1);
console.log(obj2);
… 操作符演示:
var obj1 = {
name: "张三",
age: 20,
speak: function () {
console.log("我是" + this.name);
}
};
var obj2 = {
...obj1
};
// 当修改obj2的属性和方法的时候,obj1相应的属性和方法不会改变
obj2.name = "李四";
console.log(obj1);
console.log(obj2);
JSON.parse(JSON.stringify(obj)):可实现多维对象的深拷贝,但会忽略 undefined
、 任意的函数
、Symbol 值
var obj1 = {
name: "张三",
age: 20,
birthday: {
year: 1997,
month: 12,
day: 5
},
speak: function () {
console.log("我是" + this.name);
}
};
var obj2 = JSON.parse(JSON.stringify(obj1));
// 当修改obj2的属性和方法的时候,obj1相应的属性和方法不会改变
obj2.name = "李四";
console.log(obj1);
console.log(obj2);
注意:进行
JSON.stringify()
序列化的过程中,undefined、任意的函数以及 symbol 值
,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时),由上面可知,JS 提供的自有方法并不能彻底解决Array、Object的深拷贝问题,因此我们应该自己实现。
var obj1 = {
name: "张三",
age: 20,
birthday: {
year: 1997,
month: 12,
day: 5
},
speak: function () {
console.log("我是" + this.name);
}
};
var obj2 = deepClone(obj1);
// 当修改obj2的属性和方法的时候,obj1相应的属性和方法不会改变
obj2.name = "李四";
console.log(obj1);
console.log(obj2);
/**
* 深拷贝通用方法
* @param obj 需要拷贝的对象
* @param has
* @returns {any|RegExp|Date}
*/
function deepClone(obj, has = new WeakMap()) {
// 类型检查
if (obj == null) return obj;
if (obj instanceof Date) return obj;
if (obj instanceof RegExp) return obj;
if (!(typeof obj == "object")) return obj;
// 构造对象
const newObj = new obj.constructor;
// 防止自引用导致的死循环
if (has.get(obj)) return has.get(obj);
has.set(obj, newObj);
// 循环遍历属性及方法
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
// 返回对象
return newObj;
}
7.2、ECMAScript7新特性
7.2.1、数组方法扩展
Array.prototype.includes:此方法用来检测数组中是否包含某个元素,返回布尔类型值
const mingzhu = ["西游记", "红楼梦", "三国演义", "水浒传"];
console.log(mingzhu.includes("西游记"));
- 1
- 2
7.2.2、幂运算
**
操作符的作用和 Math.pow
的作用是一样,请看代码:
console.log(2 ** 10);
console.log(Math.pow(2, 10));
- 1
- 2
7.3、ECMAScript8新特性
7.3.1、async 函数
async 函数的语法:
async function fn(){
}
- 1
- 2
- 3
async 函数的返回值:
- 返回的结果不是一个 Promise 类型的对象,返回的结果就是成功 Promise 对象
- 返回的结果如果是一个 Promise 对象,具体需要看执行resolve方法还是reject方法
- 抛出错误,返回的结果是一个失败的 Promise
async 函数的演示:
//async 函数
async function fn() {
return new Promise((resolve, reject) => {
resolve('成功的数据');
// reject("失败的错误");
});
}
const result = fn();
//调用 then 方法
result.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
7.3.2、await 表达式
async 和 await 两种语法结合可以让异步代码像同步代码一样
await 表达式的注意事项:
- await 必须写在 async 函数中
- await 右侧的表达式一般为 promise 对象
- await 返回的是 promise 成功的值
- await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
await 表达式的语法演示:
//创建 promise 对象
const p = new Promise((resolve, reject) => {
resolve("用户数据");
//reject("失败啦!");
})
//await 要放在 async 函数中.
async function fun() {
try {
let result = await p;
console.log(result);
} catch (e) {
console.log(e);
}
}
//调用函数
fun();
await 表达式的案例演示:async与await封装AJAX请求
// 发送 AJAX 请求, 返回的结果是 Promise 对象
function sendAJAX(url) {
return new Promise((resolve, reject) => {
//1. 创建对象
const x = new XMLHttpRequest();
//2. 初始化
x.open('GET', url);
//3. 发送
x.send();
//4. 事件绑定
x.onreadystatechange = function () {
if (x.readyState === 4) {
if (x.status >= 200 && x.status < 300) {
resolve(x.response);//成功
} else {
reject(x.status);//失败
}
}
}
})
}
// async 与 await 测试
async function fun() {
//发送 AJAX 请求 1
let joke = await sendAJAX("https://api.apiopen.top/getJoke");
//发送 AJAX 请求 2
let tianqi = await sendAJAX('https://www.tianqiapi.com/api/?version=v1&city=%E5%8C%97%E4%BA%AC&appid=23941491&appsecret=TXoD5e8P')
console.log(joke);
console.error(tianqi);//为了区别数据,我这里用红色的error输出
}
// 调用函数
fun();
7.3.3、对象方法拓展
- Object.keys()方法返回一个给定对象的所有可枚举键值的数组
- Object.values()方法返回一个给定对象的所有可枚举属性值的数组
- Object.entries()方法返回一个给定对象自身可遍历属性 [key,value] 的数组
//声明对象
const person = {
name: "张三",
age: 20
};
//获取对象所有的键
console.log(Object.keys(person));
//获取对象所有的值
console.log(Object.values(person));
//获取对象所有的键值对数组
console.log(Object.entries(person));
//创建 Map
const m = new Map(Object.entries(person));
console.log(m.get("name"));
Object.getOwnPropertyDescriptors方法返回指定对象所有自身属性的描述对象
//声明对象
const person = {
name: "张三",
age: 20
};
//对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(person));
//声明对象
const obj = Object.create(null, {
name: {
//设置值
value: "李四",
//属性特性
writable: true,
configurable: true,
enumerable: true
},
age: {
//设置值
value: 21,
//属性特性
writable: true,
configurable: true,
enumerable: true
}
});
//对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(obj));
7.4、ECMAScript9新特性
7.4.1、对象拓展
Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符
function connect({host, port, ...user}) {
console.log(host);
console.log(port);
console.log(user);
}
connect({
host: '127.0.0.1',
port: 3306,
username: 'root',
password: 'root',
type: 'master'
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
const skillOne = {
q: '天音波'
};
const skillTwo = {
w: '金钟罩'
};
const skillThree = {
e: '天雷破'
};
const skillFour = {
r: '猛龙摆尾'
};
const mangseng = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
console.log(mangseng);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
7.4.2、正则表达式拓展
ES9 允许命名捕获组使用符号 ?<name>
,这样获取捕获结果可读性更强。使用数组下标不好吗?的确不好,因为如果一旦你想要获取的元素一旦增加,数组下标就改变了,所以建议使用命名捕获分组
let str = '<a href="https://www.baidu.com">打开百度,你就知道!</a>';
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result = reg.exec(str);
console.log(result.groups.url);
console.log(result.groups.text);
- 1
- 2
- 3
- 4
- 5
ES9 支持正向断言,通过对匹配结果后面的内容进行判断,对匹配进行筛选。
//声明字符串
let str = "订单编号开始123456789订单编号结束";
//正向断言
const reg = /\d+(?=订单编号结束)/;//也就是说数字的后边一定要跟着 订单编号结束
const result = reg.exec(str);
console.log(result);
- 1
- 2
- 3
- 4
- 5
- 6
ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。
//声明字符串
let str = "订单编号开始123456789订单编号结束";
//正向断言
const reg = /(?<=订单编号开始)\d+/;//也就是说数字的前边一定要跟着 订单编号开始
const result = reg.exec(str);
console.log(result);
- 1
- 2
- 3
- 4
- 5
- 6
正则表达式中点 .
匹配除回车外的任何单字符,标记 s
改变这种行为,允许行终止符出现,也就是dotAll模式
let str = `
<ul>
<li>
<a>肖生克的救赎</a>
<p>上映日期: 1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期: 1994-07-06</p>
</li>
</ul>`;
//声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
// 执行匹配
let result;
let data = [];
while (result = reg.exec(str)) {
data.push({title: result[1], time: result[2]});
}
//输出结果
console.log(data);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
7.5、ECMAScript10新特性
7.5.1、对象方法拓展
Object.fromEntries()
方法是Object.entries()
的逆操作,用于将一个键值对数组转为对象。
//ES6:Map
//ES10:Object.fromEntries
const m = new Map();
m.set("name", "张三");
m.set("age", 20);
const result = Object.fromEntries(m);
console.log(result);
//ES8:Object.entries
const arr = Object.entries(result);
console.log(arr);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
7.5.2、字符串方法拓展
let str = " iloveyou ";
console.log(str.trimStart());//只去除前边的空格
console.log(str.trimEnd());//只去除后边的空格
7.5.3、数组方法拓展
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回,说白了就是将多维数组转化为低维数组。
const arr1 = [1, 2, 3, 4, [5, 6]];
console.log(arr1.flat());
const arr2 = [1, 2, 3, 4, [5, 6, [7, 8, 9]]];
console.log(arr2.flat());
console.log(arr2.flat(1));//参数为深度是一个数字
console.log(arr2.flat(2));//参数为深度是一个数字
flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
var arr1 = [1, 2, 3, 4];
console.log(arr1.map(x => x * 2));
var arr2 = [1, 2, 3, 4];
console.log(arr2.flatMap(x => x * 2));
7.5.4、Symbol属性拓展
Symbol.prototype.description用来读取Symbol的描述值
//创建 Symbol
let s = Symbol("张三");
console.log(s.description);
- 1
- 2
- 3
7.6、ECMAScript11新特性
7.6.1、class 私有属性
私有属性只能在class中访问
class Person {
//公有属性
name;
//私有属性
#age;
#weight;
//构造方法
constructor(name, age, weight) {
this.name = name;
this.#age = age;
this.#weight = weight;
}
//普通方法
intro() {
console.log(this.name);
console.log(this.#age);
console.log(this.#weight);
}
}
//实例化
const girl = new Person("小可爱", 18, "45kg");
girl.intro();
7.6.2、Promise.allSettled
该Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。
相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。
//声明两个promise对象
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("商品数据 - 1");
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("商品数据 - 2");
reject("出错啦!");
}, 1000);
});
//调用 allsettled 方法
const result1 = Promise.allSettled([p1, p2]);
console.log(result1);
//调用 all 方法
const result2 = Promise.all([p1, p2]);
console.log(result2);
7.6.3、字符串方法扩展
String.prototype.matchAll()
方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。
let str =
`<ul>
<li>
<a>肖生克的救赎</a>
<p>上映日期: 1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期: 1994-07-06</p>
</li>
</ul>`;
//声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg;
//调用方法
const result = str.matchAll(reg);
for (let v of result) {
console.log(v);
}
7.6.4、可选链操作符
当我们要使用传进来的一个属性值的时候,我们不知道这个属性值到底有没有传,我们可以使用&&运算符一级一级判断,就像这样 const dbHost = config && config.db && config.db.host;
但是这样会显得很麻烦,所以在ES11 中就提供了可选链操作符,它就简化了代码,变成了这样 const dbHost = config?.db?.host;
另一方面,即使用户没有传入这个属性,我们用了也不会报错,而是undefined
function connect(config) {
// const dbHost = config && config.db && config.db.host;
const dbHost = config?.db?.host;
console.log(dbHost);
}
connect({
db: {
host: "192.168.1.100",
username: "root"
},
cache: {
host: "192.168.1.200",
username: "admin"
}
})
7.6.5、动态 import
以前我们import 导入模块是在一开始的时候就全部导入了,这样在模块很多的时候,会显得网页速度加载很慢,在ES11中就提供了一种动态import,案例演示如下:
m1.js
//分别暴露
export let school = "华北理工大学";
export function study() {
console.log("我们要学习!");
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">点击我,加载m1.js模块</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script type="module">
const btn = document.getElementById("btn");
btn.onclick = function(){
import("./m1.js").then(module => {
module.study();
});
};
</script>
</body>
</html>
7.6.6、BigInt类型
BigInt
数据类型的目的是比Number
数据类型支持的范围更大的整数值。在对大整数执行数学运算时,以任意精度表示整数的能力尤为重要。使用BigInt
,整数溢出将不再是问题。
此外,可以安全地使用更加准确时间戳,大整数ID等,而无需使用变通方法。 它就是JS 第二个数字数据类型,也将是 JS 第8种基本数据类型:
- Boolean
- Null
- Undefined
- Number
- BigInt
- String
- Symbol
- Object
对于学过其它语言的程序员来说,JS中缺少显式整数类型常常令人困惑。许多编程语言支持多种数字类型,如浮点型、双精度型、整数型和双精度型,但JS却不是这样。在JS中,按照IEEE 754-2008标准的定义,所有数字都以双精度64位浮点格式表示。
在此标准下,无法精确表示的非常大的整数将自动四舍五入。确切地说,JS 中的Number
类型只能安全地表示-9007199254740991 (-(2^53-1))
和9007199254740991(2^53-1)
之间的整数,任何超出此范围的整数值都可能失去精度。
如何定义BigInt?需要在数字的后边加上一个n,例如;
let n = 521n;
- 1
我们接下来演示一下,大整数运算的效果:
let max = Number.MAX_SAFE_INTEGER;
console.log(max);
console.log(max + 1);
console.log(max + 2);
console.log(BigInt(max));
console.log(BigInt(max) + BigInt(1));
console.log(BigInt(max) + BigInt(2));
7.6.7、globalThis
全局属性 globalThis
包含全局的 this
值,类似于全局对象(global object)。
console.log(globalThis);
- 1
第八章 JavaScript项目百练
8.1、项目百练介绍
这一章一共提供了100道练习,源码请在配套资料中获取,这些练习均是作者早期的练习、兼容性代码、CSS特效、精品收藏等等,仅供参考,有精力朋友的可以看看,可能有些写法不是很标准或者您有更好的方案,可以直接在评论区留言,大家一起学习!
8.2、项目百练目录
- Sample0001-美女相册.zip
- Sample0002-兼容性代码.zip
- Sample0003-精美选项卡.zip
- Sample0004-百度首页换肤功能.zip
- Sample0005-带复选框表格.zip
- Sample0006-兼容性代码.zip
- Sample0007-兼容性代码.zip
- Sample0008-获取节点或者元素.zip
- Sample0009-验证密码强度.zip
- Sample0010-百度首页搜索项目.zip
- Sample0011-模拟星空.zip
- Sample0012-美女时钟.zip
- Sample0013-定时阅读协议.zip
- Sample0014-动态创建表格.zip
- Sample0015-滑动式轮播图.zip
- Sample0016-小汽车动画.zip
- Sample0017-渐入式轮播图.zip
- Sample0018-捉迷藏动画.zip
- Sample0019-吸附式导航栏.zip
- Sample0020-滑动式导航栏.zip
- Sample0021-旋转式轮播图.zip
- Sample0022-手风琴案例展示.zip
- Sample0023-开机小助手动画.zip
- Sample0024-筋斗云案例.zip
- Sample0025-美丽的天使.zip
- Sample0026-可拖拽对话框.zip
- Sample0027-放大镜效果.zip
- Sample0028-自定义滚动条.zip
- Sample0029-贪吃蛇.zip
- Sample0030-扁平化二级菜单.zip
- Sample0031-选戏子.zip
- Sample0032-手风琴选项卡.zip
- Sample0033-淘宝精品展示.zip
- Sample0034-选项卡公告栏.zip
- Sample0035-精美购物车插件.zip
- Sample0036-呼吸灯轮播图.zip
- Sample0037-仿小米手风琴展示.zip
- Sample0038-音乐导航栏.zip
- Sample0039-城市选择框.zip
- Sample0040-文字弹幕效果.zip
- Sample0041-钢琴案例.zip
- Sample0042-小火箭返航案例.zip
- Sample0043-五角星评分案例.zip
- Sample0044-懒加载图片案例.zip
- Sample0045-仿淘宝首页轮播图.zip
- Sample0046-仿京东首页轮播图.zip
- Sample0047-仿360安全换机.zip
- Sample0048-拖拽旋转相册.zip
- Sample0049-仿百度经验时光轴.zip
- Sample0050-照片墙案例.zip
- Sample0051-瀑布流加载案例.zip
- Sample0052-旋转翻页案例.zip
- Sample0053-立体图片翻转特效.zip
- Sample0054-鼠标不同方位移入特效.zip
- Sample0055-省市县三级联动.zip
- Sample0056-抽屉式手风琴.zip
- Sample0057-仿厅客世界.zip
- Sample0058-兼容性开发模板.zip
- Sample0059-仿腾讯新闻多图序列展示.zip
- Sample0060-3D分块旋转轮播图.zip
- Sample0061-仿优酷TV轮播图.zip
- Sample0062-仿商品参数选购.zip
- Sample0063-仿w3school首页.zip
- Sample0064-仿家有易购电商首页.zip
- Sample0065-网页新功能提示案例.zip
- Sample0066-鼠标点击展开详情.zip
- Sample0067-组件排序案例.zip
- Sample0068-仿职位发布案例.zip
- Sample0069-仿迅雷首页.zip
- Sample0070-后台侧栏导航.zip
- Sample0071-弹性广告栏.zip
- Sample0072-照片墙案例.zip
- Sample0073-仿京东商品图片展示.zip
- Sample0074-仿AppIntro首页.zip
- Sample0075-Material Design Icons.zip
- Sample0076-英语之家.zip
- Sample0077-鲜来鲜往.zip
- Sample0078-麦子学院.zip
- Sample0079-Material Design Color.zip
- Sample0080-Java关键字.zip
- Sample0081-浏览历史静态页面案例.zip
- Sample0082-小灯泡开关案例.zip
- Sample0083-百度折叠案例.zip
- Sample0084-计算器.zip
- Sample0085-计算器.zip
- Sample0086-judge_online_system.zip
- Sample0087-选项卡01.zip
- Sample0088-选项卡02.zip
- Sample0089-焦点轮播图.zip
- Sample0090-抽奖活动.zip
- Sample0091-抽屉式信息栏.zip
- Sample0092-无缝轮播图.zip
- Sample0093-Attendance.zip
- Sample0094-统计数字.zip
- Sample0095-反转字符串.zip
- Sample0096-表单验证.zip
- Sample0097-显示时间.zip
- Sample0098-扁平化选项卡.zip
- Sample0099-吃热狗小游戏.zip
- Sample0100-爱阅读APP.zip