JavaScript 应用程序是由许多语法正确的语句组成的。单个语句可以跨多行。如果每个语句用分号隔开,那么多个语句可以在一行中出现。
**语句和声明(按类别分类)
控制流程 Block 一个块语句可以用来管理零个或多个语句。该区块是由一对大括号分隔. 语法: 块声明:{ statement } 标记块声明: LabelIdentifier: { StatementList }
通过var声明的变量或者非严格模式下 (non-strict mode) 创建的函数声明没有块级作用域。
1 2 3 4 5 var x = 1 ;{ var x = 2 ; } console .log(x);
使用let和const声明的变量是有块级作用域的
1 2 3 4 5 let x = 1 ;{ let x = 2 ; } console .log(x);
1 2 3 4 5 const c = 1 ;{ const c = 2 ; } console .log(c);
使用function
函数声明同样被限制在声明他的语句块内:
1 2 3 4 5 6 7 foo('outside' ); { function foo (location ) { console .log('foo is called ' + location); } foo('inside' ); }
break 终止当前循环、switch或label语句,使程序跳到下一条语句执行。 语法:break [label] label 可选,与语句标签相关联的标识符。如果 break 语句不在一个循环或 switch 语句中,则该项是必须的。
终止循环:
1 2 3 4 5 6 7 8 9 10 11 12 function testBreak (x ) { var i = 0 ; while (i < 6 ) { if (i == 3 ) { break ; } i += 1 ; } return i * x; }
终止switch语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 const food = "sushi" ;switch (food) { case "sushi" : console .log("Sushi is originally from Japan." ); break ; case "pizza" : console .log("Pizza is originally from Italy." ); break ; default : console .log("I have never heard of that dish." ); break ; }
终止label语句: 一个 break 语句必须内嵌在它引用的标记中.
1 2 3 4 5 6 7 8 block_1:{ console .log ('1' ); break block_2; } block_2:{ console .log ('2' ); }
1 2 3 4 5 6 7 8 9 10 outer_block:{ inner_block:{ console .log ('1' ); break outer_block; console .log (':-(' ); } console .log ('2' ); }
break 语句不能在 function 函数体中直接使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function testBreak (x ) { var i = 0 ; while (i < 6 ) { if (i == 3 ) { (function ( ) { break ; })(); } i += 1 ; } return i * x;} testBreak(1 );
1 2 3 4 5 6 block_1: { console .log('1' ); ( function ( ) { break block_1; })(); }
continue 终止执行当前或标签循环的语句,执行下一个迭代循环 语法:continue [label]
1 2 3 4 5 6 7 8 9 10 let text = '' ;for (let i = 0 ; i < 10 ; i++) { if (i === 3 ) { continue ; } text = text + i; } console .log(text);
与 break 语句的区别在于,continue 并不会终止循环的迭代,而是:
在while循环中,控制流跳转回条件判断
在for循环中,控制流跳转到更新语句
Empty ; 空语句用来表示没有语句的情况,尽管js语法期望有语句提供。
if…else 如果指定的条件是 true,则执行相匹配的一个语句,若为 false,则执行另一个语句。
switch 计算表达式,将子句于表达式的值做匹配,执行与该值相关联的语句。 使用 严格运算符 (en-US),===做匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 switch (expression) { case value1: [break ;] case value2: [break ;] ... case valueN: [break ;] [default : [break ;]] }
如果你忘记添加 break,那么代码将会从值所匹配的 case 语句开始运行,然后持续执行下一个 case 语句而不论值是否匹配。 可以把 default 放到 case 之间
switch语句内的块级作用域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const action = 'say_hello' ;switch (action) { case 'say_hello' : let message = 'hello' ; console .log('0 ~5' ); break ; case 'say_hi' : let message = 'hi' ; case 6 : console .log('6' ); break ; default : console .log('Empty action received.' ); break ; }
导致这一问题的根本原因在于两个 let 语句处于同一个块级作用域,所以它们被认为是同一个变量名的重复声明。
通过把 case 语句包装到括号里面,我们就可以轻松解决这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const action = 'say_hello' ;switch (action) { case 'say_hello' : { let message = 'hello' ; console .log('0 ~5' ); break ; } case 'say_hi' :{ let message = 'hi' ; } case 6 : { console .log('6' ); break ; } default :{ console .log('Empty action received.' ); break ; } }
throw 抛出一个用户定义的异常。 当前函数的执行将被停止(throw 之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个 catch 块。如果调用者函数中没有 catch 块,程序将会终止。
语法:throw expression expression: 要抛出的表达式
1 2 3 4 5 6 7 8 9 10 11 12 function getRectArea (width, height ) { if (isNaN (width) || isNaN (height)) { throw new Error ('Parameter is not a number!' ); } } try { getRectArea(3 , 'A' ); } catch (e) { console .error(e); }
try…catch 标记一个语句块,并指定一个应该抛出异常的反馈。
try语句包含了由一个或者多个语句组成的try块,和至少一个catch块或者一个finally块的其中一个,或者两个兼有,下面是三种形式的try声明: 1.try…catch 2.try…finally 3.try…catch…finally
1 2 3 4 5 6 7 try { nonExistentFunction(); } catch (error) { console .error(error); }
catch子句包含try块中抛出异常时要执行的语句。如果在try块中有任何一个语句(或者从try块中调用的函数)抛出异常,控制立即转向catch子句。如果在try块中没有异常抛出,会跳过catch子句。
finally子句在try块和catch块之后执行但是在下一个try声明之前执行。无论是否有异常抛出或捕获它总是执行。
嵌套try语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 try { try { throw new Error ("oops" ); } finally { console .log("finally" ); } } catch (ex) { console .error("outer" , ex.message); }
现在,如果我们已经在 try 语句中,通过增加一个 catch 语句块捕获了异常.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 try { try { throw new Error ("oops" ); } catch (ex) { console .error("inner" , ex.message); } finally { console .log("finally" ); } } catch (ex) { console .error("outer" , ex.message); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 try { try { throw new Error ("oops" ); } catch (ex) { console .error("inner" , ex.message); throw ex; } finally { console .log("finally" ); } } catch (ex) { console .error("outer" , ex.message); }
从 finally 语句块返回:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function test ( ) { try { try { throw new Error ("oops" ); } catch (ex) { console .error("inner" , ex.message); throw ex; } finally { console .log("finally" ); return ; } } catch (ex) { console .error("outer" , ex.message); } } test()
声明 var var 语句 用于声明一个函数范围或全局范围的变量,并可将其初始化为一个值(可选)。
var声明的变量具有变量提升,“hoisting”就像是把所有的变量声明移动到函数或者全局代码的开头位置。这意味着变量可以在声明之前使用. var创建的全局变量会作为window对象的属性
let let 语句声明一个块级作用域的局部变量,并可以初始化为一个值(可选)。 与var区别:
var声明的变量作用域是全局或整个函数的,而let声明是块级的
let声明的变量不会在作用域中提升,他是编译时才初始化。
var创建的全局变量会作为window对象的属性,而let不会
var可以重复声明同一个变量,let不可以
var声明变量前访问undefined,let声明变量前访问抛出referenceError,暂时性死区
作用域规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function varTest ( ) { var x = 1 ; { var x = 2 ; console .log(x); } console .log(x); } function letTest ( ) { let x = 1 ; { let x = 2 ; console .log(x); } console .log(x); }
在全局作用域中,let 和 var 不一样,它不会在全局对象上创建属性。例如:
1 2 3 4 var x = 'global' ;let y = 'global' ;console .log(this .x); console .log(this .y);
重复声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let x = 1 ;switch (x) { case 0 : let foo; break ; case 1 : let foo; break ; } if (x) { let foo; let foo; }
case子句同属一个作用域,将case用块{}包裹可以创建一个新的作用域的词法环境,就不会产生重复声明的错误。
1 2 3 4 5 6 7 8 9 10 11 12 let x = 1 ;switch (x) { case 0 : { let foo; break ; } case 1 : { let foo; break ; } }
暂时性死区: 从块作用域开始到执行声明变量的行之前,let或const声明的变量都处于“暂时性死区”(temporal dead zone, TDZ). 当变量处于暂时性死区时,其尚未初始化,访问变量会抛出ReferenceError.
1 2 3 4 5 6 { console .log(bar); console .log(foo); var bar = 1 ; let foo = 2 ; }
使用术语”temporal”是因为区域取决于代码执行的顺序(时间),而不是编写的顺序(位置)。
1 2 3 4 5 6 7 8 9 { const func = () => console .log(letVar); let letVar = 3 ; func(); }
暂时性死区与typeof: 如果使用typeof检测在暂时性死区中的变量,会抛出ReferenceError。
1 2 console .log(typeof i); let i = 10 ;
与使用typeof检测值为undefined的未声明变量不同:
暂时性死区和词法作用域:
1 2 3 4 5 6 7 function test ( ) { var foo = 33 ; if (foo) { let foo = (foo + 55 ); } } test();
if(foo),foo有值,执行if块内代码,表达式foo + 55, 此时foo还未初始化处于暂时先死区,会抛出ReferenceError
其他情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 let x = 1 ;{ var x = 2 ; } var y = 2 ;{ let y = 2 ; } let z = 3 ;var z = 3
var声明会将变量提升到块的顶部,这会导致重复声明变量。
const 声明一个只读的命名常量。
全局或块级作用域
无法重复声明
不能通过赋值改变
全局常量不会变为 window 对象的属性
必须在声明的同一语句中指定它的值
函数和类 function 声明一个指定参数的函数。
有条件的创建函数:
函数可以被有条件来声明,这意味着,函数声明可能出现在一个 if 语句里,但是,这种声明方式在不同的浏览器里可能有不同的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var hoisted = "foo" in this ;console .log(`'foo' name ${hoisted ? "is" : "is not" } hoisted. typeof foo is ${typeof foo} ` );if (false ) { function foo ( ) { return 1 ; } }
函数声明提升:
JavaScript 中的函数声明被提升到了函数定义。你可以在函数声明之前使用该函数:
1 2 3 4 5 hoisted(); function hoisted ( ) { console .log('foo' ); }
函数表达式不会被提升:
1 2 3 4 5 notHoisted(); var notHoisted = function ( ) { console .log('bar' ); };
对于同名的var变量和函数,在声明前访问,函数的优先级最高,声明后访问,取决于变量是否赋初始值:
声明变量时只var声明未赋值,则函数会覆盖变量
声明变量时指定了初始值,则变量覆盖函数
1 2 3 4 console .log(a) var a;function a ( ) {}console .log(a)
1 2 3 4 console .log(a) var a=10 ;function a ( ) {}console .log(a)
function* function* 这种声明方式 (function关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个 Generator 对象
async function 使用指定的参数声明一个异步函数。
return 指return 语句终止函数的执行,并返回一个指定的值给函数调用者。
class class声明创建一个基于原型继承的具有给定名称的新类。
迭代器 do…while 创建一个循环来执行语句,直到该语句条件表达式的值为 false。先执行语句,再执行条件表达式,该语句至少会执行一次。
for 创建一个由 3 个可选的表达式组成的循环,该循环用括号包裹,分号分割,并在循环体中执行语句。
for…in for…in 语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 var triangle = {a : 1 , b : 2 , c : 3 };function ColoredTriangle ( ) { this .color = 'red' ; } ColoredTriangle.prototype = triangle; var obj = new ColoredTriangle();for (var prop in obj) { console .log(`obj.${prop} = ${obj[prop]} ` ); } for (var prop in obj) { if (obj.hasOwnProperty(prop)) { console .log(`obj.${prop} = ${obj[prop]} ` ); } }
for…of for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句.
示例:
迭代Array:
1 2 3 4 5 6 7 8 let iterable = [10 , 20 , 30 ];for (const value of iterable) { console .log(value); }
迭代String:
1 2 3 4 5 6 7 8 let iterable = "boo" ;for (let value of iterable) { console .log(value); }
迭代TypedArray:
1 2 3 4 5 6 7 let iterable = new Uint8Array ([0x00 , 0xff ]);for (let value of iterable) { console .log(value); }
迭代Map:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let iterable = new Map ([["a" , 1 ], ["b" , 2 ], ["c" , 3 ]]);for (let entry of iterable) { console .log(entry); } for (let [key, value] of iterable) { console .log(value); }
迭代Set:
1 2 3 4 5 6 7 8 let iterable = new Set ([1 , 1 , 2 , 2 , 3 , 3 ]);for (let value of iterable) { console .log(value); }
迭代 arguments 对象:
1 2 3 4 5 6 7 8 9 (function ( ) { for (let argument of arguments ) { console .log(argument); } })(1 , 2 , 3 );
迭代DOM集合:
1 2 3 4 5 6 let articleParagraphs = document .querySelectorAll("article > p" );for (let paragraph of articleParagraphs) { paragraph.classList.add("read" ); }
关闭迭代器:
1 2 3 4 5 6 7 8 9 10 function * foo ( ) { yield 1 ; yield 2 ; yield 3 ; }; for (let o of foo()) { console .log(o); break ; }
for await…of 在异步可迭代对象、类数组对象、迭代器和生成器上迭代,调用自定义迭代钩子,其中包含要为每个不同属性的值执行的语句。
while 创建一个循环语句,循环会一直持续到该语句条件表达式的值为 false。先执行条件表达式,然后执行语句。
其他 debugger 调用可用的调试功能。如果没有调试功能可用,该语句不生效。
export export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们.
无论您是否声明,导出的模块都处于严格模式。export 语句不能用在嵌入式脚本中。
存在两种 exports 导出方式:
命名导出(每个模块包含任意数量)
默认导出(每个模块包含一个)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 export let name1, name2, …, nameN; export let name1 = …, name2 = …, …, nameN; export function FunctionName ( ) {...}export class ClassName {...}export { name1, name2, …, nameN };export { variable1 as name1, variable2 as name2, …, nameN };export const { name1, name2 : bar } = o;export default expression;export default function (… ) { … } export default function name1 (… ) { … } export { name1 as default , … };export * from …; export * as name1 from …; export { name1, name2, …, nameN } from …;export { import1 as name1, import2 as name2, …, nameN } from …;export { default } from …;
import 用来导入外部模块或script中导出的函数。
无论是否声明了 strict mode,导入的模块都运行在严格模式下。在浏览器中,import 语句只能在声明了 type=”module” 的 script 的标签中使用。
此外,还有一个类似函数的动态 import(),它不需要依赖 type=”module” 的 script 标签。
1 2 3 4 5 6 7 8 9 10 11 import defaultExport from "module-name" ;import * as name from "module-name" ; import { export } from "module-name" ; import { export as alias } from "module-name" ; import { export1 , export2 } from "module-name" ; import { foo , bar } from "module-name/path/to/specific/un-exported/file" ;import { export1 , export2 as alias2 , [...] } from "module-name" ; import defaultExport, { export [ , [...] ] } from "module-name" ;import defaultExport, * as name from "module-name" ;import "module-name" ; var promise = import ("module-name" );
向 JavaScript 模块公开上下文特定的元数据的元属性。
label 带标识的语句,与break或continue语句一起使用。
1 2 3 4 5 6 7 8 9 10 11 12 let str = '' ;loop1: for (let i = 0 ; i < 5 ; i++) { if (i === 1 ) { continue loop1; } str = str + i; } console .log(str);
with 拓展一个语句的作用域.不建议使用