ES6
全称为EcmaScript
,是脚本语言的规范,JavaScript
可以看作EcmaScript
的一个实现,ES
新特性也就是JS
的新特性
ECMA:全称为欧洲计算机制造商协会
let变量声明
写法和var
一样
区别:
-
let
不允许多次声明同一个变量-
let a = 3; // 不允许声明两次 let a = 33;
-
-
作用域
-
块级作用域,仅在代码块中有效,除了代码块就无效了
-
{ var a = 333; } // 允许 console.log(a);
-
{ var a = 333; } // 不允许 console.log(a);
-
Uncaught ReferenceError: a is not defined
-
-
-
不存在变量提升
-
console.log(a); var a = 3;
- 以上的输出结果为
undefined
- 以上的输出结果为
-
而以下结果就是报错
-
console.log(a); var a = 3;
-
-
不影响作用域链
- 例如块内变量是在块外的,不影响使用
无法使用数组下标绑定事件
例如点击div
标签更换div
的背景颜色
window.onload = function() {
let boxes = document.getElementsByClassName("box");
for(var i = 0; i < boxes.length; i++){
boxes[i].onclick = function() {
boxes[i].style.background = "pink";
}
}
}
此时会发现报错,并且颜色无法更换,这是因为for
循环执行时,响应函数并没有执行(此时没有触发点击事件),for
循环在此处的作用仅仅是给点击事件赋值,只有点击标签时才会执行响应函数,如果输出i
的值,此时永远是数组的长度
如果把循环控制的var i
更换成let i
,此时就不会出现这个问题了,可以直接给赋值了
window.onload = function() {
let boxes = document.getElementsByClassName("box");
for(let i = 0; i < boxes.length; i++){
boxes[i].onclick = function() {
boxes[i].style.background = "pink";
}
}
}
const关键字
用来声明常量
格式const 变量名 = 值;
,值只能被赋一次,必须要赋值
-
也拥有块级作用域,即代码块中声明,代码块外不能用
-
值可以是一个对象或者数组
-
如果是一个数组,可以对这个数组中的值改变
-
如果是一个对象,可以对这个对象中的属性改变值,或者增加属性
-
允许
-
function Person() { this.name = ""; this.age = 0; } const P = new Person(); P.age = 1; console.log(P);
-
const A = [100, 200, 300]; console.log(A); A.push(333); console.log(A); A[2] = 10000; console.log(A);
-
-
解构赋值
按照一定的模式在数组或者对象中提取值对变量进行赋值
数组的解构赋值:
-
const A = [100, 200, 300]; let [a, b, c] = A; console.log(a); console.log(b); console.log(c);
-
结果为
100 200 300
对象的结构赋值:
window.onload = function () {
const A = {
name: "名字",
age: 100,
sex: true,
fun: function () {
console.log("hello" + age);
}
}
let {name, age, sex, fun} = A;
console.log(name);
console.log(age);
console.log(sex);
console.log(fun);
fun();
}
对象的解构赋值中,let {变量名}
要与对象中的属性一一对应
模板字符串
可以使用反引号声明,即`符号
let s = `模板字符串`;
console.log(s);
console.log(typeof(s));
模板字符串
string
可以有多行
let s = `模板\n\n字
符
串`;
console.log(s);
console.log(typeof(s));
输出结果为:
模板
字
符
串
还可以这么写
let s = "JavaScript";
let ss = `${s} abcd`;
console.log(ss);
结果为JavaScript abcd
在字符串拼接时可以这么写
对象的简化写法
允许只把变量和函数放到一个大括号中形成一个新的对象
let name = "名字";
let age = 100;
let sex = true;
let fun = function() {
console.log("你好");
}
let obj = {
name,
age,
sex,
fun
}
console.log(obj);
在对象中声明函数时也可以简写,可以把: function
去掉
例如
let obj = {
fun2: function(){
console.log("你好");
}
}
可以简写为
let obj = {
fun2() {
console.log("你好");
}
}
箭头函数
=>
基本写法
let fun = (a, b) =>{
return a + b;
}
console.log(fun(3, 5));
特点:
-
在箭头函数中
this
是静态的,始终指向声明时作用域下的this
-
在普通函数中
this
是调用这个函数的对象,谁调用这个函数,this
就是谁 -
window.name = "window"; let fun1 = function() { console.log(this.name); } let fun2 = () => { console.log(this.name); } // 此时输出的都是window fun1(); fun2(); let obj = { name: "自定义" } // fun1是被obj对象所调用的,所以输出的内容为自定义 fun1.call(obj); // fun2是箭头函数,在window中定义,此时this是window fun2.call(obj);
-
箭头函数不能作为构造函数,例如以下代码会报错
-
let Obj = () =>{ this.age = 0; this.sex = true; } let obj = new Obj();
-
-
不能使用
arguments
变量保存实参 -
还可以继续简写,分为两种情况:
-
省略小括号
-
当参数只有一个时可以省略小括号
-
let fun = n => { return n + n; } console.log(fun(10));
-
-
省略花(大)括号
-
当函数体只有一条返回语句时可以省略,
return
也需要省略 -
例如
let fun = (a, b) => a + b; // 返回值为25 console.log(fun(10, 15));
-
-
函数的参数初始值
ES6允许给函数的参数赋初始值
例如
let fun = function(a, b, c) {
return a + b + c;
}
console.log(fun(1, 2));
输出结果为NaN
赋初始值后
let fun = function(a, b, c = 100) {
return a + b + c;
}
console.log(fun(1, 2));
输出结果为103
,JS虽然没有要求有初始值的参数的位置必须要在最后,但一般都要往后放,否则没有意义
函数的参数可以和解构赋值结合:
window.onload = function () {
let connect = {
url: "localhost",
username: "root",
password: "123456",
port: "3306"
}
let get = function({username, password, port}) {
console.log(`username: ${username}`);
console.log(`password: ${password}`);
console.log(`port: ${port}`);
}
get(connect);
}
结果为:
username: root
password: 123456
port: 3306
解构赋值可以赋一个默认值:
window.onload = function () {
let connect = {
url: "localhost",
username: "root",
password: "123456",
port: "3306"
}
let get = function({username, password, count = -1, port}) {
console.log(`username: ${username}`);
console.log(`password: ${password}`);
console.log(`port: ${port}`);
console.log(`count: ${count}`);
}
get(connect);
}
此时count
为-1
变长参数
在参数前添加...
let fun = function(... args) {
for(let i in args){
console.log(i);
}
}
fun(13, 252, 62, 562, 62, 52);
变长参数必须放到最后,否则报错
数组中的元素拆分
使用...
可以将数组中的元素的值挨个拆分开
例如
let fun = function(a, b, c) {
return a + b + c;
}
let a = [1, 2, 3];
// 等价于fun(1, 2, 3)
console.log(fun(...a));
合并两个数组
使用...
还可以合并多个数组
格式:let 变量 = [...数组1, ..., ...数组n]
let a = [1, 2, 3];
let b = [4, 5, 6];
let d = [7, 8, 9];
let c = [...a, ...b, ...d];
console.log(c);
c
的结果为[1, 2, 3, 4, 5, 6, 7, 8, 9]
...
称为扩展运算符
也可以用来克隆数组
let a = [...b]
代表将数组b
中的所有值复制到a
中,如果数组中有引用类型的值,那么此时也是浅拷贝
Symbol数据类型
是JS的第7种数组类型
可以使用Symbol()
函数返回并创建一个Symbol
创建方式:
let 变量名 = Symbol()
let 变量名 = Symbol("字符串")
let 变量名 = Symbol.for("字符串")
- 通过这种方式创建的
Symbol
唯一
- 通过这种方式创建的
使用场景
给对象添加独一无二的属性或者方法
定义方法的方式
let sy = Symbol("fun");
let obj = {
name: "good",
[sy](){
console.log("good");
}
}
// 调用
obj[sy]();
迭代器
iterator
接口就是对象中的一个属性
使用for...of...
,此时会将数组中的内容一一输出
let arr = ["abc", 342, 6543, "狗"];
for(let i of arr){
console.log(i);
}
for...in...
如果遍历数组,此时循环变量遍历的是下标
自定义实现迭代器:
-
在自己的对象中添加
[Symbol.iterator](){}
-
使其返回一个对象,返回的对象中的内容中必须包含
next()
方法 -
next()
方法中返回的对象必须包含{value: 值, done: bool值}
-
总体:
-
[Symbol.iterator](){ return { next: function() { return { value: 值, done: true/false } } } }
-
例子
-
let obj = { arr: [1, 3, "你好", "迭代器", "数组", 1000], name: "good", [Symbol.iterator](){ let index = 0; return { next: () => { if(index < this.arr.length){ return { value: this.arr[index++], done: false } }else{ return { value: undefined, done: true } } } } } } // 自定义迭代器,实现对这个对象进行迭代时返回数组中的值 for(let i of obj){ console.log(i); } }
-
-
生成器
生成器是一个特殊的函数
定义方式
let 变量名 = function* () {
xxx
}
let fun = function* () {
console.log("你好");
}
let a = fun();
// 返回的是迭代器,只有这样才会执行
a.next();
也可以使用yield
分割,每个next
会使代码往下执行一个yield
let fun = function* () {
console.log("你好");
yield
console.log("世界");
yield
console.log("大小");
}
let a = fun();
// 使用yield分割的,每执行一次都会跑到下一个yield
a.next();
a.next();
a.next();
返回的对象的next()
方法中可以传递参数,使用变量名 = yield;
可以获取到
let fun = function* () {
console.log("你好");
let a = yield;
console.log(a);
a = yield;
console.log(a);
}
let a = fun();
a.next();
a.next("gogo");
a.next("传递参数");
应用场景
异步调用:
window.onload = function () {
let fun = function* () {
console.log("11111");
yield;
console.log("22222");
yield;
console.log("33333");
yield;
console.log("44444");
}
var f = fun();
// 应用场景:异步调用,例如隔两秒使这个函数往下执行一次
setTimeout(() => {
f.next();
setTimeout(() => {
f.next();
setTimeout(() => {
f.next();
setTimeout(() => {
f.next();
}, 2000);
}, 2000);
}, 2000);
}, 2000);
}
此时发现,如果调用的层级过多,此时称为回调地狱
Set集合
集合中的元素是互异的
构造方式1:
let s = new Set();
构造方式2:
let s = new Set([1, 3, 45, 425, 53, 6, 5633]);
Set是一个对象
方法 | 含义 |
---|---|
.size() | 取集合的大小 |
.add(值) | 添加值 |
.delete(值) | 删除某个值 |
.has(值) | 是否存在某个值 |
.clear() | 清空全部内容 |
Set
集合也实现了迭代器,因此可以使用for...of...
遍历
可以转换为数组
let s = new Set([1, 1, 1, 3, 45, 425, 53, 6, 5633]);
s.add(44);
s.add(44);
s.add(44);
s.add(44);
s.add(44);
console.log(s, typeof s)
// 转换为数组
let a = [...s];
console.log(a);
Map
.set(键, 值)
class 类
在ES6中引入的,可以看作是语法糖
写法如下
class Person{
// 定义成员变量
a = 1;
b;
constructor(){
this.name = "名字";
this.age = 100;
this.sex = true;
}
method(){
console.log("这是一个方法,必须要这么写,没有其他的格式");
}
}
let a = new Person();
console.log(a);
a.method();
静态成员
在之前的JavaScript中实现静态成员是在prototype
中放入成员变量的
class Person {
arr = [1, 3, 5, 4, 246, 53];
static variable = "静态成员变量只能通过类名.的形式访问到"
constructor() {
this.name = "名字";
this.age = 100;
this.sex = true;
}
method() {
console.log("这是一个方法,必须要这么写,没有其他的格式");
}
}
let a = new Person();
console.log(Person.variable);
静态成员变量只能通过类名.属性/方法
的形式访问到,无法通过实例变量访问到
继承
写法如下
class GoodPerson extends Person {
constructor() {
// 调用父类的构造方法
super();
}
method(){
super.method();
console.log("子类");
}
}
也可以重写父类中同名的方法
ES6的模块化
export
出口、输出,用于暴露
- 在需要暴露的变量的最前边添加这个关键字
import
进口、输入、表明
- 在需要的地方引入,写法为
import * as 变量名 from "路径文件名"
- 引入后,可以通过
变量名.xxx
进行访问其他的变量
module 中文为模块、组件,读音为ˈmɑːdʒuːl
例如
<script type="module">
import * as module1 from "./js/main.js";
console.log(module1);
console.log(module1.p);
console.log(module1.set);
</script>
/js/main.js
class Person {
age = 10
name = "名字"
}
export let p = new Person();
export let set = new Set([1, 45425, 36, 53, 6, 53, "go", 463, 6]);
暴露方式
方式1:分别暴露,以上的方式就是分别暴露
- 即在需要暴露的变量前添加
export
方式2:统一暴露
-
在
js
文件的最后使用export{变量1, ..., 变量n}
-
main.js
-
class Person { age = 10 name = "名字" } let p = new Person(); let set = new Set([1, 45425, 36, 53, 6, 53, "go", 463, 6]); // 统一暴露 export{p, set}
方式3:默认暴露
-
写法
-
export default{ 对象... }
-
如果想要在外部使用,此时默认暴露的对象都在
default
对象中,若想使用,则必须在default
对象中调用-
<script type="module"> import * as module1 from "./js/main.js"; console.log(module1); console.log(module1.default.p); console.log(module1.default.set); </script>
-
-
导入方式
方式1:通用方式,即之前导入方式
- 写法为
import * as 变量名 from "路径文件名"
方式2:解构赋值方式(针对的分别暴露)
-
写法为
import {要暴露的名称1, ..., 名称n}
,这个时候可以直接使用相关的变量 -
<script type="module"> import {p, set} from "./js/main.js"; console.log(p); console.log(set); </script>
-
class Person { age = 10 name = "名字" } export let p = new Person(); export let set = new Set([1, 45425, 36, 53, 6, 53, "go", 463, 6]);
-
如果有多个文件使用解构赋值方式时可能产生重名问题,因此可以采用别名
-
别名方式
import {要暴露的名称1 as 别名, ..., 名称n}
-
例如
-
<script type="module"> import {p as ppp, set} from "./js/main.js"; console.log(ppp); console.log(set); </script>
-
-
对于默认暴露也可以采用别名方式进行暴露
-
import {default as 别名} from "xxx"
-
<script type="module"> import {default as mmm} from "./js/main.js"; console.log(mmm.p); console.log(mmm.set); </script>
-
class Person { age = 10 name = "名字" } export default { p: new Person(), set: new Set([1, 45425, 36, 53, 6, 53, "go", 463, 6]) }
-
方式3:简便形式 仅针对默认暴露
-
import 变量名 form "路径"
-
import v from "./js/main.js"; console.log(v.p); console.log(v.set);
可以新建一个入口文件,这个文件只作为引入其他js
文件使用
Q.E.D.