JS中的异常处理:
当我们访问一些网站时,打开开发者工具,我们总能看见一些红色的警告事件,它们的一些是js脚本错误。
看以下代码:
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
let div = document.querySelector('div#out');
div.innerHTML = 'Hello World';
</script>
</body>
</html>
[/qzdypre]
当浏览此网页,打开开发者工具,我们可以看见控制台中打印出了js的异常。
这段代码试图寻找 ID 为 "out" 的div元素,如果页面上没有这样的元素,querySelector将返回null。而尝试在null上调用innerHTML则会抛出错误。
- 知识点:当有异常发生,会引发window对象的“error”事件
以上述代码为例。在js中增加如下代码
[qzdypre]
window.addEventListener('error', function (e) {
alert();
});
[/qzdypre]
在这段代码中,当出现异常错误时,会弹出一个警告框。打开浏览器发现
确实触发了window对象的error事件。
- 精华点:在浏览器中不显示异常错误
首先要注意的是:window对象的error事件没有event对象。
它接受三个参数,错误的消息/URL/行号
在js中添加如下代码:
[qzdypre]
window.onerror = function (msg,url,line) {
console.log(msg);
console.log("——————————")
console.log(url);
console.log("——————————")
console.log(line);
return true;
};
[/qzdypre]
当打开浏览器的开发者工具,会发现已经不存在红色的异常。(这里是return true起了作用)
- 思考:怎么捕捉异常,也不让用户看见?
答:利用异步,将错误的消息/URL/行号发送到日志中,最后return true让用户不能看见控制台错误。
- 补充注意点:要将事件处理程序放在捕捉对象之前,否则将不会起到效果。因为浏览器解析代码是按照“从上到下”解析的原则
- 利用try……catch来捕捉异常
首先,我们要知道的是,在js中的try……catch中,不能有重复的catch块(也就是说只能有一个catch),当我们需要捕捉多个未知(可能的)异常时,我们需要利用以下方法:
[qzdypre]
try{
let div = document.querySelector('div#out');
div.innerHTML = 'Hello World';
}catch(e){
if(error instanceof DOMError){
}else if(error instanceof RangeError){
}else if(error instanceof TypeError){
}else{
}
}
[/qzdypre]
在上面的示例中,我们使用instanceof运算符来检查异常的类型。根据不同的异常类型,我们可以在相应的catch块中编写特定的处理逻辑。如果异常类型没有匹配任何已知的类型,则可以在最后的catch块中处理未知类型的异常。
请注意,如果try中的代码并没有抛出异常,那么catch不会执行。
部分错误类型:
- 小技巧:
我们可以在js中新建一个obj对象,并在catch块中将错误消息发送给obj,在finally块中让obj对象将错误消息发送到服务器中,这样就可以在日志中看到错误消息。
- 小技巧:
我们可以在js中新建一个obj对象,并在catch块中将错误消息发送给obj,在finally块中让obj对象将错误消息发送到服务器中,这样就可以在日志中看到错误消息。
上节课的两种:1.通过超链接(href=url?page=xxx&value=xxx) 2.通过LocalStorage
这节课主要讲的是window.name和postMessage方法
引出概念:在同一个窗口中共享window.name
什么是“同一个窗口”?:
在同一个浏览器页面中打开了a.html和b.html,那他们之间可以共享window.name
- 实例演示:
新建两个页面a.html和b.html
a页面代码:
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="b.html">b.html</a>
<input type="button" value="print">
<script>
window.name="hello";
let btn = document.querySelector('input[type="button"]');
btn.addEventListener('click',(event)=>{
alert(window.name);
});
</script>
</body>
</html>
[/qzdypre]
b页面代码:
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="a.html">a.html</a>
<input type="button" value="print">
<script>
let btn = document.querySelector('input[type="button"]');
btn.addEventListener('click',(event)=>{
alert(window.name);
});
</script>
</body>
</html>
[/qzdypre]
打开a页面,点击print按钮,可以发现弹窗提示当前window.name为hello
当通过a页面跳转到b页面点击print按钮,发现也是弹窗提示当前window.name为hello
- 思考:如果我在两个页面中传输多个数据,该怎么办?
答:用JSON字符串来传输
- 思考实例演示:
将a页面代码改为:
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="b.html">b.html</a>
<input type="button" value="print">
<script>
let data={
name:"Alice",
age:20,
gender:"female"
};
window.name=JSON.stringify(data);
let btn = document.querySelector('input[type="button"]');
btn.addEventListener('click',(event)=>{
console.log(window.name);
});
</script>
</body>
</html>
[/qzdypre]
这里我们定义了一个data数组并通过JSON方法打包为window.name,当单击print 时,会在控制台打印出结果,a和b页面打印的结果一致。如图。
当然,我们也可以用JSON.parse(window.name)来提取这些数据
PostMessage方法和Message事件
- 父子窗口和父子关系
举个例子,浏览器窗口中加载parent.html,当点击某个按钮,可以加载另外一个页面(这里以child.html为例)。
下面附上代码:
parent.html
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>parent.html</h1>
<input type="button" value="print">
<script>
let btn = document.querySelector('input[type="button"]');
btn.addEventListener('click',(event)=>{
window.open("child.html" , "child_win");
});
</script>
</body>
</html>
[/qzdypre]
child.html
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>child.html</h1>
</body>
</html>
[/qzdypre]
打开这里的parent页面,当没有点击print按钮时,通过控制台输入window.name发现此时为空(因为并没有定义window.name)
当点击了print按钮,js脚本中监听到click(单击)事件,window.open("child.html","child_win");代码会将子页面的window.name改为child_win。
这里的child_win还可以是_self(当前页面打开)或者_blank(新页面打开)。
要注意的是:此时parent和child页面为父子关系
这里引入了新的属性window.open,要注意的是它的完整声明为window.open(url,name,feature,replace);
features为新窗口要显示的标准浏览器窗口的特征,具体为:
alwaysLowered | yes/no | 指定窗口隐藏在所有窗口之后
alwaysRaised | yes/no | 指定窗口悬浮在所有窗口之上
depended | yes/no | 是否和父窗口同时关闭
directories | yes/no | Nav2和3的目录栏是否可见
height | pixel value | 窗口高度
hotkeys | yes/no | 在没菜单栏的窗口中设安全退出热键
innerHeight | pixel value | 窗口中文档的像素高度
innerWidth | pixel value | 窗口中文档的像素宽度
location | yes/no | 位置栏是否可见
menubar | yes/no | 菜单栏是否可见
outerHeight | pixel value | 设定窗口(包括装饰边框)的像素高度
outerWidth | pixel value | 设定窗口(包括装饰边框)的像素宽度
resizable | yes/no | 窗口大小是否可调整
screenX | pixel value | 窗口距屏幕左边界的像素长度
screenY | pixel value | 窗口距屏幕上边界的像素长度
scrollbars | yes/no | 窗口是否可有滚动栏
titlebar | yes/no | 窗口题目栏是否可见
toolbar | yes/no | 窗口工具栏是否可见
Width | pixel value | 窗口的像素宽度
z-look | yes/no | 窗口被激活后是否浮在其它窗口之上
yes/no等同1/0
replace用来声明新文档是在窗口的浏览历史中拥有自己的条目,还是替换当前文档的条目,如果replace为true,新文档就会替换旧文档。如果replace为false,或者省略,那么新文档会在窗口的浏览历史中拥有自己的条目。
- 父窗体发送数据到子窗体
大体流程为:发送数据的窗体调用postMessage,接收数据的窗体监听message事件。
parent.html
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Parent</title>
</head>
<body>
<h1>parent.html</h1>
<input type="button" value="open">;
<input type="button" value="close">;
<input type="button" value="sendData">;
<script>
let btn = document.querySelector('input[value="open"]')
let btn_close = document.querySelector('input[value="close"]')
let btn_send = document.querySelector('input[value="sendData"]')
let child_handler=null;
btn.addEventListener('click',(event)=>{
child_handler=window.open("child.html" , "child_win");
});
btn_close.addEventListener('click',(event)=>{
child_handler.close();
});
btn_send.addEventListener('click',(event)=>{
child_handler.postMessage("hello");
});
</script>
</body>
</html>
[/qzdypre]
child.html
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Child</title>
</head>
<body>
<h1>child.html</h1>
<input type="button" value="print">
<script>
document.querySelector('input[type="button"]').addEventListener('click',(event)=>{
console.log(window.opener);
console.log(window.opener.document.body.innerHTML);
});
window.addEventListener("message",(event)=>{
console.log(event.data);
})
</script>
</body>
</html>
[/qzdypre]
这个示例中,我们看到了两个HTML页面:Parent和Child。它们通过JavaScript使用PostMessage方法进行通信。下面是这个示例中涉及的一些关键知识点:
这个示例展示了如何使用PostMessage方法在不同窗口之间发送消息,以及如何监听和处理这些消息。同时,也展示了如何使用window.opener属性在子窗口中访问父窗口的文档和数据。
当然 在实际运用中,我们尽可能用到"事件委托"来提高性能。
修改后的parent
[qzdypre]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Parent</title>
</head>
<body>
<h1>parent.html</h1>
<div id="wrapper">
<input type="button" value="open">;
<input type="button" value="close">;
<input type="button" value="sendData">;
</div>
<script>
window.name = "父窗体";
let wrapper = document.querySelector("#wrapper");
let child_handler = null;
wrapper.addEventListener('click', (event) => {
let all_btn = wrapper.querySelectorAll('#wrapper input');
switch (event.target.value) {
case "open":
child_handler = window.open("child.html", "child_win");
break;
case "close":
child_handler.close();
break;
case "sendData":
child_handler.postMessage("hello");
break;
}
});
</script>
</body>
</html>
[/qzdypre]
事件委托的好处:
将事件委托给容器元素,而不是直接委托给按钮元素,有以下几个好处:
- 减少代码量:通过将事件委托给容器元素,您可以使用更少的代码来实现相同的功能。在这种情况下,您只需要为容器元素添加一个事件监听器,而不是为每个按钮元素添加一个事件监听器。
- 更好的可维护性:如果您需要在页面中添加或删除按钮元素,您不需要更改事件监听器的代码。这是因为事件监听器是添加到容器元素上的,而不是直接添加到按钮元素上的。这样可以提高代码的可维护性。
- 更好的性能:由于容器元素可能包含多个子元素(例如按钮元素),将事件委托给容器元素可以减少事件处理函数的调用次数。这可以提高应用程序的性能。
- 更好的跨浏览器兼容性:某些浏览器可能不支持在按钮元素上使用事件委托。但是,大多数浏览器都支持在容器元素上使用事件委托。因此,将事件委托给容器元素可以确保您的代码在各种浏览器中都能正常工作。
ES6中的类:
ES6(ECMAScript 2015)是JavaScript的最新版本,引入了类的语法。ES6类是基于原型的继承模型的语法糖,使得JavaScript的面向对象编程更加方便和直观。
在JavaScript ES6中,类是一种构造函数的模式,它可以帮助我们更方便地创建对象和继承。以下是你所提到的几个要点的详细说明:
- 没有访问修饰符:在ES6类中,不像Java那样有public、private、protected等访问修饰符。在类中的属性或方法默认都是公有的,不能直接用let或var来声明为私有。如果你想声明为私有,需要通过特定的语法来实现。
- 构造器:在ES6类中,构造器是一个特殊的方法,当创建类的新实例时,构造器会被自动调用。它通常用于初始化新创建的对象。
[qzdypre]
class MyClass {
constructor(name) {
this.name = name;
}
}
[/qzdypre]
3.构造器的原理:构造器的执行过程可以分为四个步骤:
- 在堆上分配内存,创建对象。
- 把this指向这一块内存。
- 执行构造器的代码。
- 返回this,即新创建的对象。这个对象可以进一步被使用或返回给调用者。
[qzdypre]
class MyClass {
constructor(name) {
console.log('分配内存'); // 步骤1:分配内存
this.name = name; // 步骤2:this指向新对象,并添加属性name
console.log('构造器执行完毕'); // 步骤3:执行构造器代码
}
}
let obj = new MyClass('Alice'); // 步骤4:返回新对象,赋值给obj
console.log(obj); // 输出:{name: "Alice"}
[/qzdypre]
4.getter和setter:构造器的执行过程可以分为四个步骤:getter和setter是用于访问和修改对象的属性的特殊方法。在ES6类中,你可以使用getter和setter来控制对属性的访问和修改。在IDEA中可以通过alt+insert来自动生成getter和setter。例如:
[qzdypre]
class MyClass {
constructor(name) {
this._name = name; // 私有属性_name
}
get name() { // getter方法,用于获取_name属性的值
return this._name;
}
set name(value) { // setter方法,用于修改_name属性的值
if (value.length > 10) {
console.log('Name is too long!');
return;
}
this._name = value;
}
}
[/qzdypre]
文章评论