ECMAScript6(包含es6-es11)
文章目录
一、强大的babel
被称为下一代的JavaScript编译器,可以将es6的代码转换成es5的代码,从而让浏览器获得支持
Babel 的配置文件是.babelrc
,存放在项目的根目录下。使用 Babel 的第一步,就是配置这个文件。
该文件用来设置转码规则和插件,基本格式如下:
{
"presets": [],
"plugins": []
}
presets
字段设定转码规则,官方提供以下的规则集,你可以根据需要安装
# 最新转码规则
$ npm install --save-dev @babel/preset-env
# react 转码规则
$ npm install --save-dev @babel/preset-react
然后,将这些规则加入.babelrc
{
"presets": [
"@babel/env",
"@babel/preset-react"
],
"plugins": []
}
注意,以下所有 Babel 工具和模块的使用,都必须先写好.babelrc
.
命令行转码
Babel 提供命令行工具@babel/cli
,用于命令行转码。它的安装命令如下:
$ npm install --save-dev @babel/cli
基本用法如下:
# 转码结果输出到标准输出
$ npx babel example.js
# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
$ npx babel example.js --out-file compiled.js
# 或者
$ npx babel example.js -o compiled.js
# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
$ npx babel src --out-dir lib
# 或者
$ npx babel src -d lib
# -s 参数生成source map文件
$ npx babel src -d lib -s
babel-node
@babel/node
模块的babel-node
命令,提供一个支持 ES6 的 REPL 环境。它支持 Node 的 REPL 环境的所有功能,而且可以直接运行 ES6 代码。
安装模块命令:
$ npm install --save-dev @babel/node
然后,执行babel-node
就进入 REPL 环境:
$ npx babel-node
> (x => x * 2)(1)
2
babel-node
命令可以直接运行 ES6 脚本。将上面的代码放入脚本文件es6.js
,然后直接运行:
# es6.js 的代码
# console.log((x => x * 2)(1));
$ npx babel-node es6.js
2
babel/register模块
@babel/register
模块改写require
命令,为它加上一个钩子。此后,每当使用require
加载.js
、.jsx
、.es
和.es6
后缀名的文件,就会先用 Babel 进行转码。
安装模块:
$ npm install --save-dev @babel/register
使用时,必须首先加载@babel/register
:
// index.js
require('@babel/register');
require('./es6.js');
然后,就不需要手动对index.js
转码了:
$ node index.js
2
需要注意的是,@babel/register
只会对require
命令加载的文件转码,而不会对当前文件转码。另外,由于它是实时转码,所以只适合在开发环境使用。
polyfill
Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,比如Iterator
、Generator
、Set
、Map
、Proxy
、Reflect
、Symbol
、Promise
等全局对象,以及一些定义在全局对象上的方法(比如Object.assign
)都不会转码。
举例来说,ES6 在Array
对象上新增了Array.from
方法。Babel 就不会转码这个方法。如果想让这个方法运行,可以使用core-js
和regenerator-runtime
(后者提供generator函数的转码),为当前环境提供一个垫片。
安装命令如下。
$ npm install --save-dev core-js regenerator-runtime
然后,在脚本头部,加入如下两行代码。
import 'core-js';
import 'regenerator-runtime/runtime';
// 或者
require('core-js');
require('regenerator-runtime/runtime');
Babel 默认不转码的 API 非常多,详细清单可以查看babel-plugin-transform-runtime
模块的definitions.js文件。
浏览器环境
Babel 也可以用于浏览器环境,使用@babel/standalone模块提供的浏览器版本,将其插入网页。
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
// Your ES6 code
</script>
注意,网页实时将 ES6 代码转为 ES5,对性能会有影响。生产环境需要加载已经转码完成的脚本。
Babel 提供一个REPL 在线编译器,可以在线将 ES6 代码转为 ES5 代码。转换后的代码,可以直接作为 ES5 代码插入网页运行。
二、let和const
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// var 变量提升
/* console.log(a);
var a = 2; */
// 1、let声明变量,没有变量提升
// console.log(a);//报错
// let a = 10;
// console.log(b);
// 2、是一个块级作用域
/* if(1===1){
let b =10;
} */
// console.log(b);//报错
/* var c=2;
var c=4; */
// console.log(c);//4
// 3、不能重复声明
// let c =1;
// let c=4;//报错,c已经被声明了
// console.log(c);
// 1、不会变量提升
// console.log(Max);//报错
// const Max = 30;
// const 2、声明常量,一旦被声明,无法修改const。一旦声明变量,就必须立即初始化,不能留到以后赋值。
// Max =40;//报错
// console.log(max);
// 3、有块级作用域
/* if(1===1){
const Max = 30;
}
console.log(Max); */ //报错
// 4、不能重复声明
// const Max = 30;
// const Max = 40;//报错
/* const person ={
name:'xiaolin'
};
person.name = 'XIAOLIN'; */ //可以修改属性
// 不能修改整个对象
/* person ={
age:20
};
console.log(person.name); */
//如果真的想将对象冻结,应该使用Object.freeze方法。
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;
//除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数。
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
/* var arr = [];
for(var i=0;i<10;i++){
arr[i] = function(){
return i;
}
}
console.log(arr);
console.log(arr[5]()); */ //10
// 作用1:for循环(经典例子)
const arr = [];
for(let i=0;i<10;i++){
arr[i] = function(){
return i;
}
}
//上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
console.log(arr);//
console.log(arr[5]());//5
// 作用2:不会污染全局变量
let RegExp = 10;
console.log(RegExp);
console.log(window.RegExp);
// 建议:在默认情况下用const,而只有你知道变量值需要被修改的情况下使用let
</script>
</body>
</html>
另外,for
循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面代码正确运行,输出了 3 次abc
。这表明函数内部的变量i
与循环变量i
不在同一个作用域,有各自单独的作用域(同一个作用域不可使用 let
重复声明同一个变量)
暂时性死区
有些“死区”比较隐蔽,不太容易发现。
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
上面代码中,调用bar
函数之所以报错(某些实现可能不报错),是因为参数x
默认值等于另一个参数y
,而此时y
还没有声明,属于“死区”。如果y
的默认值是x
,就不会报错,因为此时x
已经声明了。
function bar(x = 2, y = x) {
return [x, y];
}
bar(); // [2, 2]
顶层对象的属性
顶层对象,在浏览器环境指的是window
对象,在 Node 指的是global
对象。ES5 之中,顶层对象的属性与全局变量是等价的。
window.a = 1;
a // 1
a = 2;
window.a // 2
上面代码中,顶层对象的属性赋值与全局变量的赋值,是同一件事。
顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一。这样的设计带来了几个很大的问题,首先是没法在编译时就报出变量未声明的错误,只有运行时才能知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);其次,程序员很容易不知不觉地就创建了全局变量(比如打字出错);最后,顶层对象的属性是到处可以读写的,这非常不利于模块化编程。另一方面,window
对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。
ES6 为了改变这一点,一方面规定,为了保持兼容性,var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
上面代码中,全局变量a
由var
命令声明,所以它是顶层对象的属性;全局变量b
由let
命令声明,所以它不是顶层对象的属性,返回undefined
。
三、模板字符串
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">
</div>
<script>
// 模板字符串:使用tab键上面的反引号,插入变量时使用${变量名}
const oBox = document.querySelector('.box');;
let id= 1,
name="xiaolin";
// oBox.innerHTML = "<ul><li><p id="+id+">"+name+"</p></li></ul>"
// 使用反引号添加
let htmlStr = `
<ul>
<li>
<p id="${id}">${name}</p>
</li>
</ul>`;
oBox.innerHTML = htmlStr;
</script>
</body>
</html>
四、强大的函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 1、带参数默认值的函数
// es5写法
/* function add(a,b){
a = a || 10; //给a添加默认值
b = b || 20; //给b添加默认值
return a+b;
}
consle.log(add()); */
// 直接使用=给a和b添加默认值
function add(a=10,b=20){
return a+b;
}
// console.log(add(30));
// 2、默认的表达式也可以是一个函数
/* function add(a,b=getVal(5)){
return a+b;
}
function getVal(val){
return val +5;
}
consle.log(add(10)); */ //10+10=20
// es5写法
/* function pick(obj){
let result = Object.create(null);
for(let i=1;i<arguments.length;i++){
// console.log(arguments[i]);
result[arguments[i]] = obj[arguments[i]];
}
return result;
}
let book = {
title:'es6hhh',
author:'xiaolin',
year:2022
}
let bookData = pick(book,'author','year','author');
console.log(bookData); */
// 3、剩余参数:由三个点...和一个紧跟着的具体参数指定 ...keys
function pick(obj,...keys){
//...keys 解决了arguments的问题
let result = Object.create(null);
for(let i=1;i<keys.length;i++){
result[keys[i]] = obj[keys[i]];
}
return result;
}
let book = {
title:'es6hhh',
author:'xiaolin',
year:2022
}
let bookData = pick(book,'author','year','author');
// console.log(bookData);
function checkArgs(...args){
//剩余运算符:如果原来是对象:产生的就是对象;如果原来是数组,产生的就是数组
console.log(args);
console.log(arguments);
}
// checkArgs('a','b','c');
// 4、扩展运算符...
// 剩余运算符:把多个独立的参数合并到一个数组中
// 扩展运算符:将一个数组分割,并将各个项作为分离的参数传给函数
/* const maxNum = Math.max(20,30);
console.log(maxNum); */
// 处理数组中的最大值,使用apply
const arr = [10,20,30,40,50,100,200,300];
// console.log(Math.max.apply(null,arr)); //es5
// es6扩展运算符更方便
// console.log(Math.max(...arr));
// 5、es6的箭头函数 *****
// 使用 =>来定义 function(){} 等于()=>{}
/* let add =function(a,b){
return a+b;
} */
/* let add = (a,b)=>{
return a+b;
} */
/* let add = val =>{
return val+5;
} */
// let add = val => (val+5);
// let add = (a,b) =>a+b;
// console.log(add(10,20));
let fn = ()=> 'hello World'+123;
// console.log(fn());
/* let getObj = id =>{
return {
id:id,
name:"xiaolin"
}
} */
let getObj = id =>({id:id,name:"xiaolin"});
let obj = getObj(1);
// console.log(obj);
/* let fn2 = (function(){
return function(){
console.log('hello es6');
}
})(); */
let fn2 = (()=>{
return ()=>{
console.log('hello es6');
}
})();
// fn2();
// 6、箭头函数没有this绑定
// es5中的this指向:取决于调用该函数的上下文对象
/* let PageHandle = {
id:123,
init:function(){
document.addEventListener('click',function(event){
console.log(this);//document
this.doSomethings(event.type);// this.doSomethings is not a function
}.bind(this),false)
},
doSomethings:function(type){
console.log(`事件类型:${type},当前id:${this.id}`);
}
}
PageHandle.init(); */
let PageHandle = {
id:123,
init:function(){
// 箭头函数没有this指向,箭头函数内部this值只能通过查找作用域链
// 一旦使用箭头函数,当前就不存在作用域链
document.addEventListener('click',(event)=>{
console.log(this);//PagrHandle
this.doSomethings(event.type);
})
},
doSomethings:function(type){
console.log(`事件类型:${type},当前id:${this.id}`);
}
}
// PageHandle.init();
// 使用箭头函数的注意事项
// 1、函数内部没有arguments
let getVal=(a,b)=>{
console.log(arguments);//arguments is not defined
return a+b;
}
// console.log(getVal(1,3));
// 2、箭头函数不能使用new关键字来实例化对象
let Person = ()=>{
};
// function函数,也是一个对象,但是箭头函数不是一个对象,它其实是一个语法糖
// let p =new Person();//报错
</script>
</body>
</html>
五、解构赋值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 解构赋值是对赋值运算符的一种扩展
// 针对数组和对象进行操作的
// 优点:代码书写上简洁易读
//如果解构不成功,变量的值就等于undefined。
let node = {
type:'iden',
name:'foo'
}
/* let type =node.type;
let name = node.name; */
// 完全解构
let {type,name}=node;
// console.log(type,name);
let obj={
a:{
name:"张三"
},
b:[1,2,3],
c:'hello es6'
}
// 不完全解构
// let {a} = obj;
// console.log(a);
// 剩余运算符
//如果原来是对象:产生的就是对象;如果原来是数组,产生的就是数组
// let {a,...res}=obj;
// console.log(res);
//注意:对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
//如果变量名与属性名不一致,必须写成下面这样。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
// baz "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
//f 'hello'
//l 'world'
// 运行指定默认值
// let {a,b=30} = {a:20};
// 对数组解构
let arr = [1,2,3];
// let [a,b] = arr;
// console.log(a,b);
// 可嵌套
let [a,[b,b1,b2],c] = [1,[2,3,4],3];
console.log(a,b,b1,b2,c);
</script>
</body>
</html>
注意,ES6 内部使用严格相等运算符(===
),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined
,默认值才会生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
上面代码中,如果一个数组成员是null
,默认值就不会生效,因为null
不严格等于undefined
。
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
function f() {
console.log('aaa');
}
let [x = f()] = [1];
上面代码中,因为x
能取到值,所以函数f
根本不会执行。上面的代码其实等价于下面的代码。
let x;
if ([1][0] === undefined) {
x = f();
} else {
x = [1][0];
}
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError: y is not defined
上面最后一个表达式之所以会报错,是因为x
用y
做默认值时,y
还没有声明
解构赋值的用途
(1)交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
上面代码交换变量x
和y
的值,这样的写法不仅简洁,而且易读,语义非常清晰。
(2)从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
(3)函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
(4)提取 JSON 数据
解构赋值对提取 JSON 对象中的数据,尤其有用。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
上面代码可以快速提取 JSON 数据的值。
(5)函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';
这样的语句。
(6)遍历 Map 结构
任何部署了 Iterator 接口的对象,都可以用for...of
循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。
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
如果只想获取键名,或者只想获取键值,可以写成下面这样。
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
(7)输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");
六、扩展的对象的功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 1、es6直接写入变量和函数,作为对象的属性和方法
// const name = 'xiaolin',age=21;
/* const person ={
name:name,
age:age,
sayName:function(){
console.log(this.name);
}
} */
// 等价于以下写法,同名省略
const name = 'xiaolin',age=21;
const person ={
name,
age,
sayName(){
console.log(this.name);
}
}
// person.sayName();
/* function fn(x,y){
return {x,y};
} */
let fn = (x,y)=>({x,y});
// console.log(fn(10,20));
let cart = {
wheel:4,
set(newVal){
if(newVal<this.wheel){
throw new Error('轮子数太少了');
}
this.wheel=newVal;
},
get(){
return this.wheel;
}
}
// console.log(cart.get());
// cart.set(3);
// console.log(cart.get());
/* const obj = {};
obj.isShow = true;
const name = 'a';
obj[name+'bc']=123;
// console.log(obj);
obj['f'+'bc']=function(){
console.log(this);
}
console.log(obj); */
const name1 = 'a';
const obj={
isShow:true,
[name1+'bc']:123,
['f'+name1]:function(){
console.log(this);
}
};
// console.log(obj);
// 对象的方法
// is() ==== 比较两个值是否严格相等
// 解决NaN的问题
// console.log(NaN===NaN);//false
// console.log(Object.is(NaN,NaN));//true
// assign() 对象的合并*****
// Object.assign(target,obj1,obj2,...);
// 返回合并之后的新对象,浅拷贝
let newObj = Object.assign({},{a:1},{b:2});
// console.log(newObj);
</script>
</body>
</html>
七、Symbol
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 原始数据类型Symbol,它表示的是独一无二的值
// 最大用途:用来定义对象的私有变量
const name = Symbol('name');
const name2 = Symbol('name');
// console.log(name===name2);//false
// console.log(name);//Symbol(name)
// console.log(name2);//Symbol(name)
// console.log(typeof name);//symbol
let s1 = Symbol('s1');
// console.log(s1);
let obj = {
[s1]:'xiaolin'
};
// obj[s1]='xiaolin';
// 如果用Symbol定义的对象中的变量,取值时一定要用[变量名]
// console.log(obj[s1]);
//可枚举可以理解为是否可以被遍历被列举出来,可枚举性决定了这个属性能否被for…in查找遍历到。
/* for(let key in obj){
console.log(key);//无法输出
} */
// [s1]不可枚举,所以输出空数组
// console.log(Object.keys(obj));
// 获取Symbol声明的属性名(作为对象的key)
let s = Object.getOwnPropertySymbols(obj);
// console.log(s);
// console.log(s[0]);
//静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组。
let m = Reflect.ownKeys(obj);
// console.log(m);
</script>
</body>
</html>
八、Map和Set
1、Set
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 集合:表示无重复值的有序列表
let set = new Set();
// console.log(set);
// 添加元素
set.add(2);
set.add('2');
set.add(["hello",1,2,3]);
// 删除元素
set.delete('2');
// 检验某个值是否在set中
// console.log(set.has(2));
// console.log(set);
// 集合的长度
// console.log(set.size);
// key和val是一样的
set.forEach((val,key)=>{
// console.log(val,key);
});
// 将set转换成数组(去除数组中的重复内容)
let set2 = new Set([1,2,3,4,5,4,3]);
// 扩展运算符
let arr = [...set2];
// console.log(arr);
// 1、set中的对象的引用无法被释放
let set3 = new Set(),obj={a:1};
set3.add(obj);
// 释放obj对象
obj = null;
// console.log(set3);//依然存在obj
let set4 = new WeakSet(),obj1={a:1};
set4.add(obj1);
// 释放obj对象
obj1 = null;
console.log(set4);
/*
WeakSet
1、不能传入非对象类型的参数
2、不可迭代
3、没有forEach()
4、没有size属性
*/
</script>
</body>
</html>
2、Map
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// Map类型是键值对的有序列表,键和值是任意类型
/* let map = new Map();
map.set('name','张三');
map.set('age',21);
console.log(map.get('name'));
console.log(map);
map.has('name');
map.delete('name');
console.log(map);
map.clear();
console.log(map);
map.set(['a',1,2],"hello");
console.log(map); */
let m = new Map([
['a',1],
['c',2]
]);
console.log(m);
// WeakMap和WeakSet类似
let map2 = new WeakMap();
</script>
</body>
</html>
九、数组的扩展功能
1、扩展方法1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
// 数组的方法 from() of()
//1、form()将伪数组转换为真正的数组
function add(){
console.log(arguments);
// es5的转换方法
/* let arr = [].slice.call(arguments);
console.log(arr); */
// es6写法
let arr = Array.from(arguments);
console.log(arr);
}
// add(1,2,3);
let lis = document.querySelectorAll('li');
// console.log(lis);
// console.log(Array.from(lis));
// 扩展运算符,将伪数组转换为真正的数组
// console.log([...lis]);
// from()还可以接收第二个参数,用来对每个元素进行处理
let liContents = Array.from(lis,ele=>ele.textContent);
// console.log(liContents);
//2、of()将一组值(任意类型),转换为数组
let arr = console.log(Array.of(3,11,'20',[1,3],{id:1}));
// 3、copywithin()复制3索引开始位置往后的内容替换0索引开始往后的内容
console.log([1,2,3,8,9,10].copyWithin(0,3));//[8,9,10,8,9,10]
// 4、find() findIndex()
// find()找到一个符合条件的数组成员并返回
let num = [1,2,-10,-20,9,2].find(n=>n<0);
console.log(num);
// findIndex()找到一个符合条件的数组成员的索引并返回
let numIndex = [1,2,-10,-20,9,2].findIndex(n=>n<0);
console.log=(numIndex);
</script>
</body>
</html>
2、扩展方法2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//5、entries() keys() values()返回一个遍历器,可以使用for...of循环进行遍历
//keys()对键名遍历
// values()对值进行遍历
// entries()对键值对遍历
console.log(['a','b'].keys());
// 取索引
for(let index of ['a','b'].keys()){
console.log(index);
}
for(let value of ['a','b'].values()){
console.log(value);
}
/* let arr = new Array;
for(let arr of ['a','b'].entries()){
console.log(arr[0],arr[1]);
} */
for(let [index,ele] of ['a','b'].entries()){
console.log(index,ele);
}
let letter = ['a','b','c'];
let it = letter.entries();
/* console.log(it.next().value);
console.log(it.next().value);
console.log(it.next().value);
console.log(it.next().value); */
// 6、includes()返回一个布尔值,表示某个数组是否包含给定的值
console.log([1,2,3].includes(2));//true
console.log([1,2,3].includes(4));//false
// 以前的indexOf()
console.log([1,2,3].indexOf(3));//返回的是索引的位置
</script>
</body>
</html>
十、迭代器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// Iterator
// 是一种新的遍历机制
// 1、迭代器是一个接口,能快捷的访问数据
// 通过Symbol.iterator来创建迭代器,通过迭代器的next()来获取迭代之后的结果
// 2、迭代器是用于遍历数据结构的指针(数据库的游标)
// 使用迭代
const items = ['one','two','three'];
// 创建新的迭代器
const ite = items[Symbol.iterator]();
console.log(ite.next());//{value: "one", done:false} done表示是否遍历完成,false未完成,true为完成
console.log(ite.next());
console.log(ite.next());
console.log(ite.next());
</script>
</body>
</html>
十一、生成器
1、生成器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//generator函数,可以通过yield关键字,将函数挂起,为了改变执行流程提供了可能,同时为异步编程也提供了方案
// 它和普通函数的区别
// 1、function后面 函数名之前有个*
// 2、只能在函数内部使用yield表达式,让函数挂起
function* func(){
console.log("one");
yield 2;
console.log("two");
yield 3;
console.log("end");
}
// 返回一个遍历对象,可以调用next()
let fn = func();
// console.log(fn.next());
// console.log(fn.next());
// console.log(fn.next());
// 总结:generator函数是分段执行的,yield语句是暂停执行,而next()是恢复执行
function* add(){
console.log("start");
let x = yield '2';
console.log("one:"+x);
let y = yield '3';
console.log("two:"+y);
console.log("total:"+(x+y));
// return x+y;
}
const fn1 = add();
/* console.log(fn1.next());//在yield '2'处,停下,{value:'2',done:false}
console.log(fn1.next(20));//在yield '3'处停下,20给x传值,x变为20,输出one:20,{value:'3',done:false};
console.log(fn1.next(30));//30给y传值,输出 two:30,{value:50,done=true};
console.log(fn.return(100)); */
// 使用场景,为不具备Iterator接口的对象提供了遍历操作
function* objectEntires(obj){
// 获取对象的所有key保存到数组[name,age]
const propkeys = Object.keys(obj);
for(const propkey of propkeys){
yield [propkey,obj[propkey]];
}
}
const obj = {
name:"小林",
age:21
}
// obj[Symbol.iterator] = objectEntires;
// console.log(obj);
for(let [key,value] of objectEntires(obj)){
console.log(`${key}:${value}`);
}
</script>
</body>
</html>
2、Generator的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<script>
/*
Generator 部署ajax操作,让异步代码同步化
*/
// 回调地狱
// $.ajax({
// url:'http://127.0.0.1:5500/test.html',
// method:'get',
// success(res){
// console.log(res);
// // 继续发送请求
// /* $.ajax({
// url:'',
// method:'',
// success(res1){
// console.log(res1);
// // 继续发送请求
// }
// }) */
// }
// });
/* const a = 10;
const b = 20; */
/* function* main(){
let res = yield request('url');
console.log(res);
// 执行后面操作
console.log("数据请求完成,可以继续操作");
}
const ite = main();
ite.next();
function request(url){
$.ajax({
url,
method:'get',
success(res){
ite.next(res);
}
});
} */
// 加载locaing...页面
// 数据加载完成...
// loading关闭掉
function* load(){
loadUI();
yield showData();
hideUI();
}
let itLoad = load();
itLoad.next();
function loadUI(){
console.log("加载loading...页面");
}
function showData(){
setTimeout(()=>{
console.log("数据加载完成");
itLoad.next();
},5000);
}
function hideUI(){
console.log("隐藏loading...页面");
}
/* loadUI();
showData();
hideUI(); */
/* function showData1(){
console.log("加载loading...页面");
setTimeout(()=>{
console.log("数据加载完成");
console.log("隐藏loading...页面");
},1000);
}
showData1(); */
</script>
</body>
</html>
十二、Promise
1、Promise对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// Promise 承诺
// 相对于一个容器,保存着未来才会结束的事件(异步操作)的一个结果
// 各种异步操作都可以用同样的方法进行处理axios
/*
特点:
1、对象的状态不受外界影响,处理异步操作有三个操作 Pending(进行中) Resolved(成功) Rejected(失败)
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果
*/
/* let pro = new Promise(function(resolved,rejected){
// 执行异步操作
let res = {
code:404,
data:{
name: 'xiaolin'
},
error:"请求失败"
}
setTimeout(()=>{
if(res.code===200){
resolved(res.data);
}else{
rejected(res.error);
}
},2000);
});
console.log(pro);
pro.then((val)=>{
console.log(val);
},(err)=>{
console.log(err);
}); */
function timeOut(ms){
return new Promise((resolved,rejected)=>{
setTimeout(()=>{
resolved("请求成功!");
},ms);
});
}
timeOut(5000).then((val)=>{
console.log(val);
});
</script>
</body>
</html>
2、使用Promise封装ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const getJSON = function(url){
return new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.onreadystatechange = handler;
xhr.responseType='json';
xhr.setRequestHeader('Accept','application/json');
// 发送
xhr.send();
function handler(){
// console.log(this.readyState);
if(this.readyState===4&&this.status==200){
resolve(this.response);
}else{
reject(new Error(this.statusText));
}
}
});
}
/* getJSON("http://127.0.0.1:5500/test.html")
.then((data)=>{
console.log(data);
},(error)=>{
console.log(error);
}); */
/* getJSON('http://127.0.0.1:5500/test.html')
.then((data)=>{
console.log(data);
}).then(null,err=>{
}); */
// 等价于下面
getJSON('http://127.0.0.1:5500/test.html')
.then(data=>{
console.log(data);
}).catch(err=>{
console.log(err);
});
// then()方法
/*
then()第一个参数就是resolve回调函数,第二个参数是可选的,是reject状态回调函数
then()返回一个新的Promise实例,可以采用链式编程
*/
</script>
</body>
</html>
3、Promise的其他方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="div">正在加载图片....</div>
<script>
// 1、resolve()能将现有的任何对象转换为promise对象
// let p =Promise.resolve('foo');
/* let p =new Promise(resolve=>resolve('foo'));
// console.log(p);
p.then((data)=>{
console.log(data);
}); */
// 2、all()应用:一些游戏类的素材,等待图片、flash、等等静态资源等加载完成后才进行页面初始化
/* let pro1 = new Promise((resolve,reject)=>{
});
let pro2 = new Promise((resolve,reject)=>{
});
let pro3 = new Promise((resolve,reject)=>{
});
let p4 = Promise.all([pro1,pro2,pro3]);
p4.then(()=>{
//三个都成功才成功
}).catch(err=>{
// 如果有一个失败了,就失败了
}); */
const div = document.getElementById('div');
// 3、race():某个异步请求设置超时时间,并且在超时后执行相应的操作
// 请求图片资源
function requestImg(imgSrc){
return new Promise((resolve,reject)=>{
const img = new Image();
img.onload = function(){
resolve(img);
}
img.src = imgSrc;
});
}
function timeOut(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(new Error("图片请求超时"));
},5000);
});
}
Promise.race([requestImg('https://ts2.cn.mm.bing.net/th?id=OIP-C.tV58EvgJSzLG3iOTLzB85QHaEo&w=316&h=197&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2'),timeOut()]).then(data=>{
console.log(data);
div.innerHTML="";
document.body.appendChild(data);
}).catch(err=>{
console.log(err);
div.innerText="您的网速过慢,图片请求失败!";
});
/*
done()和finnally() 无论成功还是失败都会执行
*/
/*
server.listen(3000).then(()=>{
}).finnally(server.stop());
*/
</script>
</body>
</html>
十三、async的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
async作用:使得异步操作更加方便
基本操作 async他会返回一个Promise对象
就可以使用then catch
是generator的一个语法糖
*/
/*
Generator Promise async
1、解决回调地域
2、使得异步操作变得方便
*/
async function f(){
// return await 'hello async';
let s = await 'hello async';
let data = await s.split('');
return data;
}
// 如果async函数中有多个await,那么then函数会等待所有await指令。运行完的结果才去执行
// console.log(f);
f().then(v=>{
console.log(v);
}).catch(e=>{
console.log(e);
});
async function f2(){
// throw new Error("出错了!");
try {
await Promise.reject("出错了");
} catch (error) {
}
return await Promise.resolve("hello");
}
f2().then(v=>console.log(v)).catch(e=>console.log(e));
// 需求,获取和风天气 现在now的数据
const getJSON = function(url){
return new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.onreadystatechange = handler;
xhr.responseType='json';
xhr.setRequestHeader('Accept','application/json');
// 发送
xhr.send();
function handler(){
// console.log(this.readyState);
if(this.readyState===4&&this.status==200){
resolve(this.response);
}else{
reject(new Error(this.statusText));
}
}
});
}
async function getNowWeather(url){
// 发送ajax 获取实况天气
let res = await getJSON(url);
console.log(res);
// 获取HeWeather6的数据 获取未来3-7天的天气
let arr =await res.HeWeather6;
return arr[0].now;
}
getNowWeather("url")
.then(now=>{
console.log(now);
})
</script>
</body>
</html>
十四、class类
1、类的简介
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// es5造类
/* function Person(name,age){
this.name,
this.age=age
}
Person.prototype.sayName=function(){
return this.name;
}
let p1 = new Person('xiaolin',21); */
// console.log(p1);
// es6
class Person{
// 实例化的时候会立即被调用
constructor(name,age){
this.name=name,
this.age
}
/* sayName(){
return this.name;
}
sayAge(){
return this.age;
} */
}
// 通过Object.assign方法一次性向类的原型添加多个方法
Object.assign(Person.prototype,{
sayName(){
return this.name;
},
sayAge(){
return this.age;
}
});
let p2 = new Person('xiaolin',21);
</script>
</body>
</html>
2、类的继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 使用关键字extends
class Animal{
constructor(name,age){
this.name=name;
this.age=age;
}
sayName(){
return this.name;
}
sayAge(){
return this.age;
}
}
class Dog extends Animal{
constructor(name,age,color){
super(name,age);
// Animal.call(this,name,age);
this.color=color;
}
sayColor(){
return this.color;
}
// 重写
sayName(){
console.log("我叫:"+ this.name);
}
}
let d = new Dog('xiaoh',3,'yellow');
console.log(d);
d.sayName();
console.log( d.sayColor());
// 思考:如何让多个类,混入到一个类中
</script>
</body>
</html>
十五、module模块的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
// import obj,{name,age,sayName} from './modules/index.js'
import * as f from './modules/index.js'
// console.log(obj);
console.log(f.default);
console.log(f.name);
// console.log(name,age,sayName());
</script>
</body>
</html>
// es6模块功能主要有两个命令构成:export和import
// export用于规定模块的对外接口,import用于输入其他模块提供的功能
/*
一个模块就是一个独立的文件
*/
export const name ='XIAOLIN';
export const age =18;
export const sayName = function(){
return "my name is xiaolin";
};
function sayName1(){
return "my name is xiaolin";
};
export {sayName1};
/* const obj ={
foo:'foo'
} */
class Person{
constructor(){
}
sayName(){
}
}
// export default obj;
export default Person;
this.age=age;
}
sayName(){
return this.name;
}
sayAge(){
return this.age;
}
}
class Dog extends Animal{
constructor(name,age,color){
super(name,age);
// Animal.call(this,name,age);
this.color=color;
}
sayColor(){
return this.color;
}
// 重写
sayName(){
console.log("我叫:"+ this.name);
}
}
let d = new Dog('xiaoh',3,'yellow');
console.log(d);
d.sayName();
console.log( d.sayColor());
// 思考:如何让多个类,混入到一个类中
</script>
```
十五、module模块的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
// import obj,{name,age,sayName} from './modules/index.js'
import * as f from './modules/index.js'
// console.log(obj);
console.log(f.default);
console.log(f.name);
// console.log(name,age,sayName());
</script>
</body>
</html>
// es6模块功能主要有两个命令构成:export和import
// export用于规定模块的对外接口,import用于输入其他模块提供的功能
/*
一个模块就是一个独立的文件
*/
export const name ='XIAOLIN';
export const age =18;
export const sayName = function(){
return "my name is xiaolin";
};
function sayName1(){
return "my name is xiaolin";
};
export {sayName1};
/* const obj ={
foo:'foo'
} */
class Person{
constructor(){
}
sayName(){
}
}
// export default obj;
export default Person;