扩展运算符
扩展运算符可以将遍历对象转换为数组,能够在数组或函数调用中轻松展开表达式:
function cast() { return [...arguments] } cast("a", "b", "c"); //["a", "b", "c"]
扩展运算符可以将一个字符串分割成数组,数组的元素是字符串的每个字符:
[..."show me"] // ["s", "h", "o", "w", " ", "m", "e"]
还可以在扩展运算符的左右添加其他的内容:
function cast() { return ["left", ...arguments, "right"]; } cast("left", "a", "b", "c", "right"); //["left","a", "b", "c", "right"]
拼接多个数组无压力:
const all = [1, ...[2, 3, 4, 5, 6], 7, ...[8, 9]]; console.log(all); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
模板字面量
模板字面量是对常规JavaScript的巨大改进。使用反引号“`”
const text = `algorithm`;
使用模板字面量,就无需对各种特殊符号转义:
const text = `1+1=2`; console.log(text); // 1+1=2
字符串插值
模板字面量允许在模板的任意位置加入JavaScript的表达式:
const num=2 const text = `1+1=${num}`; console.log(text); // 1+1=2 const text = `1+1=${1+1}`; console.log(text); // 1+1=2
多行模板字面量
模板字符串默认支持多行字符串。在使用模板字符串前想要表示多行字符串,则需借助转义符、字符串连接数组,甚至可能是注释:
// 原方法 const escaped = "The first line\n\ A second line\n\ Then a third line"; const concatenated = "The first line\n" ` "A second line\n" ` "Then a third line"; const joined = ["The first line", "A second line", "Then a third line"]; // ES6模板字符串 const multiline = `The first line A second line Then a third line`;
多行模板字符串同样也存在缩进问题:
function getParagraph(){ return ` Dear Rod this is a template literal string... Nico ` } getParagraph() //" // Dear Rod // // this asfsdfasfasdfsdfs // asfasdfasdf // // Nico //"
我们期待的结果可能是没有缩进的,但是仍然会保留缩进。如果使用通用函数来移除字符串每一行的缩进,怎么做不太理想。对于这些更高级的用例,需要使用另一种名叫标签模板的字面量。
标签模板
在JavaScript中\具有特殊含义,表示转义。例如:\n表示换行。String.raw标签模板可以使得转义字符不在转义:
const text = String.raw`"\n" is taken literally. It'll be escaped instead of interpreted`; console.log(text); // "\n" is taken literally. // It'll be escaped instead of interpreted
模板字面量String.raw用于解析模板,在接受一个数组参数或者其他参数时,数组包含模板的每一个静态部分,其他参数对应每个表达式的计算结果。
let和const声明
变量提升
变量提升:是不管变量声明在代码的哪个位置,都会提升到所在作用域的顶部。
function study() { console.log(a) var a = 1; } study()//undefined
虽然a在console下面声明,但是会变量提升并赋值understand。
var的作用域:基于词法作用域,可以是函数作用域或者全局作用域。
function study(item) { if (item == 1) { var a = true; } return a } study(1); //true
虽然a是在if代码分支语句声明的,但是依然可以在代码分支外访问到,主要是因为var是函数作用域。
使用var声明的变量,可以使用deep变量声明所在块的外部访问到该变量,并不会报错。如果能在以下几种情况下抛出异常:
- 访问内部变量会破坏代码的封装性
- 内部变量和外部变量没用任何联系
- 同级的兄弟块中可能想要使用相同的变量
- 某个父块中已经可能存在的相同变量,但仍然需要在内部使用该变量
let声明,就是var声明的替代方案。
let声明
let遵循块级作用域规则,而不是默认的词法作用域的规则。相较于var而言,var只能通过嵌套函数来创建更深的作用域,而let只需要一个花括号即可。
let algorithm = {}; { let language = {}; { let questions = {}; } // 在此尝试访问questions会报异常 } // 在此尝试访问language会报异常 // 在此尝试访问questions会报异常
let有个非常有用的方法,在for中使用let,作用域会封闭在循环体内能获取到想要的值:
for (var i = 0; i < 5; i++) { setTimeout(() => console.log(i)); } // 5 // 5 // 5 // 5 // 5 for (let i = 0; i < 5; i++) { setTimeout(() => console.log(i)); } // 0 // 1 // 2 // 3 // 4
这是var的典型案例,因为i的值会被绑定到函数作用域,但是因为for不是函数,就渗透出去。在每个循环添加时间回调时,i的值一直增加到5。在等输出时就输出最终值。
相反,let声明绑定块级作用域,每次循环都会有新的绑定。每次添加时间回调时,每个回调都会保存一个新的变量i的值。
暂时性死区
变量在作用域开始前就会创建,但是会产生暂时性死区,在变量未执行前无法访问。
console.log(study);//ReferenceError: study is not defined let study = "algorithm";
暂时性死区的主要目的:
- 更轻松的捕捉错误
- 以防用户在声明变量前,就使用变量
- 避免由于变量提升导致的不良编码习惯
const声明
于let相似,const也遵循块级作用域,也存在暂时性死区。实际上暂时性死区就是为const实现的,之后为统一也应用于let。const之所以需要暂时性死区,是因为如果没暂时性死区,就可以在const声明执行前给提升的const变量赋值,执行语句会报错。
// const 遵循块级作用域规则 const study = algorithm; { const study = language; console.log(study); //language } console.log(study); //algorithm
const于let的不同:
1.const必须在声明时就初始化
const algorithm = "nignt"; const language;//语法错误,缺少初始化
2.用const声明的变量(是简单数据类型时)值不可改变
const algorithm = "nignt"; const language = 1; if (algorithm === "nignt") { language = 2;//捕获类型错误:分配给常量变量 }