常识

简单罗列一下

标识符

  • 区分大小写
  • 标识符(变量、函数、属性或函数参数的名称)第一个必须是字母 _ $ ,后面的可以有数字
  • 标识符推荐使用驼峰大小写形式,即第一个单词的首字母小写,后面每个单词的首字母大写,如 firstSecond myCar doSomethingImportant
关键字、保留字、true、false 和 null 等不能作为标识符

代码结构

语句

JavaScript语句之间使用分号分割

alert('Hello'); alert('World');

可以自动添加分号

alert('Hello')
alert('World')

**虽然js大多时候可以自动添加分号
但是!!!
强烈建议所有的代码都手动加上分号**

注释

  • 单行注释: //注释内容
  • 多行注释

    /*注释内容
    这里可以换行
    */

    严格模式

严格模式对正常的 JavaScript语义做了一些更改。
严格模式通过抛出错误来消除了一些原有静默错误。
严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。

具体变化参考Mozilla链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode#%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F%E4%B8%AD%E7%9A%84%E5%8F%98%E5%8C%96

要开启严格模式, 只需在脚本开头加上:

"use strict"; 

也可以只单独指定一个函数在严格模式运行:

function doSomething() { 
 "use strict"; 
 // 函数体 
} 
现代 JavaScript 支持 “class” 和 “module” ,会自动启用 use strict。因此,在使用时这些模式时,则无需添加 use strict 指令。

变量

原始(基本)类型:

  • Number类型
  • BigInt类型(兼容性不佳)
  • String类型
  • Boolean类型
  • null值
  • undefined值
  • Symbol类型(es6)

引用类型:
统称Object类型, 如果要细分的话大概分为以下几种:

  • Object类型
  • Function类型
  • Array类型
  • Date类型
  • RegExp类型
用C语言的指针概念就非常好理解引用类型,这些变量都只是一个地址,指向堆中保存的对象,只是在我们使用的时候js帮我们解引用了.

先简单说说几个特殊的类型,其他的各个类型后面再说

null 值

特殊的 null 值不属于任何一种类型
它构成了一个独立的类型,只包含 null 值:

let age = null;

相比较于其他编程语言,JavaScript 中的 null 不是一个“对不存在的 object 的引用”或者 “null 指针”。

JavaScript 中的 null 仅仅是一个代表“无”、“空”或“值未知”的特殊值。

上面的代码表示 age 是未知的。

undefined 值

特殊值 undefinednull 一样自成类型。

undefined 的含义是 未被赋值。

如果一个变量已被声明,但未被赋值,那么它的值就是 undefined:

let age;

alert(age); // 弹出 "undefined"

从技术上讲,可以显式地将 undefined 赋值给变量:

let age = 100;

// 将值修改为 undefined
age = undefined;

alert(age); // "undefined"

但是不推荐这样做, 通常,使用 null 将一个“空”或者“未知”的值写入变量中,而 undefined 则保留作为未进行初始化的事物的默认初始值。

typeof 运算符

我对 typeof 又爱又恨, 他返回参数的类型. 当我们想要分别处理不同类型值的时候, 或者想快速进行数据类型检验时, 非常有用. 但是他又有非常多的问题:

typeof undefined // "undefined"

typeof 0 // "number"

typeof 10n // "bigint"

typeof true // "boolean"

typeof "foo" // "string"

typeof Symbol("id") // "symbol"

// 下面是typeof需注意的地方
typeof [1,2,3] // "object"  typeof将object类型统称为object,不能更精确的判断

typeof null // "object"  typeof会将null判断为object 这是JavaScript官方承认的错误

typeof alert // "function" 在 JavaScript 语言中没有一个特别的 “function” 类型。
//函数隶属于 object 类型。不过这个特性在一些地方还是挺好用的

总结下来就是:

  1. typeof只能判断基本类型
  2. typeof不能判断null

var关键字

var关键字定义一个变量 随后不仅可以改变这个值, 也可以改变这个值的类型

var message = "hi"; 
message = 100; // 合法,但不推荐

var作用域

使用 var 操作符定义的变量会成为包含它的函数的局部变量。比如,使用 var
在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:

