什么是"暂时性死区"?
在代码块内,使用let命令声明变量之前,该变量都是不可用的。
var tmp = 123;
let tmp; //报错
typeof a; //报错
let a = 1;
// 不报错
var x = x;
// 报错
let x = x;
// ReferenceError: x is not defined
如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
块级作用域
1、外层作用域无法读取内层作用域的变量。
{
{
{
{
{let insane = 'Hello World'}
console.log(insane); // 报错
}}}};
2、避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
// 函数声明语句
{
let a = 'secret';
function f() {
return a;
}
}
// 函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
3、ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
// 不报错
'use strict';
if (true) {
function f() {}
}
// 报错
'use strict';
if (true)
function f() {}
const
1、只在声明所在的块级作用域内有效。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
2、一旦声明,常量的值就不能改变。
3、只声明不赋值,就会报错。
4、同样存在暂时性死区,只能在声明的位置后面使用。
5、const只能保证指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,不能完全控制。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; // 报错
数组解构
- 如果等号右边不是可遍历结构,那么将会报错。
- 只要某种数据结构存在Iterator接口,都可以进行解构,Set,生产函数等。
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {}
对象解构
- 变量必须与属相同名
- 如果不同名:(真正被赋值的是后者,而不是前者。)
let {foo:baz} = {foo:"aaa" , bar:"bbb"};
baz // "aaa"
如果要将一个已经声明的变量用于解构赋值,请不要将花括号放在行首:
let x ;
({x} = {x:1});
可以方便的将现有对象的方法赋值到某个变量上:
//简化console.log
const {log} = console;
log('简化log')
//Math
let {sin} = Math;
字符串解构
字符串解构时被转换成了一个类似数组的对象,可以读取length属性
字符串拓展
3个查询是否包含字符串的新方法:
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
- 它们都支持第二个参数,表示开始搜索的位置。
让字符串重复n次:
- xxx.repeat(n),返回一个新的字符串,参数是 0 到-1 之间的小数,则等同于 0,0 到-1 之间的小数,取整以后等于
-0,
参数NaN
等同于 0。
ES2017引入了补全字符串长度功能,如果某个字符串不够指定长度,会在头部或者尾部补全
- padStrat() , 用于头部补全,常见用途:为数值补全位数,另一个用途是提示字符串格式。
-
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12" '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
- padEnd() , 用于尾部补全
- 一共接受两个参数,第一个参数补全生效的最大长度,第二个参数是用来补全的字符串
- 如果原字符串的长度,等于或者大于最大长度,补全不生效,返回源字符串。
- 如果用来不全的字符串与原字符串长度和超出最大长度,则截去超出位数后的补全字符串。
- 省略第二参数,空格自动补充。
返回正则表达式在当前字符串的所有匹配:
matchAll();
模板字符串 -- 编译模板详解
首先需要了解一些知识(正则):
-
( ) 标记一个子表达式的开始和结束位置,子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。
-
. 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。
-
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
-
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
-
\ s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。
-
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
-
replace中的$1代表什么? $1是正则里的捕获,就是前面的(.*?)里的东西
-
eval()是什么? eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码
自己写的简化版例子:
全部代码:
let template = `
<p>
<@ name @>
</p>
`;
function complate(template){
let reg = /<@(.+?)@>/g;
template = template.replace(reg,'`) \n javas($1) \n javas(`');
template = 'javas(`' + template + '`)';
let script = `
(function parse(name){
var output = '';
function javas(html){
output+=html;
}
${template};
return output;
})
`
return script;
}
let parse = eval(complate(template));
document.querySelector('div').innerHTML = parse('DEMO1')
解析:
//首先 有这样一个自定义模板
let template = `
<p> <@name@></p>`;
如果想把它转化为可解析的模板,需要将其转化为JavaScript表达式字符串,如下:
javas(`<p>`)
javas( name )
javas(`</p>`)
步骤:
//首先 需要使用字符串的replace方法,通过正则更改<@@>
let reg = /<@(.+?)@>/g;
template = template.replace(reg,'`) \n javas($1) \n javas(`');
得到结果:
<p>`)
javas( name )
javas(`</p>
可以看到拼接并不完全,接下来:
template = 'javas(`' + template + '`)';
//拼接完即可拿到:
/*
javas(`<p>`)
javas( name )
javas(`</p>`)
*/
拼接完成以后,需要将template放在一个函数中返回:
let script = `
(function parse(name){
let output = '';
function javas(html){
output+=html;
}
${template};
return output;
})
`;
至此已完成大半,下面将其封装成函数:
function complate(template){
let reg = /<@(.+?)@>/g;
template = template.replace(reg,'`) \n javas($1) \n javas(`')
template = 'javas(`' + template + '`)';
let script = `
(function parse(name){
var output = '';
function javas(html){
output+=html;
}
${template};
return output;
})
`
return script;
}
最后调用:
let parse = eval(complate(template)); //调用return的script模板字符串
document.querySelector('div').innerHTML = parse('DEMO1'); //传入name值渲染到div
输出一下script看起来更直观:
(function parse(name){
var output = '';
function javas(html){
output+=html;
}
javas(`<p>`)
javas( name )
javas(`</p>`);
return output;
})
可以看到这里进行的操作:
- 定义parse方法用来传入name和调用,定义javas方法,传入参数即为模板字符串和变量,拼接到空字符串上返回,这样就可以得到正常的html结构,最后通过eval()执行函数
output的输出:
<p>
DEMO1
</p>