1、语句及语句块
- 每个语句以分号
;
结束,语句块用大括号{...}
。但是,JavaScript并不强制要求在每个语句的结尾加;
,浏览器中负责执行JavaScript代码的引擎会自动在每个语句的结尾补上;
。 - 强力建议: 每个独立的语句都写分号,并且一行只写一个语句;
例如:
1、下面的一行代码就是一个完整的赋值语句:
var x = 1;
2、下面的一行代码是一个字符串,但仍然可以视为一个完整的语句:
'Hello, world';
3、 下面的一行代码包含两个语句,每个语句用;
表示语句结束:
var x = 1; var y = 2; // 不建议一行写多个语句!
- 语句块是一组语句的集合,例如,下面的代码先做了一个判断,如果判断成立,将执行
{...}
中的所有语句:
if (2 > 1) {
x = 1;
y = 2;
z = 3;
}
-
{...}
还可以嵌套,形成层级结构:
if (2 > 1) {
x = 1;
y = 2;
z = 3;
if (x < y) {
z = 4;
}
if (x > y) {
z = 5;
}
}
2、注释
(1)行注释
- 以
//
开头直到行末的字符被视为行注释;
// 这是一行注释
alert('hello'); // 这也是注释
(2)块注释
用/*...*/
把多行字符包裹起来,把一大“块”视为一个注释;
/* 从这里开始是块注释
仍然是注释
仍然是注释
注释结束 */
3、变量与常量
(1)定义变量与变量名
- JavaScript 是一种弱类型语言 ,javascript的变量类型由它的值来决定。
- 定义变量需要用关键字 var或者let;
-
变量名是大小写英文、数字、
$
和_
的组合,且不能用数字开头。变量名也不能是JavaScript的关键字,如if
、while
等;
(2)变量赋值
- 在JavaScript中,使用等号
=
对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var
申明一次;
var a = 123; // a的值是整数123
a = 'ABC'; // a变为字符串
- 这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错
- 要显示变量的内容,可以用
console.log(x)
,打开浏览器的控制台就可以看到结果。
(3)定义常量
- 定义常量用关键字 const`;
- 在ES6引入新特性const定义常量,一旦定义后不能修改,修改则会报错 (扩展-ES6之前定义常量的方式:全部用大写字母定义的变量就是常量,建议不要修改);
(4)变量定义var、let、const的异同请查看下方 “块级作用域”
4、变量作用域
什么是作用域?–通常来说,作用域就是限制一个变量在程序中的使用范围。
(1)全局变量和局部变量
在 JavaScript
中作用域的边界是以函数划分。有 全局 和 局部 作用域之分。
- 全局作用域:声明在全局的变量或者不使用var声明的变量在整个程序中都是可用的,所以叫全局作用域。
- 局部作用域:声明在函数体内的变量,在整个函数执行环境和其子函数内都是可用的,但是在函数外访问不到,所以叫局部作用域。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js用法</title>
</head>
<body>
</body>
</html>
<script>
var global = "我是全局变量,全局都能看到我";
global2 = "我也是全局变量,全局都能看到我";
function getName() {
var name = "我是局部变量,只能在getName函数内才能找到我"
}
console.log(name) // 这一行没有打印任何内容,原因后面再说
console.log(global)
console.log(global2)
console.log(getName)
</script>
(2)预解析和变量提升
-
js代码执行的整个过程:代码加载完成之后,先进行预解析和变量提升,之后才会从上往下执行代码;
-
预解析是在程序执行之前,会进行一遍预检。查找当前作用域内的
function
和var
。并且每次更换作用域都会在此作用域中执行预解析; -
变量提升是指,在查找到
function
和var
后,首先在当前作用域的顶端定义好并赋给默认值。var
的默认值为 undefined,function
的默认值为函数本身。 -
注意: 像 var getName = function() {} 这种代码会被当做变量定义,而不会当做函数定义。
来一个例子加深理解:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js用法</title>
</head>
<body>
</body>
</html>
<script>
console.log(global) // undefined
console.log(getName) // function getName(){}
var global = 1;
function getName () {}
</script>
运行结果:
说明:
1、先执行预解析,查找全局作用域内的变量和函数,找到了变量global
和函数getName
;当执行两个console的时候会将变量进行提升赋给默认值,global
的默认值是undefined,函数getName
的默认值是函数本身,所以出现这样的打印结果;
2、预解析和变量提升的过程中,并不会将变量赋值,而只是定义,等真正执行的时候才会赋值。修改一下代码,在global变量下方再次打印。
var global = 1;
console.log(global); // 1
解释:当执行到打印函数的时候,global
已经被赋值为 1。此时已经在执行代码的阶段 ,而不是在预解析阶段。
再来一个案例说明:猜想一下,下方程序如何输出。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js用法</title>
</head>
<body>
</body>
</html>
<script>
console.log(a); // 1
var a = 1;
console.log(a); // 2
getName();
function getName () {
console.log(a); // 3
console.log(b); // 4
a = 2;
console.log(a); // 5
var b = 3;
console.log(b); // 6
function b(){}
}
</script>
结果:
针对上方代码,通过图解的方式更直观的理解js代码的加载、预解析及执行过程。
(3)作用域链
- 作用域链:就是查找变量或者函数的链路;
- 特别注意:作用域只能从下向上查找,不可逆向,从函数外不能访问函数内的变量。
- 例如:在上面的案例中,getName()函数内,向上查找 a 变量的过程,那个就是作用域链的查找过程。
(4)从全局获取函数内部的变量/从这个函数获取另一个函数的变量
A、从全局获取函数内部的变量
在函数外访问函数内的变量是访问不到的,如果我们坚持要访问呢?—我们可以在函数内将变量返回出来,这样就可以访问到函数内的变量了。
console.log(getA()); // 通过这种方式,我们就可以访问到 a 变量。
function getA() {
var a = 1;
return a;
}
B、从这个函数获取另一个函数的变量
使用闭包:比如,A函数要使用B函数中的变量,在B函数中调用A函数,这样A函数在B函数内部,A函数自然就可以访问到B函数内的具备变量了;
(5)块级作用域
在ES6到来的时候,javascript
迎来一个全新的概念,-- 块级作用域 。顾名思义,块级作用域可以让变量只在一块代码内生效。
比如:
{
var a = 1
}
console.log(a)
上述代码会正常输出,但是下方的代码会抛出 a 变量未定义的错误a is not defined
。
{
let a = 1;
}
console.log(a)
也就是说a变量只在花括号内生效,在花括号外是访问不到的。
可以声明块级作用域的方式有两种。let
和 const
。
A、变量定义var、let、const的异同
相同点: 都可以声明变量。
不同点:
1、var 存在局部作用域,可变量提升,声明的值可更改。
console.log(a)
var a = 1;
a = 2;
// 上述操作都可以
2、let 存在块级作用域,不可变量提升,声明的值可修改。(只可以先声明变量,然后再使用)
console.log(a) // 会报错, a is not defined
let a = 1;
a = 2;
3、const 存在块级作用域,不可变量提升,声明的值本身不可修改(只可以先声明变量,然后再使用)
const a = 1;
a = 2; // 会报错,a不可修改
// 下述情况可运行
const a = []
a[0] = 1;
注意: const声明的叫做常量,不可以修改其本身,但如果声明的是复杂类型的对象,对象里的值是可修改的。
B、为什么let和const不能变量提升
使用let
和const
声明的变量,在预解析 的时候会将变量放入到一个暂时不可访问的区间中–TDZ (暂时性死区),此时访问变量会提示未定义错误,在给变量赋值后,将变量放入到正常的执行环境中。使变量可以正常访问。
console.log(a) // 此时的a在TDZ中
let a = 1; // 将a从TDZ中移出来
console.log(a) // 此时可以正常访问a变量