常识
简单罗列一下
标识符
- 区分大小写
- 标识符(变量、函数、属性或函数参数的名称)第一个必须是
字母
_
$
,后面的可以有数字 - 标识符推荐使用驼峰大小写形式,即第一个单词的首字母小写,后面每个单词的首字母大写,如 firstSecond myCar doSomethingImportant
关键字、保留字、true、false 和 null 等不能作为标识符
代码结构
语句
JavaScript语句之间使用分号分割
alert('Hello'); alert('World');
可以自动添加分号
alert('Hello')
alert('World')
**虽然js大多时候可以自动添加分号
但是!!!
强烈建议所有的代码都手动加上分号**
注释
- 单行注释:
//注释内容
多行注释
/*注释内容 这里可以换行 */
严格模式
严格模式对正常的 JavaScript语义做了一些更改。
严格模式通过抛出错误来消除了一些原有静默错误。
严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。
要开启严格模式, 只需在脚本开头加上:
"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 值
特殊值 undefined
和 null
一样自成类型。
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 类型。不过这个特性在一些地方还是挺好用的
总结下来就是:
- typeof只能判断基本类型
- 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: 给常量赋值
最佳实践
- 不使用
var
除非特殊需要, 绝大部分人都不需要var
了, 限制自己只使用let
和const
有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。 const
优先,let
次之
使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作, 这样可以更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为。