JS|7种JavaScript代码调试的方法

JS是解释型语言,是逐条语句解释执行的,如果错误发生在某个语句块,此语句块以前的语句一般都可以正常执行。这不同于C等编译型语言。

代码调试的重点在于找到错误发生点,然后才能有的放矢。

通常可以使用警告框来提示变量信息。

alert(document.body.innerHTML);

当警告框弹出时,用户将需要单击“确定”来继续。

需要注意的是是,以下写法会替换整个页面的内容:

<button type=\”button\” onclick=\”document.write(5 + 6)\”>试一试</button>

即使是函数调用也是如此。

document.getElementById(\”demo\”).innerHTML =\”\”

JS的运行环境是浏览器,由浏览器引擎解释执行JS代码,一般来说,浏览器也提供调试器,如chrome按F12即可调出高试器:

如果您的浏览器支持调试,那么您可以使用 console.log() 在调试窗口中显示 JavaScript 的值:

内置的调试器可打开或关闭,强制将错误报告给用户。

通过调试器,您也可以设置断点(代码执行被中断的位置),并在代码执行时检查变量。

JavaScript 实际上会创建带有两个属性的 Error 对象:name 和 message。

name 设置或返回错误名。

message 设置或返回错误消息(一条字符串)。

debugger停止执行 JavaScript,并调用调试函数(如果可用)。

可以注释掉一些可疑代码来确定错误发生点。

或者考虑逐步增加代码的方法,逐步验证,以避免错误。

8.1 意外使用赋值运算符

如果程序员在 if 语句中意外使用赋值运算符(=)而不是比较运算符(===),JavaScript 程序可能会产生一些无法预料的结果。

8.2 令人困惑的加法和级联

加法用于加数值。

级联(Concatenation)用于加字符串。

在 JavaScript 中,这两种运算均使用相同的 + 运算符。

正因如此,将数字作为数值相加,与将数字作为字符串相加,将产生不同的结果:

而加法以外的其它算法运算符可以将字符串进行自动类型转换。

10-\”5\” // 5

8.3 令人误解的浮点数

JavaScript 中的数字均保存为 64 位的浮点数(Floats),符合IEEE754的标准。

所有编程语言,包括 JavaScript,都存在处理浮点值的困难:

8.4 错位的分号

因为一个错误的分号,此代码块无论 x 的值如何都会执行:

在一行的结尾自动关闭语句是默认的 JavaScript 行为。

在 JavaScript 中,用分号来关闭(结束)语句是可选的。

8.5 对象使用命名索引

在 JavaScript 中,数组使用数字索引。

在 JavaScript 中,对象使用命名索引。

如果您使用命名索引,那么在访问数组时,JavaScript 会将数组重新定义为标准对象。

8.6 Undefined 不是 Null

Undefined 的类型是 Undefined,Null的类型是Object。

JavaScript 对象、变量、属性和方法可以是未定义的。

此外,空的 JavaScript 对象的值可以为 null。

在测试非 null 之前,必须先测试未定义:

if (typeof myObj !== \”undefined\” && myObj !== null)

8.7 JS没有块作用域(与C语言不同)

在 ES2015 之前,JavaScript 只有两种类型的作用域:全局作用域和函数作用域。

ES2015 引入了两个重要的 JavaScript 新关键词:let 和 const。

这两个关键字在 JavaScript 中提供了块作用域(Block Scope)变量(和常量)。

-End-

客户端脚本语言:JS的运行机制

JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。

代码块: JS中的代码块是指由<script>标签分割的代码段。JS是按照代码块来进行编译和执行的,代码块间相互独立(即就算代码块1出错,但不影响代码块2的加载和执行),但变量和方法共享。

案例:2个代码块

<script type=\”text/javascript\”>

console.log(\”这是代码块一\”);

</script>

<script type=\”text/javascript\”>

console.log (\”这是代码块二\”);

</script>

HTML页面中JS的加载原理: 在加载HTML页面的时候,当浏览器遇到内嵌的JS代码时会停止处理页面,先执行JS代码,然后再继续解析和渲染页面。同样的情况也发生在外链的JS文件中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它,在这个过程中,页面的渲染和用户互交完全被阻塞。由于现代浏览器都允许并行下载JS文件,因此<script>标签在下载外部资源时不会阻塞其他的<script>标签。遗憾的是JS下载过程仍然会阻塞其他资源的下载。

JavaScript的单线程:

JS语言的一大特点就是单线程,也就是说,同一个时间只能做一件事情。之所以是单线程,是因为与它的用途有关,作为浏览器脚本语言,JS的主要用途是与用户互动以及操作DOM。这决定了它只能是单线程,否则会带来复杂的同步问题。为了利用多核CPU的计算功能,HTML5提出了web worker标准,允许JS脚本创建多个线程,但是子线程完全受主线程控制,且不能操作DOM,所以这个新标准并没有改变JS单线程的本质。

JavaScript的任务列队:

JS任务可以分为两种:一种是同步任务,另一种是异步任务。注意,只有主线程空了,才会去读取\”任务队列\”,这就是JS的运行机制,这个过程会不断重复。

同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕了,才会执行后一个任务。

异步任务:在主线程之外,还存在一个“任务列队”,异步任务就是不进入主线程,而是进入“任务列队”的任务,只有“任务列队”通知主线程,某个异步任务可以执行了并且同步任务执行完毕,该任务才会进入主线程执行。

Javascript的事件和回调函数:

\”任务列队\”是一个事件的列队,IO设备完成一项任务,就在\”任务队列\”中添加一个事件,表示相关的异步任务可以进入\”执行栈\”了。主线程读取\”任务队列\”,就是读取里面有哪些事件。\”任务队列\”中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入\”任务队列\”,等待主线程读取。

所谓\”回调函数\”,就是那些会被主线程挂起的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。\”任务队列\”是个先进先出的数据结构,排在前面的事件,优先被主线程读取。

主线程的读取过程基本上是自动的,只要执行栈一清空,\”任务队列\”上第一位的事件就自动进入主线程。但是,由于存在后文提到的\”定时器\”功能,主线程首先检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

定时器:

除了放置异步任务的事件,\”任务队列\”还可以放置定时事件,即指定某些代码在多少时间之后执行。定时器功能主要由setTimeout和setInterval这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeou方法:

setTimeout接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数

console.log(1)

setTimeout(function {

console.log(2)

}, 1000);

console.log(3)

上面代码的执行结果是1=>3=>2,因为setTimeout将第二行推迟到1000毫秒之后执行

如果将setTimeout的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后立即执行

setTimeout(function {

console.log(2)

}, 0);

console.log(3)

上面代码的执行结果是3=>2,因为只有在执行完第二行以后,系统才会去执行\”任务队列\”中的回调函数。总之,setTimeout(fn, 0)的含义是,指定某个任务在主线程最早的空闲时间执行,也就是说尽可能早的执行。它在\”任务队列\”的尾部添加一个事件,因此要等到同步任务和\”任务队列\”现有的事情都处理完,才会得到执行。

需要注意的是,setTimeout知识将事件插入了\”任务队列\”,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数,要是当前代码耗时很长,有可能要等很久,所以并没有办法保证回调函数一定会在setTimeout指定的时间执行。

内容来源:博客园

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。