一、完整的JavaScript包含什么?他们的含义又是什么?
1.1 完整的JavaSctript包含三个部分
核心ECMAScript、文档对象模型DOM、浏览器对象模型BOM
1.2 JavaScript 三个部分的含义
1.2.1 ECMAScript
ECMA-262定义了一门语言的语法、类型、语句、关键字、保留字、操作符、全局对象;ECMA-262迭代了很多版本,比如ECMA-262的第六版就是俗称的ES6。
ECMAScript是ECMA-262所定义的语言,是对实现这个规范描述所有方面的一门语言的称呼。
JavaScript实现了ECMAScript。
1.2.2 DOM 文档对象模型
文档对象模型(DOM)是一个应用编程接口,用于在HTML中使用拓展的XML。
DOM将整个页面抽象为一组分层节点。
<html>
<head>
<title>Page Title</title>
</head>
<body>
<p>Hello World</p>
</body>
</html>
代码通过DOM可以表示成一组分层节点。
1.2.3 BOM 浏览器对象模型
浏览器对象模型(BOM),用于支持访问和操作浏览器的窗口。使用BOM,开发者可以操控浏览器显示页面之外的部分。BOM的核心是Window对象,表示浏览器的实例。
二、var、let、const的使用与异同?
2.1 var、let、const异同点总览
可声明变量 | 作用域 | 变量提升 | 冗余声明 | 需要初始化变量 | 不可修改 | |
var | 所有类型 | 函数作用域 | ✔ | ✔ | × | × |
let | 所有类型 | 块作用域 | × | × | × | × |
const | 所有类型 | 块作用域 | × | × | ✔ | ✔ |
2.2 var、let、const异同点详情和使用
2.2.1 作用域
var的作用域为函数作用域,即是一旦在函数中声明,整个函数中都可使用。
let 和 const 的作用域为块作用域,即在{}中定义let,则在{}外则无法访问。
下面给出var、let和const作用域的示例, 对于var声明的变量,可以看到即使是在块内声明,整个函数都可以访问到,当然他的作用域也仅仅局限于当前函数test()之中。
function test() {
if (true) {
var a = "HelloWorld"
console.log("块内声明:" + a)
}
console.log("块外:" + a)
}
test()
console.log("函数作用域外:" + a)
而对于let和const声明的变量而言,只能在当前块中访问,当前函数作用域以及函数作用域外都不能访问到。
function test() {
if (true) {
//const a = "HelloWorld"
let a = "HelloWorld"
console.log("块内声明:" + a)
}
console.log("块外:" + a)
}
test()
console.log("函数作用域外:" + a)
2.2.2 变量提升
var存在变量提升,即是使用var会自动将var所定义的变量,自动提升到函数作用域顶部。
let、const则不存在变量提升。
下面给出var、let和const变量提升的示例。var 变量提升指的是,使用这个关键字声明的变量会自动提升到函数作用域顶部,不会报错,而是会显示undefined未赋值。
console.log(a)
var a = "HelloWorld"
console.log(a)
即是上面的代码行等价于如下代码:
var a
console.log(a)
var a = "HelloWorld"
而let和const不存在变量提升,如果在使用这个变量前没有定义,会直接报错。
console.log(a)
let a = "HelloWorld"
// const a = "HelloWorld"
2.2.3 冗余声明
var可以冗余声明,let、const则不允许出现冗余声明。
冗余声明指的是反复声明同一个变量多次,var允许冗余声明。
var a = "1"
var a = "2"
var a = "3"
console.log(a)
let和const则不允许冗余声明,会直接报错。
let a = "1"
let a = "2"
//const a = "1"
//const a = "2"
console.log(a)
2.2.4 初始化变量和修改
const声明变量的同时必须初始化变量,且不能修改const的值。
const a
const a = 1;
a += 2;
三、 for循环中为什么最好使用let而不是const、var
3.1 var存在变量渗透、以及在使用定时器时会出现诡异的错误
3.1.1 变量渗透
所谓变量渗透即是因为var的作用域为整个函数作用域,通过var定义的i,会在整个函数中有定义。而不是仅仅在这个循环体中有定义。
for (var i = 0; i < 3; i++) {
}
console.log(i);
3.1.2 定时器“错误”
同时如果在循环中加入定时器,需要对i的值进行操作,那么只能得到导致循环结束的i的值
for (var i = 0; i < 3; i++) {
setTimeout(() => { console.log(i) }, 0)
}
3.2 const从定义上就不允许修改,用于当作循环计数的i时会直接报错
for (const i = 0; i < 3; i++) {
}
3.3 使用let可以完美规避掉上面这三个问题,因此for循环中最好使用let。
使用let定义for循环时的i,则定时器中函数引用的变量为JavaScript引擎为每次迭代声明的新变量,而不是直接打印最后结束时候的i值,此时也不会发生变量渗透的问题,i不会作用于函数外。
for (let i = 0; i < 3; i++) {
setTimeout(() => { console.log(i) }, 0)
}
console.log(i)
四、Undefined、Null、NaN分别是什么?
4.1 Undefined
当使用var或let声明了变量但没有初始化时,就相当于给变量赋予了undefined值。
let a
var b
console.log(a)
console.log(b)
4.2 Null
null值表示一个空对象指针,null用typeof会传回object
console.log(typeof(null))
undefined由null派生而来,ECMA-262将他们定义为表面上相等,即是null==undefined会返回true,当然并不是全等于。
console.log(undefined == null)
console.log(undefined === null)
4.3 NaN
NaN(Not a Number)为一个特殊的数值,用于表示本来要返回数值的操作失败了。0,+0或-0相除会返回NaN
比如0/0 会返回NaN,而3/0会返回Infinity
console.log(0/0)
console.log(3/0)
NaN在进行大小比较的时候也会出现很诡异的情况
console.log(NaN > 3);
console.log(NaN <= 3);
console.log(NaN == NaN);
console.log(NaN === NaN);
console.log(NaN != NaN);
NaN 既不大于3也不等于3,也不等于自己,更不全等于自己。
五、关系操作符,为什么会出现3 > 23 ?
这四行代码执行后,会有一行返回false,即是出现奇怪的 “23 < 3”
console.log("23" > "3");
console.log(23 > "3");
console.log("23" > 3);
console.log(23 > 3);
“23”和“3”都是字符串的时候,会逐步比较他们的字符编码,“2”的编码50小于“3”的编码51,即会返回false。
其他情况当包含有数值时,字符串便会被转化为数值再进行比较。