JavaScript语句整理

344 阅读5分钟

在JavaScript标准中,把语句分成了两种:声明和语句。

普通语句

  • 语句块

  • 空语句

  • 表达式语句

  • if语句

  • switch语句

  • 循环语句

    • for循环
    • for in循环
    • for of循环
    • for await of循环
    • while循环
    • do while循环
  • return语句

  • break语句

  • continue语句

  • with语句

  • throw语句

  • try语句

  • debugger语句

声明型语句

  • var语句
  • let语句
  • const语句
  • class声明
  • 函数声明
    • 普通函数声明
    • async函数声明
    • generator函数声明
    • async generato函数声明

语句块

语句块就是一对大括号。让我们把多行语句是为同一行语句,这样,if、for等语句定义起来就比较简单。 需要注意语句块会形成作用域

{
    let x = 1;
}
console.log(x); //error

let声明仅仅对语句块作用域生效,在作用域外访问x就会报错。

空语句

空语句就是一个分号,仅仅用于语言设计完备性,允许插入多个分号而不抛出错误。

;

if语句

if 语句比较熟悉,if () {} else if () {} else {}就是可以分支。

switch语句

switch是继承自Java,Java继承自C和C++, switch是跳转的变形,如果要分支,就必须加上break。在C时代,switch是性能是略优于if else的,对JS并无本质区别。

循环语句

while循环和 do while 循环
let a = 100;
while (a--) {
    console.log("*");
}
let a= 101;
do {
    console.log(a);
} while (a < 100)

do while循环无论如何至少执行一次。

普通 for 循环
for in 循环

for in 循环枚举对象的属性,体现了enumerable特征。

let o = {a: 10, b: 20};
Object.defineProperty(o, "c", {enumerable: false, value: 30})

for(let p in o)
    console.log(p); //a b
for of循环和 for await of 循环
for (let e of [1, 2, 3, 4, 5]){
    console.log(e)
}
// 1 2 3 4 5

背后的机制是iterator机制。可以给一个对象添加iterator,使它可以用于for of语句,如下:

let o = {
    [Symbol.iterator]: () => ({
        _value: 0,
        next() {
            if (this._value = 10) {
                return {
                    done: true
                }
            }
            else return {
                value: this._value++,
                done: false
            };
        }
    })
}
for(let e of o)
    console.log(e);

这段代码展示了如何为一个对象添加iterator。但是实际操作的时候,不需要定义iterator,可以使用generator function。

function* foo() {
    yield 0;
    yield 1;
    yield 2;
    yield 3;
} 
for (let e of foo()) {
    console.log(e);
}
// 0 1 2 3

上面代码展示了generator function和foo的配合。

return

return用户函数中,终止函数的执行,并且制定函数的返回值。

function squre(x) {
    return x * x;
}

break语句和continue语句

这两个都是控制型语句,用法比较相似。需要注意的是,他们都可以有带标签的用法。

outer: for(let i = 0; i < 100; i++) 
    inner: for(let j = 0; j < 100; j++)
        if (i == 50 && j == 50)
            break outer;

outer: for(let i = 0; i < 100; i++)
    inner: for(let j = 0; j < 100; j++)
        if ( i >= 50 && j == 50)
            continue outer;

带标签的break和continue可以控制自己被外层的哪个语句消费,可以跳出复杂的语句结构。

with语句

let o = {a: 1, b: 2};
with(o) {
    console.log(a, b);
}

with语句把对象的属性在内部作用域内变成了变量。

try语句和throw语句

try语句和throw语句用于处理异常,他们是配合使用的。在大型应用中,异常机制非常重要。

try {
    throw new Error("error");
} catch(e) {
    console.log(e);
} finally {
    console.log("finally");
}

try语句捕获异常,throw抛出异常,可以在try语句的结构中被处理掉: try部分用于标识捕获异常后的代码段,catch部分用于捕获异常后做一些处理,finally用于执行后做一些必须执行的清理工作。即使try中出现了return, finally中的语句也一定要被执行。

debugger语句

通知调试器在此断点。

----------------------------------------------------------------------

普通语句和声明型语句的交界线

----------------------------------------------------------------------

var

var声明是古典的JS声明变量的方式,如今let和const都是更好的选择。 如果仍然使用var,把它当做一种“保障变量是局部”的逻辑,遵循以下三条规则:

  • 声明同时必定初始化
  • 尽可能在离使用的位置近处声明
  • 不要在意重复声明

let 和 const

let和const 看起来都是执行到了才生效,但是应该预处理过。

const a = 20;
if (true) {
    console.log(a);
    const a = 2;
}
// error. Cannot access 'a' before initialization

执行console.log的时候,如果const 声明没有被预处理,就不该报错。在执行的时候已经知道后面代码有变量a,于是不允许访问外层作用域的a。(暂时性死区,TDZ)

class声明

class内部可以使用constructor关键字定义构造函数, getter, setter方法。默认内部的函数是strict模式的。

函数声明

函数声明有几种类型

function foo() {}

funtion* foo() {
    yield 1;
    yield 2;
}

async function foo() {
    await sleep(3000)
}

async function* foo() {
    await sleep(3000);
    yield;
}

带*的函数是generator,生成器函数可以理解为返回一个序列的函数,底层是iterator机制。 async函数是可以暂停执行,等待异步操作的函数,底层是Promise机制。异步生成器是两者的结合。

重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏, 每天10分钟,重构你的前端知识体系。在学习了JavaScript语句这一章节以后,抄录归纳了这些内容。