基础知识
一、关于变量
1.1 弱类型
JavaScript是一种弱类型语言,声明变量时不用指定变量的类型,变量类型由赋予的值所决定。
1 | var web = "ashun.com"; |
1.2 变量提升
代码在执行前会进行预解析,会把var形式的变量的声明提升到当前作用域(window、function)最前。
预解析示例:
我们知道while
是js的关键字,是不能够作为变量名称的,下面代码在解析过程中发现while
不能做为变量名,没有到执行环节就出错了,这是一个很好的预解析过程的体验。
1 | var web = 'ashun'; |
使用 var
声明代码会被提升到前面,按理来说,变量未定义就使用应该报错,但是由于变量提升的缘故,并不会报错。
1 | console.log(a); //undefined |
下面是 if(false)
中定义的var也会发生变量提升,注释掉if
结果会不同
1 | var web = "ashun"; |
1.3 TDZ
TDZ 又称暂时性死区,指变量在作用域内已经存在,但必须在let/const
声明后才可以使用。
TDZ可以让程序保持先声明后使用的习惯,让程序更稳定。
- 变量要先声明后使用
- 建议使用let/const 而少使用var
使用let/const
声明的变量在声明前存在临时性死区(TDZ)使用会发生错误
1 | console.log(x); // Cannot access 'x' before initialization |
在run
函数作用域中产生TDZ,不允许变量在未声明前使用。
1 | as = "Ashuntefannao"; |
函数参数的解析,自左向右,下面代码b没有声明赋值不允许直接使用
1 | function test(a = b, b = 3) {} |
因为a已经赋值,所以b可以使用a变量,下面代码访问正常
1 | function test(a = 2, b = a) {} |
二、块级作用域
var不具有块级作用域,ES6的let、const具有块级作用域。但是它们都具有函数、全局作用域。
2.1 共同点
在父级作用域中声明的变量,能够被子级作用域访问,但子级作用域中的变量不能够被父级访问。
1 | var as = "ashunte"; |
2.2 let
let
、const
都具有块级作用域,会与{ }
形成一个作用域,块级作用域与全局、函数作用域一样:在父级作用域中声明的变量,能够被子级作用域访问,但子级作用域中的变量不能够被父级访问。
但let
、const
形成的块级作用域所定义的变量,不会污染全局, 并且let
、const
定义的变量全局不会添加到window
中。
1 | { |
每一层都是独立作用域,里层作用域可以声明外层作用域同名变量,但不会改变外层变量
1 | function run() { |
–var不具有块级作用域,使用 var
声明的变量存在于最近的函数或全局作用域中。
没有块作用域很容易污染全局,下面函数中的变量污染了全局环境
1 | function run() { |
注意:上面示例的代码中,web变量没有使用var声明,如果使用var声明,就会报错。
使用var声明报错的原因:
如果使用var声明了web变量,证明web变量属于run函数的作用域。所以外部作用域不能访问该变量。
没有使用var声明:
证明使用的是全局变量,变量提升在全局作用域,因此在函数外部(全局作用域)能够访问。
1 | var web;//变量提升过程 |
这个问题虽然说出来很理所当然,但是在一开始可能很难get到这个点。
–没有块作用作用域时var
在for循环
中也会污染全局
1 | for (var i = 0; i < 10; i++) { |
–var
全局声明的变量也存在于 window
对象中
1 | var as = "ashun"; |
–使用let、const
全局声明的变量不会添加到window
中
1 | let as = "ashun"; |
2.3 const
使用 const
用来声明常量,这与其他语言差别不大,比如可以用来声明后台接口的URI地址。
常量名建议全部大写
const常量只能声明一次
声明时必须同时赋值
不允许再次全新赋值
拥有块、函数、全局作用域
可以修改
引用类型
变量的值1
2
3
4
5
6const INFO = {
url: 'https://www.ashuntefannao.com',
port: '8080'
};
INFO.port = '443';
console.log(INFO); //443Object.freeze
如果冻结变量后,变量也不可以修改了,若使用严格模式还会报出错误。
1
2
3
4
5
6
7
8"use strict";
const INFO = {
url: "https://www.ashun.com",
port: "8080",
};
Object.freeze(INFO);
INFO.port = "443"; //Cannot assign to read only property
console.log(INFO);
2.4 重复定义
- 使用
var
定义变量,可在同作用域
下定义同名变量进行覆盖。 - 使用
let、const
定义的变量,不可在同作用域
下定义同名变量
使用 var 可能造成不小心定义了同名变量
1 | //优惠价 |
使用let
可以避免上面的问题,因为let声明后的变量不允许在同一作用域中重新声明
1 | let web = 'astfn.github.io'; |
不同作用域可以重新声明
1 | let web = "ashun.com"; |
三、 严格模式
严格模式(“use strict”),可以定义在script标签对顶部定义(范围为该标签对内),也可以在函数体顶部定义(范围为该函数内部)。
严格模式的使用,可以使我们的代码更加的规范,所以强力推荐使用"use strict"
3.1 不同点
变量必须
先定义后使用
函数形参名称不能重复
关键词不允许做变量使用
单独为函数设置严格模式
1
2
3
4
5
6
7function strict(){
"use strict";
return "严格模式";
}
function notStrict() {
return "正常模式";
}为了在多文件合并时,防止全局设置严格模式对其他没使用严格模式文件的影响,将脚本放在一个执行函数中。
1
2
3
4(function () {
"use strict";
url = 'ashun.com';
})();解构差异
非严格模式可以不使用声明指令,严格模式下必须使用声明。所以建议使用 let 等声明语法进行声明。
1
2{name,url} = {name:'ashun',url:'ashuntefannao.com'};
console.log(name, url); //ashun ashuntefannao.com1
2
3"use strict";
{name,url} = {name:'ashun',url:'ashuntefannao.com'};
console.log(name, url); //Uncaught ReferenceError: age is not defined1
2
3"use strict";
const {name,url} = {name:'ashun',url:'ashuntefannao.com'};
console.log(name, url); //ashun ashuntefannao.com
四、运算符与流程控制
在JavaScript中,与其他语言一样,都具有常用的运算符、流程控制语法。这里不再对其进行赘述,只对比较特殊的运算符、流程控制语法进行详细的解释。
4.1 运算符
- 赋值运算符(=)
- 算数运算符(+、-、*、/、%)
- 复合运算符(+=、-=、*=、/=、%=)
- 一元运算符(++、–)
- 比较运算符(>、>=、<、<=、==、===)
- 逻辑运算符(&&、||、!)
4.2 流程控制
if、else、else if
switch
while
do/while
for
break/continue
三元表达式
for/in
for/in循环能够遍历Array,Object的
key
。1
2
3
4let arr = ["A", "s", "h", "u", "n"];
for (let i in arr) {
console.log(i); //0~4
}1
2
3
4let obj = { baseURL: "http://ashuntefannao.com", port: 8081 };
for (let i in obj) {
console.log(i); //baseURL port
}–遍历window对象的所有属性
1
2
3for (name in window) {
console.log(window[name]);
}for/of
for/of,能够遍历所有的
可迭代
数据结构,例如:Dom元素、Array、String、Set、Map。但是Object、WeakSet、WeakMap是不可迭代的,因此也就不能够使用for/in进行遍历
1
2
3
4let arr = [1, 2, 3];
for (const iterator of arr) {
console.log(iterator);
}1
2
3
4let str = 'Ashun';
for (const iterator of str) {
console.log(iterator);
}--使用迭代特性遍历数组,后期在迭代器部分会详解。
1
2
3
4
5let site = ["ashun", "Ashuntefannao"];
for (const [key, value] of site.entries()) {
console.log(key, value);
}
//0 ashun 1 Ashuntefannao使用
for/of
也可以用来遍历DOM元素1
2
3
4
5
6
7
8
9
10
11
12
13
14<body>
<ul>
<li></li>
<li></li>
</ul>
</body>
<script>
let lis = document.querySelectorAll("li");
for (const li of lis) {
li.addEventListener("click", function() {
this.style.backgroundColor = "red";
});
}
</script>lable
标签(label) 为程序定义位置,可以使用
continue/break
跳到该位置。下面取
i+n
大于15时退出循环1
2
3
4
5
6
7
8
9
10
11ashuntefan: for (let i = 1; i <= 10; i++) {
ashun: for (let n = 1; n <= 10; n++) {
if (n % 2 != 0) {
continue ashun;
}
console.log(i, n);
if (i + n > 15) {
break ashuntefan;
}
}
}