大家好呀,我是阿燃❤️ 准备把前端开始学起来了,学到ES6 就停滞不前了,坚持每天学习,坚持日更博客😉,小白一个,欢迎大佬指点
ES6基础
ES6 基础语法和特性:
1、let和const指令
1.1 let命令
- let 命令基本用法:
- 用途:用于声明变量,但和var不同(后续会比较说明)
- let声明的变量不存在变量提升(必须先声明后使用)
- let存在块作用域(变量只在当前作用域内有效)
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
根据以上特性常用于for循环,以下是关于for循环的说明:
- for循环内的变量应该只在当前for循环作用域内有效,目的是防止对外面造成影响,而var声明的变量全局有效,所有for循环内推荐使用let声明变量
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
- for设置循环变量的作用域和循环体内部作用域都是单独的作用域
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
- let的常见特性说明
- 不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
- 存在暂时性死区
概念说明:暂时性死区是指在当前作用域内,变量只能先声明后使用,没有声明之前变量不可用
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
// 说明,这段因为x在y没有声明之间就使用y进行赋值,所以会报错
// 但是关于函数参数声明的这里面并没有指定是let还是var声明 怎么会存在暂时性死区的,还是说默认就是有死区???
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
- 不能重复声明变量
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
- let和var的比较
说明 | let | var |
---|---|---|
不同点1 | 存在块级作用域,变量只在当前作用域内有效 | 全局有效 |
不同点2 | 不存在变量提升,只能先声明后使用 | 可以先使用(默认值是undefined),后声明 |
不同点3 | 不能重复性声明 | 可以重复性声明 |
相同点 | 都是用来声明变量的 | 都是用来声明变量的 |
1.2 块级作用域
- 块级作用域存在的原因
ES5 中只有全局作用域和函数作用域不存在块级作用域,带来了一些问题
- 内层变量可能会覆盖外层变量
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
- 用来计数的循环变量泄露为全局变量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
- let命令带来了块级作用域
- ES6 允许块级作用域的任意嵌套
{{{{
{let insane = 'Hello World'}
console.log(insane); // 报错
}}}};
- 内层作用域可以定义外层作用域的同名变量
{{{{
let insane = 'Hello World';
{let insane = 'Hello World'}
}}}};
- 块级作用域和函数声明
ES6中规定可以在块级作用域中声明函数,但是其声明的函数相当于var即存在变量提升,函数声明还会提升到所在的块级作用域的头部。
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
//以上相当于
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
1.3const命令
- 基本使用
- const命令声明的变量必须同时声明和赋值,并且后续不允许修改变量值,即声明的变量是常量
- const声明的变量也存在块级作用域和暂时性死区
- const本质解释
const声明的变量是不允许当前声明的变量修改当前指向内存空间中的数据
- 对于基本类型(布尔值、字符串、数字)等存取的数据就是当前值
- 对于对象类的存取的数据是对当前对象的引用,用const声明后的变量后续不允许当前变量指向别的对象引用,但是对象本身属性是可以修改的
const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; // 报错
- ES6中声明变量的方法有六种
ES5 只有两种声明变量的方法:var命令和function命令。ES6 除了添加let和const命令,另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。
1.4顶层对象的属性
- 在ES5中var声明的变量就是全局变量,所以通过window.xx 可以获取
- 在ES6中let const class 声明的变量是块级作用域变量只在当前作用域有效,通过window.xxx不能获取
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
1.5 globalThis对象
如何获取全局对象
- 对于全局对象不同实现里面不是统一的
- 浏览器里面,顶层对象是window, Node 和 Web Worker 没有window。
- 浏览器和 Web Worker 里面,self也指向顶层对象,是 Node 没有self。
- Node 里面,顶层对象是global,但其他环境都不支持。
- this的指向问题分析
- 全局环境里面this会返回全局对象,但在Es6 this返回的是当前模块
- 函数里面的this 如果作为对象的方法则返回当前对象,如果作为单纯的函数,则返回全局对象,但是严格模式下会返回undefined
- globalThis对象
ES2020 在语言标准的层面,引入globalThis作为顶层对象。也就是说,任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this
2、变量的解构赋值
2.1 数组的解构赋值
- 含义
如果等号两边结构一致,就会自动的对应赋值 - 基本使用
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
说明:只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
function* fibs() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
- 默认值问题
当变量的值严格等于undefined时才会触发默认值
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
2.2 对象的解构赋值
- 基本使用
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
如果解构失败,变量值是undefined
2. 说明:模式匹配
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
// 上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}
- 默认值问题
只有当变量值严格等于undefined时默认值才会生效
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
- 注意点:
- 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
2.3 字符串的解构赋值
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
2.4 数值和布尔值的解构赋值
说明:解构赋值等号右边只要不是对象或者数组会先转换为对象然后赋值,由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
2.5 解构赋值的具体应用场景
- 交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
- 函数参数值
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
- 函数返回多个值
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
- 函数参数默认值
- 遍历map数据结构
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
- 提取json数据
2.6 圆括号问题( 有点不清楚)
- 圆括号运算符
可以提高运算的优先级 - 用于包裹匿名函数让其立即执行
- 圆括号包裹号可以解析为对象创建
- 圆括号通常用于表示一个函数调用或对象创建。
// 错误的写法
let x;
{x} = {x: 1};
前面解析为了块级作用域所以会报错
// SyntaxError: syntax error
// 正确的写法
let x;
({x} = {x: 1});
//通过加上圆括号解析为对象创建 然后为其解构赋值
3、字符串的扩展
模板字符串:
- 基本使用:
模板字符串是增强版的字符串,用反引号 (`)表示
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
- 注意:
- 如果在模板字符串中需要使用反引号,则前面要用反斜杠转义
let greeting = `\`Yo\` World!`;
- 所有模板字符串的空格和换行,都是被保留的,比如
- 标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
- 模板字符串中嵌入变量,需要将变量名写在${}之中。
function authorize(user, action) {
if (!user.hasPrivilege(action)) {
throw new Error(
// 传统写法为
// 'User '
// + user.name
// + ' is not authorized to do '
// + action
// + '.'
`User ${user.name} is not authorized to do ${action}.`);
}
}
- 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
let x = 1;
let y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`
// "1 + 4 = 5"
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`
// "3"
- 模板字符串之中还能调用函数。
参考:https://www.bookstack.cn/read/es6-3rd/spilt.5.docs-let.md