JS 基础语法
本文参考内容 https://www.bilibili.com/video/BV1fGBFBZE6f/
行末分号
以下字符开头的代码,前一行行末不能省略分号,可以在行首加上
[(方括号)((圆括号)`(反引号)/(斜杠)+,-等一元或二元运算符
以下是部分常见的行首增加分号的情况
- 使用 ES6 解构对变量进行交换
// 数组乱序 const arr = [1, 2, 3, 4, 5, 6, 7] for(let i = arr.length - 1; i > 0 ; i--) { const j = Math.floor(Math.random() * (i+1)) // 0-i 的一个索引值,实现可能交换或不交换 ;[arr[j], arr[i]] = [arr[i], arr[j]] // 数组值交换,类似 py - 此处由于开头满足条件,在行首使用分号 } - 自执行函数
;(function(){ console.log(1) })() ;(function(){ console.log(1) }()) - 非数组对象调用数组方法
let res = '' const str = 'abc' ;[].forEach.call( // `[` 开头,需要分号 str, c => {res += "\\x" + c.charCodeAt(0).toString(16)} ) console.log(res)
作用域
JS 中的作用域说明
代码块
在介绍作用域之前,先了解代码块。以下是三种不同类型的代码块
普通代码块
- 可以在任何位置使用代码块。以下代码可以正常运行
{ console.log(1) } - 可以随意嵌套代码块。以下代码也可正常运行
{ { { { { console.log(2) } } } } }
- 可以在任何位置使用代码块。以下代码可以正常运行
语句代码块
for(i = 0; i < 10; i++) { // for语句的代码块 console.log(i) }函数代码块: 函数在声明时也使用了
{}。函数的块是块的特例// 函数每次调用时,会开辟新的空间。因此 **函数内声明的变量** 每次都是全新的。多次调用函数时,变量互不影响 function test(input) { const a = input console.log(a) } test(1) // 创建 input,创建了 a 接收 input 的值,调用结束后销毁 test(2) // 创建了新的 input 与 a
变量作用域
const与let声明的变量作用域在其所在的代码块中(代码块中定义,代码块结束时销毁)。如果没有在代码块中,则为全局作用域- 例1: 普通代码块
{ const a = 1; // 出了该定义语句所在块,则该声明失效 { const b = 2 ; // 内层块内有效 } // 此时销毁 b console.log(a) // console.log(b) // 这里会报错 } // 此时销毁 a // 本例中 由于该块内的子块也属于该块,所以内层块中 a 也有效 - 例2: 语句代码块
// i 在 for 语句内,且该语句含有代码, i 的作用域就是 for 语句的代码块 for(let i = 0; i < 10; i++) { //与别的 for 语句代码块中的 i 不冲突 console.log(i) } // 此时销毁 i // console.log(i) // 这里会报错 - 例3: 代码块内不可重复定义
// 重复声明报错 let a = 1 let a = 2 // 报错:Identifier 'a' has already been declared
- 例1: 普通代码块
var定义变量。特性:1. 作用域至少为函数语句块 2. 升格到作用域头部 3. 可重复声明同一变量,会覆盖值- 例1: 函数代码块
function testVar() { console.log(a) { var a = 1 console.log(a) } console.log(a) } // 由于 var 的作用域是 函数,所以 var 声明会跳出非函数的块。变成如下样子 /* function testVar() { var a // 1. 升格到函数作用域开始处 console.log(a) // 3. 还没赋值,输出 undefined { a = 1 // 2. 赋值保留原始位置 console.log(a) } console.log(a) } */
- 例1: 函数代码块
- 普通代码块 与 语句代码块 中无关键字的变量声明都是 全局变量(函数代码块中会报错)
{ g1 = 1 { g2 = 2 } } console.log(g1, g2) // 普通代码块内的全局变量 for (i = 0; i<10; i++){ ; } console.log(i) // 语句代码块内的全局变量
函数作用域
// 1. 变量形式声明的函数作用域符合变量作用域逻辑 // 例如 var 定义函数,就是 var 变量,升格,但赋值位置不变 function func() { console.log(test) // 由于升格会输出 undefined var test = function () {} } func()
// 2. function 关键字定义的函数,作用域类似 var,至少为函数代码块,且升格到作用域头部
// 2.1 函数内容(赋值)如果执行,也会升格 { function func() { // func 会升格到全局 console.log(test) // 由于升格会正常输出一个函数对象,且有函数体,即使定义在后方. console.log(test()) // 由于升格,可正常执行 test 内的 console.log(a) function test() { const a = 1 console.log(a) } } } func() // 此处可正常调用func
// 2.2 函数内容未执行的升格,该函数声明无法执行 if (false) { // func 会升格,但该函数声明不被执行,因此 func 无法完成赋值 function func() {const b = 2} } console.log(func) // 同 var 变量,输出 undefined
作用域链
观察以下示例代码
const x = 1
{ // block 1
const a = 1
{ // block 2
const a = 2
{ // block 3
const b = 3
for (i = 1; i < 10; i++){ // block 4
const c = 4
console.log(i, a, b, c, d, x, y)
}
}
}
}
说明: 当代码执行到 console.log(a, b, c, d, x, y) 时,解释器会先在其所在的作用域,即 block 4 中寻找对应变量。如果没有,则往父层作用域中寻找,依次向上,直至全局作用域,寻找目标变量。找到则停止该变量的查找,找不到则报错未定义
本例中几个特殊变量
- y: 一直往上无法找到。则报错未定义
- a: 由于依次往上,先找到值为 2 ,因此 a 会是 2。即使
a = 1对子层来说也是作用域,但a = 2会被先找到
此处依次向上查找的过程 block 4 -> block 3 -> block 2 -> block 1 -> 全局 就是作用域链
代码执行到任何位置都会有作用域链,此时可访问作用域链上的所有变量(同名时,内层会覆盖外层,例如本例中指定到最里边时的 a 变量)