Python测开28期-偕行-JavaScript基本语法

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的关键字,如ifwhile 等;

(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>

image

(2)预解析和变量提升

  • js代码执行的整个过程:代码加载完成之后,先进行预解析和变量提升,之后才会从上往下执行代码;

  • 预解析是在程序执行之前,会进行一遍预检。查找当前作用域内的 functionvar 。并且每次更换作用域都会在此作用域中执行预解析

  • 变量提升是指,在查找到 functionvar 后,首先在当前作用域的顶端定义好并赋给默认值var的默认值为 undefinedfunction的默认值为函数本身

  • 注意:​ 像 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>

运行结果:
image

说明:
1、先执行预解析,查找全局作用域内的变量和函数,找到了变量global函数getName;当执行两个console的时候会将变量进行提升赋给默认值,global的默认值是undefined函数getName的默认值是函数本身,所以出现这样的打印结果;
2、预解析和变量提升的过程中,并不会将变量赋值,而只是定义,等真正执行的时候才会赋值。修改一下代码,在global变量下方再次打印。

var global = 1;
console.log(global); // 1

image

解释:当执行到打印函数的时候,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>

结果:
image

针对上方代码,通过图解的方式更直观的理解js代码的加载、预解析及执行过程。

(3)作用域链

  • 作用域链:就是查找变量或者函数的链路;
  • 特别注意:作用域只能从下向上查找,不可逆向,从函数外不能访问函数内的变量。
  • 例如:在上面的案例中,getName()函数内,向上查找 a 变量的过程,那个就是作用域链的查找过程。
    image

(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变量只在花括号内生效,在花括号外是访问不到的。

可以声明块级作用域的方式有两种。letconst

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不能变量提升

使用letconst 声明的变量,在预解析 的时候会将变量放入到一个暂时不可访问的区间中–TDZ (暂时性死区),此时访问变量会提示未定义错误,在给变量赋值后,将变量放入到正常的执行环境中。使变量可以正常访问。

console.log(a) // 此时的a在TDZ中
let a = 1;     // 将a从TDZ中移出来
console.log(a) // 此时可以正常访问a变量