1. 语句和表达式
Javascript中语句相当句子,表达式相当于短语,例如:var a = 1;
,整个这一个包含两个表达式var a
, a = 1
, 合起来组成了一个语句
1.1 语句结果值
对于每一个语句存在一个结果值,通常结果值是undefined
,实际上结果是最后一个语句的最后一个外部变量相关表达式的值,在Chrome控制台中可以看到:
// 直接输出1,因为是操作外部变量a的表达式
if (true) {
a = 1;
}
// 控制台输出undefined,因为a并不是语句外部的变量
if (true) {
var a = 1;
}
// 控制台输出2,因为最后一个是语句2,是操作语句2的一个外部变量a的表达式
if (true) { // 语句1
a = 1;
if (true) { // 语句2
var a = 2;
}
}
// 这个时候控制台输出undefined,因为最后一个是语句2,a不是语句2的外部变量
if (true) { // 语句1
a = 1;
if (true) { // 语句2
a = 2;
}
}
如果我们想获取到当前的语句结果值,可以使用eval
函数获取,其中ES7规范提供了新的do
表达式获取(并没有进行测试)
var b = eval('if(true){ a = 2}');
console.log(b); // 2;
var c = do { if(true) { a = 2}};
console.log(c); // 2;
1.2 表达式的副作用
表达式在使用的过程中可能会产生副作用,也就是会改变变量的值
var a = 1;
var b = a++;
console.log(b); // 1
console.log(a); // 2
上面的例子中a
首先将值赋给了b
,然后进行了自增,对自身的值进行了改变,这里b
只能获取到a
自增之前的值,如果想在一个赋值语句中让b
赋值为a
自增后的值,除了在a
自增后进行再次进行赋值以外,可以可以使用,
来简化写法
var a = 1;
var b = (a++, a);
console.log(b); // 2
console.log(a); // 2;
1.3 代码块
- 使用大括号
{}
可以创建字面量的对象,也可以创建一个代码块,并创建一个标签,结合break
和continue
,使得语句运行可以跳到这个定义的标签处,如果使用continue label
,会继续执行下一次语句,如果用break label
,则会直接跳过当前语句
// 如果使用continue
{
label: for(let i = 0; i< 2; i++){
for(let j = 0; j < 2; j++) {
console.log(`i:${i}-j:${j}`);
if(j === 1) {
continue label;
}
}
}
}
// 上面的输出为
// i:0-j:0
// i:0-j:1
// i:1-j:0
// i:1-j:1
// 如果使用break
{
label: for(let i = 0; i< 2; i++){
for(let j = 0; j < 2; j++) {
console.log(`i:${i}-j:${j}`);
if(j === 1) {
break label;
}
}
}
}
// 上面的输出为
// i:0-j:0
// i:0-j:1
- 使用大括号
{}
在进行运算的时候,如果大括号在语句的最前端,则它会被解析为一个代码块,从而会忽略掉该部分内容
{} + '2' === 2; // 语句结果值为true, 相当于执行了一元运算符 +'2' 操作
- ES6中运用大括号
{}
可以进行解构赋值操作
// 对于对象赋值解构
var obj = {a: 1, b: 2};
var {a, b} = obj;
console.log(a); // 1
console.log(b): // 2
// 对于函数参数解构
function f({a, b, c}){
console.log(a, b, c);
}
f({a:1, b: 2, c: 3}); // 1 2 3
1.4 try finally语句
try finally
的finally
语句中的代码一定会进行执行,但是如果finally
存在抛出异常或者返回值,那么原本的返回值会被废弃/覆盖,最终结果为finally
中的返回值
function f() {
try {
return 1;
} finally {
// throw Error('err');
return 2;
}
}
console.log(f()); // 2,原本返回值1被覆盖
1.5 switch语句
switch
语句中的case
比较为严格比较(===
比较),如果需要使用宽松比较(==
),可以将switch
值设置为true
,在case
条件中使用宽松比较
var a = '2';
switch(a) {
case 1:
console.log('case 1');
break;
default:
console.log('default');
}
// 输出 default
var a = '1';
switch(true) {
case a == 1:
console.log('case 1');
break;
default:
console.log('default');
}
// 输出 case 1
2. 运算符
2.1 优先级
运算符的优先级影响不同运算符之间的组合规则,优先级越高的,越先组合在一起,其中&& > || > ?:
var a = false ? 3 : 4 && 5;
console.log(a); // 5 由于&&优先级大于?: , 所以相当于false ? 3: (4 && 5)
2.2 关联
运算符的关联影响相同运算符之间的组合规则,&&
和||
是左关联,也就是左边先组合,?:
是右关联,也就是右边先组合
var a = 1 && 2 && 3;
console.log(a); // 3 相当于 (1 && 2) && 3
var a = true ? true: false ? false: true
console.log(a); // true 相当于 true ? true: (false ? false: true);
3. 其他
3.1 自动分号ASI
Javascript为了提高容错率,如果在每一行换行处缺少分号,会自动在换行后增加分号
3.2 函数中的arguments
函数中的arguments
在非严格模式下和形参存在关联,修改后会对形参进行修改,在严格模式下,则不存在关联关系,不会影响到形参的值
function f(a) {
arguments[0] = 2;
console.log(a);
console.log(arguments[0]);
}
f(1); // 2 2
'use strict'
function f(a) {
arguments[0] = 2;
console.log(a);
console.log(arguments[0]);
}
f(1); // 1 2
3.3 宿主环境
宿主环境是指Javascript的运行环境,不同的宿主环境中会有不同的内置对象,例如:浏览器中有HTMLDivElement
,而node
环境中则没有;浏览器中全局变量为window
,而node环境中为global
同时控制台对象(console
对象)也是根据宿主环境进行变化的,浏览器中是开发者控制台输出,而node
中是标准的输出
3.4 全局DOM变量
在浏览器环境中,HTML
的节点中定义id
属性,会创建一个全局变量
// html中
// js中
console.log(a); // 会直接打印该DOM节点对象
3.5 原生函数
Javascript的内置函数,不建议直接操作内置函数(包括增加新的属性和原型链扩展),如果实在需要进行扩展,可以加上判断条件后会相对安全
(function(){
if(!Array.prototype.a) {
Array.prototype.a = function() {};
}
})()
但是即使加上判断条件也无法确保该方法执行结果一定为我们所想要的扩展方法,所以,可以加上测试代码来确保扩展方法的正确性。
(function(){
if(!Array.prototype.a) {
Array.prototype.a = function() { return 1};
return;
} else { // 例如我们想要a方法返回1
var a = [];
if (a.a === 1) { // 等于1的时候满足我们的需求
return;
}
}
throw Error('error');
})()
3.6
但是变量声明的提升作用不会跨提升到最前:
每个运行过程是独立的,一个出错不会导致其他的停止运行:
script中的语句不能包含字符串,否则会作为script的结束标签解析而结束
3.7 保留字
Javascript中关键字,预留关键字,基本类型等不能作为变量名使用
3.8 限制
Javascript对于一些数据存在限制,例如字符串的最大长度,函数的参数最大个数,变量名的最大长度,阻塞的最大时常等
4. 参考
《你不知道的Javascript(中卷)》