语法
标识符
标识符指的是用来识别各种值的合法名称
- 第一个字符,可以是任意 Unicode 字母(包括英文字母和其他语言的字母),以及美元符号($)和下划线(_)
- 第二个字符及后面的字符,除了 Unicode 字母、美元符号和下划线,还可以用数字 0-9
JavaScript 有一些保留字,不能用作标识符
变量
声明
let。声明块级作用域的变量const。声明不可变的常量,但实际上如果声明一个对象,这个对象本身还是可以修改属性的var。声明函数作用域的变量
var 会存在变量声明提升,let、const 则不存在变量声明提升
let/const 在进入块级作用域后,会因为提升的原因先创建,但不会被初始化,直到声明语句执行的时候才被初始化,初始化的时候如果使用 let 声明的变量没有赋值,则会默认赋值为 undefined,而 const 必须在初始化的时候赋值。而创建到初始化之间的代码片段就形成了暂时性死区
Q&A
下面的输出是什么,如何修改才能正确输出 0-4?
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
// 输出: 5, 5, 5, 5, 5
修改方案 1:将 var 替换为 let(利用块级作用域)。
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
原理解析(伪代码): 为什么
let可以,而var不行? 因为var是函数作用域/全局作用域,循环 5 次实际上修改的是同一个内存地址的i。而let是块级作用域,JS 引擎在每次循环时,都会为let重新创建一个新的词法环境(Lexical Environment),把当前的i值保存下来供闭包使用。引擎底层的处理逻辑类似于下面的伪代码:// var 的底层情况:全局只有一个 i
var i = 0;
setTimeout(() => console.log(i), 0);
i = 1;
setTimeout(() => console.log(i), 0);
// ...当所有 setTimeout 真正执行时,i 早就变成了 5
// let 的底层情况:每次循环都生成了独立的作用域块
{
let i = 0;
setTimeout(() => console.log(i), 0);
}
{
let i = 1;
setTimeout(() => console.log(i), 0);
}
{
let i = 2;
setTimeout(() => console.log(i), 0);
}
// ...当 setTimeout 执行时,它们沿着各自的块级作用域链向上找,找到了当时被锁住的独立变量 i
修改方案 2:使用 IIFE(立即调用的函数表达式)形成闭包。
for (var i = 0; i < 5; i++) {
(function (j) {
setTimeout(() => {
console.log(j);
}, 0);
})(i);
}
关键字
- this。指向函数的执行上下文
- function。定义函数
- class。定义类
- async function。 定义一个异步函数表达式
- await。暂停或恢复执行异步函数,并等待
promise的resolve/reject回调 - new。创建构造函数实例
面试高频:箭头函数与普通函数的区别
在 ES6 中引入了箭头函数(Arrow Function),它不仅仅是普通函数的语法糖,在底层行为上有极其严格的区别。这是前端面试中几乎必问的考点。
| 特性 | 普通函数 (function) | 箭头函数 (() => {}) |
|---|---|---|
this 的指向 | 动态绑定。由调用方式决定(如作为对象方法调用、call/apply、new 等)。 | 静态绑定(词法作用域)。没有自己的 this,始终继承自外层执行环境的 this,且永远无法被 call/apply/bind 改变。 |
arguments 对象 | 有自己的 arguments 类数组对象,包含所有传入的参数。 | 没有 arguments 对象。如果要获取不定参数,必须使用剩余参数(...args)。 |
作为构造函数 (new) | 可以被 new 调用,生成实例对象。 | 不能被 new 调用,会抛出 TypeError。因为箭头函数没有 [[Construct]] 方法和 prototype 属性。 |
prototype 属性 | 有 prototype 属性。 | 没有 prototype 属性。 |
yield 关键字 | 可以作为 Generator 函数使用(function*)。 | 不能用作 Generator 函数,内部不能使用 yield 关键字。 |
常见陷阱题:
const obj = {
name: "Jackson",
sayHi: () => {
console.log(this.name);
},
};
obj.sayHi(); // 输出什么?答案:输出
undefined(或全局的 name)。因为对象字面量{}不产生作用域,箭头函数的this会向上寻找到全局作用域(浏览器中是window),而不是obj。如果要在对象方法中使用this,必须使用普通函数。
.和 new 的优先级 new 后边紧跟对象,new 的优先级低;new 后边紧跟构造函数执行,new 的优先级高
new Foo.getName()中.的优先级高于newnew Foo().getName()中new Foo()优先级更高
表达式
- 属性访问符(
obj.a、object["a"]) - /ab+c/i。正则表达式
- {}。对象初始化
- []。数组初始化
运算符
- 算术
+、-、*、/和% - 赋值
= - 复合,如
+=、-=、*=、/= - 自增减
++、-- - 比较
>、<、<=、>=、==、!=、===、!== - 逻辑
&&、||、!、?? - 三元
?: - 展开运算符
...obj
+ 有一些额外的作用:
- 连接字符串(
5+'') - 转换为 Number(
+'5'、+false)
&& 和 || 也有一些额外的作用
- 短路逻辑
let name = a || 'default'
以上只是罗列了一些常用表达式和运算符,更详细的参考 表达式和运算符
语句
- 条件
if/else if/else、switch - 循环
for、while、do-while、for...in、label、break、continue - 异常捕获
try/catch/finally
注释
- 单行
// - 多行
/** **/