关于
本文由 [WowBar][WowBar] 团队首发于 [GitHub][GitHub]
作者: yvongyang
-
语句和表达式
JavaScript 中表达式和语句的主要区别在于一条语句执行一个动作,一个表达式产生一个值。意思是一个表达式执行后一定会生成一个值,而语句不一定会产生值。语句主要是用来执行动作,程序就是由一系列语句组成。
例如:// 表达式 name 1 + x getNames() // 语句 var name = 'yang'; function getNames() { } var foo = getNames() { };
接下来的内容里不会介绍表达式,只是列出来表达式的分类,语句部分会分别介绍语句的用法和示例,如果对于表达式和语句的内容比较清楚的可以直接跳到本章最后一部分——表达式和语句的比较。
-
表达式
表达式分为基本的表达式(包括基本关键字),还有左值表达式以及运算符。
1. 基本表达式
- this关键字
- 字面量(null,布尔值字面量,数字字面量,字符串字面量)
- 初始化字面量(数组字面量[],对象字面量{},正则表达式字面量
/ab+c/i
) - 函数表达式
- 类表达式
- 分组操作符
()
- 模板字面量 `…${…}…`
2. 左值表达式
- 属性访问符
- new
- 元属性:new.target
- super
- 函数调用
- 参数列表(arguments, …arguments)
- import
3. 运算符
- 一元
delete, void, typeof, +, -, ~, ! - 算术
+, -, /, *, %, A++, A–, ++A, --A - 比较
<, >, <=, >=, in, instanceof,==
,===
,!==
,!===
- 条件
condition ? ifTrue : ifFalse - 赋值
=, -=, +=, *=, /=, &=, |=, 解构赋值如[a, b] = [1, 2]、{a, b} = {a: 1, b: 2}等 - 逗号
, - 位移,二进制,二元逻辑
<<, >>, >>>; &, ^, |; &&, ||等
-
语句
语句分为声明语句、流程控制语句和其他语句。
其中,流程控制语句分为基本流程控制语句、迭代语句、跳转语句和条件语句。具体如下。1. 声明语句
-
1.1 变量声明
1.1.1 var 声明
声明一个变量,并可以地将其初始化为一个值
var a; var a = 2; var a = 2, b = 3; // 多个变量的初始化
var 声明的变量是可以提升的,提升意味着无论变量实际在哪里声明的,都会被当成在当前作用域顶部声明的变量。看下面示例 4。根据示例 1,2,3 显示的情况,建议始终声明变量,无论它们在函数还是全局作用域内。
var 声明的函数表达式不能提升。对于未声明的变量,可以用
typeof
检测其是否存在且不会报错。// 示例 1: 声明的变量的作用域在其声明位置的上下文中,而未声明变量是全局的; // 建议始终声明变量,无论它们是否在函数还是全局作用域内 function x() { y = 1; // 在严格模式(strict mode)下会抛出 ReferenceError 异常 var z = 2; } x(); console.log(y); // 打印 "1" console.log(z); // 抛出 ReferenceError: z 未在 x 外部声明 // 示例 2:声明的变量在任何代码执行前创建(会被提升),未声明变量只有在执行赋值操作时被创建; console.log(a); // 抛出 ReferenceError。 console.log('still going...'); // 永不执行。 console.log(a); // 打印 "undefined" 或 ""(不同浏览器实现不同)。 var a; console.log('still going...'); // 打印 "still going..."。 // 示例 3:声明的变量是它所在上下文环境的不可配置属性,非声明变量是可配置的(如可被删除) var a = 1; b = 2; delete this.a; // 在严格模式(strict mode)下抛出TypeError,其他情况下执行失败并无任何提示。 delete this.b; console.log(a, b); // 抛出ReferenceError。 // 'b'属性已经被删除。 // 示例 4: 变量提升 var x = y, y = 'A'; console.log(x + y); // undefinedA // 实际会被转换为: var x; var y; x = y; y = 'A';
1.1.2 let 声明
声明一个块级作用域的变量,并可以将其初始化。
let x; let x = 1; let x = 1, y = 2;
与 var 关键字声明变量的不同点在于:
var
声明的变量只能是全局或者整个函数块的,let/const
声明的变量只在其声明的块或子块中使用;(示例 1)let/const
不会在全局声明时创建window
对象的属性,而 var 会。(示例 2)let/const
在同一个块作用域或函数中不能重复声明(会报错),var
可以;(示例 3,4)var
声明的变量会被初始化为undefined
,let/const
声明的变量直到它们的定义被执行时才会初始化。量会被初始化为undefined
,let/const
声明的变量直到它们的定义被执行时才会初始化。
// 示例 1 function varTest() { var x = 1; { var x = 2; // 同样的变量! console.log(x); // 2 } console.log(x); // 2 } function letTest() { let x = 1; { let x = 2; // 不同的变量 console.log(x); // 2 } console.log(x); // 1 } // 示例 2 var x = 'global'; let y = 'global'; console.log(this.x); // "global" console.log(this.y); // undefined // 示例 3 if (x) { let foo; let foo; // SyntaxError thrown. } // 示例 4:case 没用 `{}` 包裹起来没形成块作用域,所以两个 `foo` 会在同一个块中被声明,所以报错。 let x = 1; switch(x) { case 0: let foo; break; case 1: let foo; // SyntaxError for redeclaration. break; } // 示例 5 function do_something() { console.log(bar); // undefined console.log(typeof foo); // ReferenceError,typeof也不安全 var bar = 1; let foo = 2; } // 示例 6 function go(n) { // n here is defined! console.log(n); // Object {a: [1,2,3]} for (let n of n.a) { // ReferenceError console.log(n); } } go({ a: [1, 2, 3]});
1.1.3 const
声明一个块作用域中的变量,并必须初始化一个值。与 let 用法基本相同,除了声明的变量的值不能被改变。
let a = 1; a = 2; console.log(a); // 2 const c = 1; c = 2; // Uncaught SyntaxError: Invalid or unexpected token
-
1.2 函数声明
每个函数都是一个 Function 对象,与其他对象的区别在于可被调用;
若函数没有 return 语句,则返回 undefined;
函数是值传递方式(对象是引用传递);
Es6 开始,严格模式下,块里的函数作用域为这个块。非严格模式下的块级函数不要用。定义函数的方式有3种:
函数声明: 普通函数声明,生成器函数声明
构造函数: 普通的构造函数 Function, 生成器构造函数 GeneratorFunction。(不推荐构造函数的方式定义函数,函数体为字符串,会引起其他问题)
函数表达式: 函数表达式,函数生成器表达式,箭头函数表达式写法示例:
// 函数声明定义函数 function getName(name1, name2, ...) { // 语句 } // 构造函数定义函数 var getName = new Function('name1', 'name2', 'return "myName:" + name1'); getName('yang'); // "myName:yang" // 函数表达式定义函数 var getName = function(name1, name2) { return 'myName:' + name1; } // 函数表达式 (function bar() { })
函数声明和表达式区别:
- 最主要的区别在于函数表达式可以省略函数名称,就是创建匿名函数;
- 函数表达式未省略函数名称,函数名只能在函数体内用,函数声明的函数名可以在其作用域内被使用;
- 函数声明可以提升,函数表达式不可以提升,所以表达式不能在调用之前使用;
- 函数表达式可被用作 IIFE(即时调用的函数表达式)。
-