function test() { 
 var message = "hi"; // 局部变量
} 
test(); 
console.log(message); // 出错!

但是如果在函数内我们忘记了这个变量没有定义,没有定义就直接进行赋值, 那个这个变量就会变成全局变量

function test() { 
 message = "hi"; // 全局变量
} 
test(); 
console.log(message); // "hi" 
在严格模式中这么做是不允许的,像这样给未声明的变量赋值, 会导致抛出 ReferenceError

非常不推荐这么做, 这么做会导致全局的变量被污染, 导致发生意外的情况, 对于后人的维护也是灾难

var声明提升

所谓的提升,就是js会把所有变量声明都拉到函数作用域的顶部.即在函数运行前就提前定义好所有var变量

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

等同于:

function foo() { 
 var age; // 提前在顶部声明了
 console.log(age); 
 age = 26; 
} 
foo(); // undefined

同时,反复声明一个var变量也没有问题

function foo() { 
 var age = 16; 
 var age = 26; 
 var age = 36;//以最后一个为准 
 console.log(age); 
} 
foo(); // 36 

let

let就没有这么随性了, 相比var多了一些限制

作用域

最大的区别就是作用域了, let是块级作用域, 而var是函数作用域
var:

if (true) { 
 var name = 'Matt'; 
 console.log(name); // Matt 
} 
console.log(name); // Matt 

let:

if (true) { 
 let age = 26; 
 console.log(age); // 26 
} 
console.log(age); // ReferenceError: age 没有定义

在这里,age 变量之所以不能在 if 块外部被引用,是因为它的作用域仅限于该块内部(if).

虽然这里举例使用的if, 但是非常不推荐条件声明, 这会使程序变得非常难以理解.

let也不能冗余声明

var name; 
var name; 
let age; 
let age; // SyntaxError;标识符 age 已经声明过

暂时性死区

let 与 var 的另一个重要的区别,就是 let 声明的变量不会在作用域中被提升

// name 会被提升
console.log(name); // undefined 
var name = 'Matt'; 
// age 不会被提升
console.log(age); // ReferenceError:age 没有定义
let age = 26; 

在解析代码时,JavaScript 引擎也会注意出现在块后面的 let 声明,只不过在此之前不能以任何方
式来引用未声明的变量。在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此
阶段引用任何后面才声明的变量都会抛出 ReferenceError .

全局声明

与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声
明的变量则会)。

var name = 'Matt'; 
console.log(window.name); // 'Matt' 
let age = 26; 
console.log(window.age); // undefined

for循环中使用let变量

在 let 出现之前,for 循环使用var定义的迭代变量会渗透到循环体外部:

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

使用let之后

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

还有一个很常见的例子:

for (var i = 0; i < 5; ++i) { 
 setTimeout(() => console.log(i), 0) 
} 
// 你可能以为会输出 0、1、2、3、4 
// 实际上会输出 5、5、5、5、5 

之所以这样, 是因为在之后的超时中, 打印的所有的i都是同一个变量,退出循坏时, 迭代变量保存的是迭代最后退出的值, 因而输出的都是同一个变量最终值5.

而使用let就符合就不会这样,符合人类直觉

for (let i = 0; i < 5; ++i) { 
 setTimeout(() => console.log(i), 0) 
} 
// 会输出 0、1、2、3、4 

这是因为在使用 let 声明迭代变量时,js会为每个迭代循环声明一个新的迭代变量。
每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循
环执行过程中每个迭代变量的值

这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括 for-in 和 for-of
循环

let的这些特性使得js更像一门"语言"了,更加完善,更加符合人类的直觉,因此强烈建议在后面的开发中抛弃var,全面拥抱let !

const

const行为与let基本相同, 唯一的区别就是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误。毕竟是 常量

const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,
那么修改这个对象内部的属性并不违反 const 的限制。

即const 声明的限制只适用于基本类型

const age = 26; 
age = 36; // TypeError: 给常量赋值

最佳实践

  1. 不使用var
    除非特殊需要, 绝大部分人都不需要var了, 限制自己只使用 letconst 有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。
  2. const优先,let次之
    使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作, 这样可以更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为。
最后修改:2022 年 05 月 06 日
如果觉得我的文章对你有用,请随意赞赏