ECMAScript(ES6)基本语法介绍
1.新增数据类型——Symbol
-
介绍
- Symbol类型的值通过Symbol函数生成,相同Symbol函数返回的值是唯一的。
- Symbol函数可以接收字符串作为参数,但是即使相同参数返回的也是唯一值,即
Symbol('miaov') != Symbol('miaov')
-
实例
打印结果:外部:undefined 、内部:男
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Symbol实例</title> </head> <body> <script> /* *Symbol一般用于属性保护,即属性私有化 *属性私有化:即方法内部可以访问,但方法外部不可以访问 */ var Person = (function(){ var _gender = Symbol('gender'); function P(name,gender){ this.name = name; this[_gender] = gender; } P.prototype.say= function() { console.log('内部'+this[_gender]) } return P; })(); var p1 = new Person('风雅', '男'); console.log('外部'+p1[Symbol('_gender')]); console.log(p1.say()); </script> </body> </html>
2.变量声明——const
块及作用域:由 {} 包含的代码块所产生的作用域
-
Let
-
不支持变量声明预解析(先声明后使用)
-
支持块作用域
-
不允许重复声明(暂存死区)
点击按钮一:打印0
点击按钮二:打印1
点击按钮三:打印2
即对应的索引值
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> button.active { background: pink; } p { display: none; } p.active { display: block; } </style> </head> <body> <button class="active">按钮一</button> <button>按钮二</button> <button>按钮三</button> <p class="active">内容一</p> <p>内容二</p> <p>内容三</p> </body> <script> btns = document.querySelectorAll('button'); ps = document.querySelectorAll('p'); for (let i=0; i<btns.length; i++){ btns[i].onclick=function(){ console.log(i); } } </script> </html>
-
-
var
-
支持变量声明预解析
-
不支持块作用域
-
允许重复声明
点击按钮一:打印3
点击按钮二:打印3
点击按钮三:打印3
即打印循环结束时i的值
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> button.active { background: pink; } p { display: none; } p.active { display: block; } </style> </head> <body> <button class="active">按钮一</button> <button>按钮二</button> <button>按钮三</button> <p class="active">内容一</p> <p>内容二</p> <p>内容三</p> </body> <script> btns = document.querySelectorAll('button'); ps = document.querySelectorAll('p'); for (var i=0; i<btns.length; i++){ btns[i].onclick=function(){ console.log(i); } } </script> </html>
-
-
const——常量声明,一经声明不能改变
- 不支持变量声明预解析(先声明后使用)
- 支持块作用域
- 不允许重复声明(暂存死区)
3.解构赋值
允许按照一定模式,从数组和对象中提取值,并对变量进行赋值,这被称为解构赋值
-
数组解构赋值
顺序对应
let [a, b, c] = [1, 2, 3];
-
对象解构赋值
key 值对应
let { foo, bar } = { foo: "aaa", bar: "bbb" }
-
解构赋值——别名
foo: 原始名称
f:别名
let { foo: f, bar: b } = { foo: "aaa", bar: "bbb" }
-
多重解构
let { foo: [a, b] } = { foo: [10,20], bar: "bbb" }
4.扩展运算符——...
把数组/对象转成参数序列(使用逗号分隔的序列)
['a,'b','c'] => 'a','b','c'
相当于去除数组的中括号
{left:100, top:200} => left: 100, top: 200
相当于去除大括号
-
案例
// 合并数组 var arr1 = [1,7,3,6,2]; var arr2 = ['a', 'b', 'c']; console.log( [...arr1, 'maiov', ...arr2] ); // 合并对象 let obj1 = {left:100, top: 200}; let obj2 = {width: 200, height: 200}; let obj3= { ...obj1, ...obj2 }; console.log(obj3);
5.迭代
-
迭代协议
-
规定了迭代与实现的逻辑
-
基本结构
// 迭代协议 obj[Symbol.iterator] = function () { return { next: function () { // 迭代函数必须的 return { value: '' // 迭代输出值 done: true | faslse // 是否结束迭代 } } } }
-
-
迭代器
-
具体的迭代实现逻辑,即 [Symbol.iterator]方法
输出结果:left 100
top 200
-
实例
/* * 迭代对象 * 实现了迭代器的对象 * 迭代器:方法 [Symbol.iterator] */ var obj = { left: 100, top: 200 }; // 迭代协议 // 自定义迭代条件,以及返回值 obj[Symbol.iterator] = function () { let keys = Object.keys(obj); // 获取对象的key值,返回一个数组 let len = keys.length; let n = 0; return { next: function () { // 迭代函数必须的 if (n < len) { // 迭代条件 return { value: { k: keys[n], v: obj[keys[n++]] },// 迭代输出的值,此处的n++的含义是先输出obj[keys[n]]的值,然后在n++ done: false // false代表还需继续迭代 } } else { return { done: true } } } } } // obj[Symbol.iterator]().next() => {done: true} for (var { k, v } of obj) { //of 是一个可迭代的对象 console.log(k, v); }
-
-
迭代对象
- 可被迭代的对象,含有 [Symbol.iterator]方法的对像
-
迭代语句
-
for…in:以原始插入的顺序迭代对象的可枚举属性
输出结果:0, 1, 2
-
实例
<script> var arry = ['a', 'b', 'c']; for(var attr i arry){ console.log(attr); } </script>
-
for…of:根据迭代对象的迭代器具体实现迭代对象数据
输出结果:a, b, c
-
实例
<script> var arry = ['a', 'b', 'c']; for(var attr of arry){ console.log(attr); } </script>
-
6.数据结构——Set
集合的基本概念:集合是由一组无序且唯一(即不能重复)的项组成。这个数据结构使用了与有限集合相同的数学概念,应用在计算机的数据结构中。
特点:key和value相同,没有重复的value。
- ES6提供了数据结构Set。它类似于数组,但成员值唯一的,没有重复的值
-
如何创建一个Set
const s= new Set([1, 2, 3]); console.log(s);
-
Set类的属性
- size:类似于数组的length
-
Set类的方法
-
set.add(value)
:添加一个数据,返回Set结构本身s.add('a'); //向数组添加a这一元素 s.add('b').add('c').add('d'); //链式添加
-
set.delete(value)
:删除指定数据,返回一个布尔值,表示是否删除成功console.log(s.delete('a')); //删除a,成功删除返回true,否则返回false
-
set.has(value)
:判断该值是否为Set的成员,返回一个布尔值console.log(s.has( 'a')); // 判断s中是否含有a这一元素,有则返回true,反之返回false
-
set.clear()
:清除所有数据,没有返回值 -
keys()
返回键名的遍历器 -
values()
:返回键值的遍历器 -
entries()
:返回键值对的遍历器 -
foreach()
:使用回调函数遍历每个成员
-
7.数据结构——Map
字典:是用来存储不重复key的Hash结构。不同于集合(Set)的是,字典使用的是[键,值]的形式来存储数据的。
**Object对象 **:JavaScript 的对象(Object: {})只能用字符串当作键,这给他的使用带来很大的限制。
Map:为了解决这个问题,ES6提供了Map数据结构。他类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。也就是说,Object结构提供了“字符串——值”的对应,Map结构提供了“值——值”的对应,是一种完善的Hash结构实现。如果你需要“键值对应的数据结构,Map比Object更加适合。
-
如何创建一个Map
var map = new Map([ ['a', 1], ['b', 2] ]);
-
Map 类的属性
- size:类似于数组的length
-
Map 类的方法
-
map.set(key, value)
:设置键名key对应的键值为value,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就会生成新的键该键。map.set('fengya', 'shuai').set('new', 'fq');
-
map.get(key)
:get方法获取key对应的键值,如果找不到key,返回undefined。 -
map.delete(key)
:删除某个键,返回true,如果删除失败则返回false。 -
map.has(key)
:返回一个布尔值,表示某个键是否存在当前Map对象之中。 -
map.clear()
:清除所有数据,没有返回值。 -
map.keys()
:遍历所有的键名,并将其返回。 -
map.values()
:遍历所有的键值,并将其返回。 -
map.entries()
:遍历所有的键名与键值,并将其返回。 -
map.forEach()
:使用回调函数遍历每个成员。
-
-
使用注意事项:
- map的键名跟内存地址绑定,只要内存地址不一样,则就代表不同的键名
- map里面的key的排列顺序是按照添加顺序进行排列的。
8.Iterator
和 for...of
循环
-
基本概念 :
- 在ES6中新增加了Set和Map两种数据结构,再加上js之前原有的数组和对象,这样就有了四种数据集合,平时还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象等。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
- Iterator :这就是一种机制,它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署在
Iterator
接口,就可以完成遍历操作,而且这种遍历操作是依次处理该数据结构的所有成员。 - Iterator遍历器的作用:
- 为各种数据结构,提供一个统一的、简便的访问接口。
- 使得数据结构的成员能够按某种次序排列。
- ES6新增了遍历命令
for...of
循环,Iterator
接口主要提供for...of
消费。
-
手写Iterator接口
const arr = [1, 2, 3]; function iterator(arr){ let index = 0; return { next: function(){ return index < arr.length ? {value: arr[index++], done: false} : {value: undefind, done: true}; } } } const it = iterator(arr); console.log(it.next()); // 打印结果{value:1, done:false} console.log(it.next()); // 打印结果{value:2, done:false} console.log(it.next()); // 打印结果{value:3, done:false} console.log(it.next()); // 打印结果{value:undefind, done:true}
- Iterator的遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的next()方法,可以将指针指向数据结构的第一个成员。
- 第二次调用指针对象的next()方法,指针就指向数据结构的第二个成员。
- 不断调用指针对象的next()方法,直到他指向数据结构的结束位置。
- 每一次调用next()方法,都会返回数据结构的当前成员信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
- 凡是具有
Symbol.iterator
属性的数据结构都具有Iterator
接口 - 凡是具有
Symbol.iterator
属性的数据结构都可以进行解构赋值和使用扩展运算符...
- Iterator的遍历过程:
9.class语法
-
基本概念:JS语言的传统方法是通过构造函数,定义并生成新的对象,是一种基于原型的面向对象系统。这种写法跟传统的面向对象语言(比如C++和Java)差异很大,很容易让新学这门语言的人感到困惑。所以在ES6中新增加了类的概念,可以使用class 关键字声明一个类,之后以这个类来实例化对象。
-
实例
class Miaov { constructor(a, b){ this.a = a; this.b = b; return this; } print(){ console,log(this.a + ' ' + this.b ); } }; const miavo = new Miaov ('hello', 'world').print(); // 输出结果:hello world consoe.log(typeof Miaov); // 打印结果:function
-
Miaov 中的
constructor
方法是构造方法,this
关键字则代表实例对象。也就是说,ES6的构造函数,对应Miaov 这个类的构造方法。 -
Miaov 这个类除了构造方法,还定义了一个
print
方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去就可以了。另外,方法直接不需要逗号分隔,加了会报错。 -
构造函数的
prototype
属性,在ES6的类上面继续存在。而且类的所有方法都定义在类的prototype
的属性上console.log(Miaov.prototype);
-
定义在类中的方法都是不可以枚举的。
console.log(Object.keys(Miaov.prototype));
-
constructor
方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor
方法,如果没有显示定义,一个空的constructor
方法会被默认添加。 -
生成类的实例对象的写法,与ES5完全一样,也是使用new命令。如果忘记加上new,像函数那样调用Class,将会报错。
-
-
继承:
-
案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> canvas { box-shadow: 2px 2px 12px rgba(0, 0, 0, 0.5); } </style> </head> <body> <canvas id="canvas"></canvas> <!-- <script src="./bundle.js"></script> --> <script> window.onload = function () { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const w = canvas.width = 600; const h = canvas.height = 400; class Ball { constructor(x, y, r) { this.x = x; this.y = y; this.r = r; this.color = `rgb(${~~Ball.rpFn([55, 255])}, ${~~Ball.rpFn([55, 255])}, ${~~Ball.rpFn([55, 255])})`; return this; } render(ctx) { ctx.save(); ctx.translate(this.x, this.y); ctx.fillStyle = this.color; ctx.beginPath(); ctx.arc(0, 0, this.r, 0, 2 * Math.PI); ctx.fill(); ctx.restore(); return this; } static rpFn(arr) { let max = Math.max(...arr), min = Math.min(...arr); return Math.random() * (max - min) + min; } } // const ball = new Ball(100, 100, 30).render(ctx); class SuperBall extends Ball { constructor(x, y, r) { super(x, y, r); this.vy = SuperBall.rpFn([2, 4]); this.g = SuperBall.rpFn([0.2, 0.4]); this.a = 0; return this; } move(ctx) { this.y += this.vy; this.vy += this.g; let current = this.vy * (-0.75); if (this.y + this.r >= ctx.canvas.height) { this.y = ctx.canvas.height - this.r; if (Math.abs(current - this.a) < 0.01) return false; this.a = this.vy *= (-0.75); } ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); super.render(ctx); return true; } } // const ball = new SuperBall(100, 100, 30).render(ctx); let ball, timer; canvas.onclick = function (e) { let x = e.offsetX, y = e.offsetY; let r = ~~Ball.rpFn([25, 55]); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ball = new SuperBall(x, y, r).render(ctx); ballMove(); } function ballMove() { timer = window.requestAnimationFrame(ballMove); if (!ball.move(ctx)) { window.cancelAnimationFrame(timer); } } } </script> </body> </html>
-
说明:
- 子类继承父类 使用
extends
关键字 - 为父类指定静态方法,使用
static
方法名字 - super:
- 在构造函数中可以当做一个函数来使用,相当于调用父类的构造函数。
- 在原型方法中,可以当做一个对象来使用,相当于父类的原型对象,并且会自动绑定this到子类上。
- 子类继承父类 使用
-
10.Promise
对象
-
基本概念:
- 是ES6中新增的异步编程解决方案,体现在代码中它是一个对象,可以通过
Promise
构造函数来实例化。
- 是ES6中新增的异步编程解决方案,体现在代码中它是一个对象,可以通过
new Promise(cb) ===> 实例的基本使用 Pending Resolved Rejected
new Promise(cb)
的状态Pending(进行中) ===> Resolved(已完成)
Pending(进行中) ===> Rejected(已失败)
-
两个原型方法:
-
Promise.prototype.then()
-
Promise.prototype.catch()
-
实例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /** * 两个原型方法: * - Promise.prototype.then() //捕获成功的回调 * - Promise.prototype.catch() //捕获失的回调 */ const imgs = [ 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1074716685,4059509041&fm=27&gp=0.jpg', 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=4159865632,111097556&fm=27&gp=0.jpg', 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2551503021,1875446291&fm=27&gp=0.jpg' ]; const p = new Promise(function (resolve, reject) { // resolve成功的回调函数, reject失败的回调函数 const img = new Image(); img.src = imgs[0]; img.onload = function () { resolve(this); }; img.onerror = function () { reject(new Error('图片加载失败')); } }); // 加载完成之后会调用then 方法 p.then(function (img) { // 成功回调 document.body.appendChild(img); // },function(err){ // 失败回调,但不推荐使用 // console.log(err) }).catch(function (err) { // 失败回调,推荐使用 console.log(err) }) </script> </body> </html>
-
-
两个常用静态方法:
-
Promise.all()
:可以将多个Promise
实例包装成一个新的Promise
实例-
当所有的
Promise
实例的状态都变成resolved
,Promise.all
的状态才会变成resolved
,此时返回值组成一个数组,传递给then
的resolve
函数 -
只要其中一个被
rejected
,Promise.all
的状态就会被rejected
,此时第一个被reject
的实例的返回值,会传递给p的回调函数 -
实例
function loadImg(url) { const p = new Promise(function (resolve, reject) { // resolve成功的回调函数, reject失败的回调函数 const img = new Image(); img.src = url; img.onload = function () { resolve(this); }; img.onerror = function () { reject(new Error('图片加载失败')); } }); return p; } const allDone = Promise.all([loadImg(imgs[0]),loadImg(imgs[1]),loadImg(imgs[2])]); allDone.then(function(datas){ datas.forEach(function(item, i){ document.body.appendChild(item); }) }).catch(function(err){ console.log(err) })
-
-
Promise.resolve()
-
第一种用法:参数是Promise实例,将不做任何修改,原封不动地返回这个实例
function loadImg(url) { const p = new Promise(function (resolve, reject) { // resolve成功的回调函数, reject失败的回调函数 const img = new Image(); img.src = url; img.onload = function () { resolve(this); }; img.onerror = function () { reject(new Error('图片加载失败')); } }); return p; } // 参数是Promise实例,将不做任何修改,原封不动地返回这个实例 Promise.resolve(loadImg(imgs[0])).then(function(img){ document.body.appendChild(img); })
-
第二种用法:将对象转化为Promise对象,然后立即执行thenable 对象的then方法
Promise.resolve({ then(resolve, reject){ const img = new Image(); img.src = imgs[1]; img.onload = function(){ resolve(this); } } }).then(function(img){ document.body.appendChild(img); })
-
第三种用法:参数是一个基本数据类型或者不传参数,那么返回一个状态为resolved的Promise对象
// 传递一个基本数据类型 Promise.resolve('fengya').then(function(str){ console.log(str); // fengya }) // 不传参数 const p =Promise.resolve(); console.log(p); // Promise {<resolved>: undefined}
-
-
11.Generator
函数
-
基本概念 :在形式上,
Generator
是一个普通函数,但是有两个特征。-
function命令与函数之间有一个星号。
-
函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /** * 基本概念 :在形式上,Generator是一个普通函数,但是有两个特征。 * - function命令与函数之间有一个星号。 * - 函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态。 */ /** * 输出结果:先输出 1,然后输出 {value: undefined, done: false}, 1秒过后输出3 */ function* fn(){ console.log(1); // yield console.log(2); let val = yield getData(); // 接收传递出来的数据 console.log(val); console.log(3) } function getData(){ setInterval(function(){ // 此处可以调用ajax获取数据,然后将数据通过next参数传递出去,例如此处传递100 f.next(100); },1000) } let f = fn(); // 调用函数 f.next(); </script> </body> </html>
-
封装自动调用函数co
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> function* fn() { console.log(1); let val = yield getData(); // 接收传递出来的数据 console.log(val); console.log(3) } function getData() { // 将其包装为Promise对象 return new Promise((resolve, reject) => { setTimeout(function () { resolve(100); }, 1000); }); } function co(callback){ let cb = callback(); // 封装一个next方法调用f // 用co 递归调用cb的next方法从而实现自动调用f.next function next(d){ let result = cb.next(d); // 返回结果{value,done} if(result.done){ return; // 所有的f.next() 执行结束了 } // 获取数据,并传递出去 result.value.then(data => { next(data); // 递归调用next()方法 }) } next(); } co(fn); </script> </body> </html>
-
-
async 方式
-
实例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> async function fn() { console.log(1); try{ //成功捕获 let v = await getData(); console.log(v); } catch(e){ // 失败捕获 console.log(e) }; // console.log(v); // console.log(3) } function getData() { // 将其包装为Promise对象 return new Promise((resolve, reject) => { setTimeout(function () { resolve(100); // 成功返回100 reject('err'); // 错误返回err }, 1000); }); } fn(); </script> </body> </html>
-