重学JavaScript(2)

80 阅读5分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第12天

语法规则

变量命名

区分大小写的,test和Test是不同的变量

标识符

变量、函数、属性或函数参数的名称,其组成规则是:第一个字符必须是字符,下划线或美元符号;剩下的其它字符可以是字母,下划线,美元符号或数字。标识符按照惯例采用小驼峰式明明,即首字母小写,后面每个单词首字母大写。关键字、保留字、true、false、null不能作为标识符

注释

单行注释:

// 单行注释

多行注释

/*
 * 这是多行注释
*/

严格模式

ECMAScript 5中增加的概念,JavaScript的一种解析和执行模型,对于ECMAScript 3的一些不规范的写法进行处理,抛出不安全的活动错误。

启用方式:在脚本开头加上"use strict",这是一个预处理指令。

单独指定一个函数在严格模式下进行:

function doSomething() {
    "use strict";
    // 函数体
}

关键字和保留字

关键字:

break, do, in, typeof, case, else, instanceof, var, catch, export, new, void, class, extends, return, while, const, finally, super, with, continue, for, switch, yield, debugger, function, this, default, if, throw, delete, import, try

关键字指的是ECMA-262中有特殊用途的单词,比如表示控制语句的开始和结束,或执行特定的操作。关键字不能用作标识符或属性名。

保留字

enum(始终保留), implements, package, public, interface, protected, static, let, private(严格模式下保留), await(模块代码中保留)

保留字同样不能用来做标 识符但可以做对象属性名,但不建议。保留字是保留下来给将来做关键字的

变量

ECMAScript变量是松散类型的,即变量可以用来保存任何类型的数据,每个变量是一个用于保存人一直的命名占位符。声明边浪的方式:var,const,let

var

var message

上述代码即声明了一个变量,此时,变量的值是undefined的,因为没有定义初始值。

var message = "hi"

上述代码即对变量进行了赋值,即初始化。这种方式并不会决定变量的类型,后续更改,甚至可以赋值为其他数据类型的值,但不推荐这样做。

var的作用域

使用var在函数内部声明的变量,是局部变量,在函数退出时会被销毁。在定义变量时,省略var操作符,不管是全局作用域内,还是函数作用域内,该变量都是全局变量。但是这种方法,会使得变量变得难以维护。

在同时定义多个变量是,可以使用逗号将其隔开:

var message = "hi",
    found = false,
    age = 18;

var的作用域提升

先看一段代码:

function foo() {
    console.log(age);
    var age = 18;
}
foo(); // undefined

上述代码是可以成功执行,不会报错的,这是因为var声明的变量会有作用域提升,也就是说,var声明的变量会自动提升到函数作用域顶部,等价于:

function foo() {
    var age;
    console.log(age);
    age = 18;
}
foo();

另外,使用var来声明变量,是可以多次声明同一个变量的。

let

let和var的作用相似,不同在于,let没有变量提升(暂时性死区:在解析代码时,JavaScript引擎也会注意出现在块后面的let声明,只不过在此之前不能以任何方式来引用未声明的变量。在let声明之前的执行瞬间被称为“暂时性死区”),并且声明的范围是块级作用域,var是函数作用域。

if(true) {
    var name = "Matt";
    console.log(name); // Matt
}
console.log(name); // Matt
------
if(true) {
    let age = 18;
    console.log(age); // 18
}
console.log(age); // ReferenceError: age未定义
// 这里age的作用域,仅限于花括号内,也就是块级作用域内。

块级作用域是函数作用域的子集,适用于var的作用域限制也适用于let。

let不允许声明同一个变量。但是在不同的块级作用域内,是可以的

var name = 'Nicholas';
console.log(name); // 'Nicholas'
if (true) {  
    var name = 'Matt';
    console.log(name);   // 'Matt' 
}
let age = 30;
console.log(age); // 30
if (true) {
    let age = 26;
    console.log(age);   // 26 
}

let全局声明

let在全局作用域中声明的变量不会成为window队形的属性(var会),但是let声明也能在全局中使用,所以要确保使用let时,页面中不存在重复声明的变量,避免SyntaxError。

for循环中的let声明

使用var在for循环中定义的迭代变量会渗透到循环体外部

    for (var i = 0; i < 5; ++i) {      
    // 循环逻辑 
    }
    console.log(i); // 5

使用let,保证了迭代变量的作用域仅限于for循环块内部

    for (let i = 0; i < 5; ++i) {
        // 循环逻辑 
    }
    console.log(i); // ReferenceError: i没有定义

在使用var时,经常会出现这么一个问题:

for (var i = 0; i < 5; i++) {
    setTimeout(() => console.log(i), 0)
}
// 实际输出结果:5,5,5,5,5

这是因为,在退出循环时,迭代变量保存的是导致循环退出的值5,在之后执行setTimeout时,所有的i都是同一个变量,因而输出的都是同一个最终值。

而使用let时,JS引擎在后台会给每一个迭代循环声明一个新的迭代变量,每个setTimeout引用的变量实例都是不同的,所有console.log输出的是0,1,2,3,4。

for-in和for-of同样适用。

const

const和let大体上是一样的,不同在于声明变量时必须要赋值,且不能修改,也就是定义常量。但是,如果const声明的是一个对象,这个对象内部的属性是可以修改的,因为const声明的限制只适用于它指向的变量的引用。

const不能用在for循环中声明迭代变量,因为迭代变量是会变的,可以用于在for循环中声明一个不会被修改的循环变量。

let i = 0;
for (const j = 7; i < 5; ++i) {
    console.log(j);    
}   
// 7, 7, 7, 7, 7   
for (const key in {a: 1, b: 2}) {      
    console.log(key);    
}   
// a, b    
for (const value of [1,2,3,4,5]) {      
    console.log(value);   
}   
// 1, 2, 3, 4, 5

在声明变量时,优先使用const。如果声明的变量需要修改,则使用let。尽量不要使用var。

参考资料

JavaScript高级程序设计(第4版)