本文主是对于es6的总结,其分别从它的使用到它的新特性进行介绍。
es6总结
1. ES6介绍
ES6是ES2015、ES2016、ES2017他们的统称
官方名字:《ECMAScript 2015 标准》=> ES6
2. 包管理机制(npm)
1.npm init -y 初始化nodejs项目
生成一个package.json文件,该文件中保存了项目所有相关信息。
2.全局依赖 cnpm install xxx --global 简写: cnpm i xxx -g
3.局部依赖
(1) 产品依赖 cnpm install xxx --save 简写:cnpm i xxx -S
(2)开发依赖 cnpm install xxx --save-dev 简写:cnpm i xxx -D
3. Bable安装与使用
1.babel-cli安装
cnpm install babel-cli -global
cnpm install babel-preset-latest
2.使用:
(1)在项目的根目录新建文件.babelrc文件
{
“presets”: “latest”
}
(2)转码命令
转码并输出到控制台
babel . /1 - es6.js
转码并输出到新的文件
babel . /1 - es6.js – out - filt 1 - es5.js 简写:babel ./1 - es6.js - o 1 - es5.js
转码并输出整个文件夹
babel src – out - dir dist 简写: babel src - d dist
4. 模块化机制
4.1 CommonJS模块化规范
//导入
let { firstName, lastName } = require('./1-module.js');
//等同于
let obj = require('./1-module.js');
let first = obj.firstName;
let last = obj.lastName;
//对象解构(模式匹配)
let data = {
username: '张三',
age: 12
}
let { username, age } = data;
//导出
let name = 'zhangsan';
let num = 1;
module.exports = {
name,
num
}
4.2 AMD模块化规范
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。
它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
4.3 ES6模块化规范
//导入
import { firstName, lastName, a, post } from './module1.js';
import { firstName as first, lastName as last } from './module1.js';
//导入默认
import data from './module1.js'
//导入全部
import * as data from './module1.js';
//导入执行(模块加载)
import './module1.js';
//导入第三方模块(去项目的根目录找node_modules)
import 'babel-preset-latest';
//导出
export { firstName, lastName };
export { firstName as fist, lastName as last };//重命名导出
export let a = 1;//导出a
export function post() {
} //导出post函数
//默认导出(一个模块有且只能有1个默认导出,默认导出与普通导出可以共存)
export default {
}
4.4 CommonJS与ES6模块化规范区别
①CommonJS
var a = {age:12}; a.age = 13
b = a;
对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
当使用require命令加载某个模块时,就会运行整个模块的代码。
当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
运行时加载
② ES6
ES6模块中的导入导出值属于【动态只读引用】。
对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
编译时输出接口。
ES6模块与CommonJS模块的差异:
1. CommonJS 模块输出的是一个值的拷贝/复制,ES6 模块输出的是值的引用。
2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口.
1.CommonJS模块化规范 值得拷贝
1.1导出模块
let firstname='ren';
let lastname='terry';
setTimeout(()=>{
firstname:'zhao'
},2000)
module.exports={
firstname,
lastname
};
1.2导入模块
let {firstname,lastname}=require('./module1.js');
console.log(firstname,lastname);
setTimeout(()=>{
console.log(firstname,lastname);//ren terry
},4000)
2.ES6模块 值得引用
2.1导出模块
let firstname='ren';
let lastname='terry';
setTimeout(()=>{
firstname='zhao'
},2000)
export {
firstname,
lastname
};
2.2导入模块
import {firstname,lastname} from './module3.js';
console.log(firstname,lastname);
setTimeout(()=>{
console.log(firstname,lastname);//zhao terry
},4000)
5. es6新特性
5.1 let const 变量/常量声明
5.1.1 let 用于声明一个变量
①变量不会提升,即在变量声明之前无法使用该变量;
②不能重复声明;
③具有块级作用域,只在当前作用域有效。
5.1.2 const 用于声明一个常量
①变量声明不会被提升,即在变量声明之前无法使用该变量;
②不能重复声明;
③具有局部作用域,即const声明的变量只能在对应代码块中使用;
④const声明的变量在声明的时候就需要赋值,并且只能赋值一次,不能修改。
注意
:这里const变量不能修改的是存储的地址值不可被修改
,意思就是简单类型的数据是不能修改的。复合类型的数据(主要是对象和数组)const只能保证这个指针是固定的
,而这个具体的对象实例包含的属性是可以被修改的
。
//1.es6 不存在变量声明的提升
console.log(name);
let name = "zhangsan";
//所处作用域内部可以访问外部,但外部不可以访问内部,不可以重复声明
{
let name1="lisi"
console.log('内部',name1,);
}
//2.局部作用域,标识{}
//第一种
for(let i=1;i<10;i++){
console.log(i);
}
console.log('for循环',i); // let 在外部,不能被访问到 会报错
//第二种
for(var i=1;i<10;i++){
console.log(i);
}
console.log('for循环',i); //var 在外部,可以被访问到 10
console.log('外部',name);
function myFun (){
var age= 12;//当前此变量被声明了,但未使用
console.log(name);
}
myFun();
console.log(age); //error 在函数内部定义了,但与函数外部无关
//3.不允许重复声明 let const都不允许重复声明
const b=1;
const b=true; //error
//4.const声明的变量声明是就要赋值,而且不允许修改
const c = 0;
5.2 解构(模式匹配)
5.2.1 介绍
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。解构的本质属于“模式匹配”,只要等号两边的模式相同
,左边的变量就会被赋予对应的值
。如果解构不成功,变量的值就等于undefined。
5.2.2 数组解构
//1.等号左边的变量放到中括号内部,匹配右侧数组中的元素。
let [a,b,c,d,e]=[1,2,3,[4,5],6];
console.log(a,b,c,d,e); //1 2 3 [4,5] 6
//2.不完全解构
let [a,b,c,[d],e]=[1,2,3,[4,5,6],7];
console.log(a,b,c,d,e);//1 2 3 4 7
//3.拓展运算符解构(用在=左边聚合作用,返回一个新数组)
let [a, ...b] = [1,2,3];
console.log(a,b);//1 [2,3]
//4.默认值解构
//4.1
let [a,b,c] = [1,2]
console.log(a,b,c); //1 2 undefined
//此时设置默认值为10
let [a,b,c=10] = [1,2]
console.log(a,b,c); //1 2 10
//有值默认值就不生效
let [a,b,c] = [1,2,3]
console.log(a,b,c); //1 2 3
//4.2 //函数返回值作为默认值 注意:只要是函数,就有返回值
function myFun(){
console.log('函数');
return 100;
}
let [a,b,c=myFun()]=[1,2]
console.log(a,b,c); //1 2 undefined(没有返回值的时候)
console.log(a,b,c); //1 2 100
5.2.3 对象解构
等号左边的变量放到大括号内部,匹配右侧对象中的元素。对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
//对象的解构,使用对象的简写形式(省略key,保留value对应的值)
let {name:username,age:userage} ={name:"张三",age:12};
console.log(username,userage);//张三 12
//重命名 将x重命名为z
let {x:z}={x:'zhangsan',y:16};
console.log(z);//zhangsan
//嵌套解构
let obj ={p:['hello',{y:'world'}]};
let {p:[a,{y:b}]} =obj;//a:'hello' b:'world'
console.log(a+b);//helloworld
//默认值解构
let {name,age,gender = "male"}={name:'张三',age:12}
console.log(name,age,gender); //张三 12 male
let {name,age,gender}={name:'张三',age:12,gender:'男'}
console.log(name,age,gender); //张三 12 男
//拓展运算符解构
let {a, ...b} ={a:1,b:2,c:3}
console.log(a,b); //1 { b: 2, c: 3 }
//使用拓展运算符可以解构数组和对象,并返回解构后的新数组或者新对象
let obj={a:1,b:2,c:3}
let {...b} =obj
console.log(b); //{ a: 1, b: 2, c: 3 }
console.log(obj == {...b});//false
5.2.4 字符串解构
//字符串既可以使用[]解构,也可以使用{}解构
//使用[]解构字符串内部的字符
//使用{}解构字符串原型中的方法和属性
let [a,b,c,d,e] ="hello";
console.log(a,b,c,d,e); //h e l l o
//基本类型没有实例和方法,使用JavaScript来调用
//对象解构:从字符串原型中解构属性和方法
let {toString,valueOf,length}='hello';//相当于把‘hello’当成String基本包装器类型
console.log(toString,valueOf,length);//[Function: toString] [Function: valueOf] 5
//将string字符串转数组的方法 拓展运算符
let [...arr] ="hello";
console.log(arr); //[ 'h', 'e', 'l', 'l', 'o' ]
5.2.5 number解构和boolean解构
可以获取到数值包装器构造函数原型中指定的方法。
// 数字和Boolean只能使用{}解构其原型对象中的方法和属性
//而不能使用[]解构 因为它们都是不可迭代 string可迭代
let { valueof } = 10
console.log(valueof);//[Function: valueOf]
let { valueOf } = 12 valueOf === Number.prototype.valueOf()
let { valueOf } = true valueOf === Boolean.prototype.valueOf()
5.3 对象
5.3.1 简写
let name="张三";
let obj={
name,
//以前写法:
// sayName:function(){
// console.log(this.name);
// }
sayName(){
console.log(this.name);
}
}
obj.sayName();//张三
5.3.2 API拓展-静态方法(通过构造函数来调用)
1.Object.is(a,b) 判断a,b两个值是否一样,类似于===
2.Object.assign() 深复制或者合并对象
3.Object.assign(obj1, obj2) 深复制,将obj2复制给obj1
4.Object.assign(obj1, obj2, obj3) 对象合并,将obj2、obj3合并后复制给obj1
5.Object.setPrototypeOf(obj1, obj2) 为目标对象设置原型,将obj2设置成obj1的原型
6.Object.getPrototypeOf() 获取目标对象的原型
7.Object.keys() 返回对象属性名组成的数组
8.Object.values() 返回对象属性值组成的数组
9.Object.entries() 返回对象[属性名, 属性值]组成的二维数组
API具体实现:
//1.Object.is(a,b)比较两个值是否一样
console.log(1===1);//true
console.log(Object.is(1,1));//true
console.log(+0 === -0);//true
console.log(Object.is(+0,-0));//false
console.log(NaN === NaN);//false
console.log(Object.is(NaN,NaN));//true
//2.assign() //第一个参数是目标对象 第二个参数是复制的内容 返回值是第一个参数
let obj1={};
let obj2={
name:'zhangsan',
age:12
};
let obj3={
name:'lisi', //这里的name与obj2的name重复,起作用的是这里,"后来者居上"
gender:'男'
};
Object.assign(obj1,obj2);// 2-3.深复制 将obj2复制给obj1
console.log(obj1); //{ name: 'zhangsan', age: 12 }
console.log(obj1 === obj2); //false
//对象合并的结果
let a={
};
let b={
name:"zs",
age:12
};
let c={
name:"lis",
gender:'男'
}
let result =Object.assign(a,b,c);
console.log(result); //{name:'lisi',age:12,gender:'男’}
Object.assign(obj1,obj2,obj3);//4.三个参数代表的是合并对象obj2,obj3到obj1当中并返回obj1对象
console.log(obj1);//{ name: 'lisi', age: 12, gender: '男' }
//5.获取原型对象中的方法
Object.setPrototypeOf(obj1,obj2);
// 原型 实例
console.log(obj1.__proto__);//{ name: 'zhangsan', age: 12 }
//构造函数
console.log(obj1.constructor.prototype);//[Object: null prototype] {}
// 6.通过getPrototypeOf方法
console.log(Object.getPrototypeOf(obj1));//{ name: 'zhangsan', age: 12 }
//7-9
let keys =Object.keys(obj1); //获取所有属性名组成的数组
let values =Object.values(obj1); //获取所有属性值组成的数组
let entries =Object.entries(obj1); //获取键值对组成的数组
entries.forEach((item,index)=>{
console.log(item[0],item[1]);
})
5.4 数组
5.1.1 拓展运算符(★)
...用在=左边,聚合的作用(对象、数组的解构)
...用在=右边,展开(剥离)的作用
let arr1 = [ ‘tom’ , ‘lerry’ , ‘jacky’ ];
let arr2 = [ …arr1 , ‘zhangsan’ , ‘lisi’ ] ;
console.log(arr2); // [ ‘tom’, ‘lerry’, ‘jacky’, ‘zhangsan’, ‘lisi’ ]
...(拓展运算符)也可以用在函数的形参中,作为rest参数,也叫拓展运算符的逆运算
5.4.2 API拓展
Array.from() 将其他类型数据转成数组
Array.of() 创建数组实例,实参就是数组的元素
Array.prototype.find() 返回满足条件的第一个元素,参数为函数
Array.prototype.findIndex() 返回满足条件的第一个元素的索引,参数为函数
Array.prototype.includes() 判断当前数组中是否包含某个元素,参数为元素,返回true/false
Array.prototype.fill() 填充数组(所有元素填充),修改数组原值
Array.prototype.keys() 索引的迭代器对象
Array.prototype.values() 元素的迭代器对象
Array.prototype.entries() 键值对的迭代器对象
API具体实现:
//1.Array.from() 将其他类型数据转成数组
let arrObj={
0:"张三",
1:"李四",
2:"王五",
length:3
}
let arr1 = Array.from(arrObj);
console.log(arr1);//[ '张三', '李四', '王五' ]
//2.Array.of() 创建数组实例,实参就是数组的元素
let arr1 = new Array(3);//这里的3为默认为长度 [empty,empty,empty]
let arr1= Array.of(3);//这里的3是数组元素的长度
console.log(arr1);//[3]
//3.Array.prototype.find() 返回满足条件的第一个元素,参数为函数
let arr1= Array.of(1,2,3,4,5,6);
let result =arr1.find((item,index)=>{
//语法约束
return item>2;//查找下标大于2
})
console.log(result);//3
//4.findIndex 返回满足条件的第一个元素的索引,参数为函数 找不到返回-1
let result =arr1.findIndex((item,index)=>{
return item>2;
//return item>6; 找不到返回-1
})
console.log(result);//2
//5.includes() 判断当前数组中是否包含某个元素,参数为元素,返回true/false
let result1=arr1.includes(1);//判断当前数组中是否还有1
console.log(result1);//true
//6.fill() 填充数组(所有元素填充),修改数组原值
let result2 = arr1.fill(7);
console.log(result2);//[ 7, 7, 7, 7, 7, 7 ]
console.log(result2 === arr1);//true
console.log(arr1);//[ 7, 7, 7, 7, 7, 7 ]
/*7.Array.prototype.keys() 索引的迭代器对象
Array.prototype.values() 元素的迭代器对象
Array.prototype.entries() 键值对的迭代器对象
*/
let keys =arr1.keys();
let values =arr1.values();
let entries =arr1.entries();
//迭代器对象
console.log(keys,values,entries);//Object [Array Iterator] {} Object [Array Iterator] {} Object [Array Iterator] {}
console.log(Array.from(keys));//[ 0, 1, 2, 3, 4, 5 ]
console.log(Array.from(values)); //[ 1, 2, 3, 4, 5, 6 ]
console.log(Array.from(entries));//[ [ 0, 1 ], [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ], [ 5, 6 ] ]
5.5 函数
5.5.1 函数参数
//1.默认值
let myFun1 = function(a,b,c=10){
console.log(a,b,c); //null 2 10
//类数组对象,可迭代的 可通过Array.from来转换数据类型
console.log(arguments);//[Arguments] { '0': null, '1': 2 }
}
// myFun1(null,2);//null 2 undefined(没有默认值)
myFun1(null,2);//null 2 10
//2.对象的解构 {}
let myFun2 = function({a,b,c}){
//根据对象的key值
console.log(a,b,c);//tom 1 2
}
//类似于 let {a,b,c} = {a:1,c:2,b:'tom'}
myFun2({b:1,c:2,a:'tom'});
//3.数组 []
let myFun3 = function ([a,b,c]) {
console.log(a,b,c);//undefined 2 3
}
myFun3([,2,3]);//数组里面可以存空
//4.获取es6的实参
//...运算符,用在函数形参位置,叫做rest参数,也叫拓展运算符的逆运算
let myFun4 = function (a,...b) {
console.log(a,b);
}
myFun4(1,2,3,4,5,6);//1 [ 2, 3, 4, 5, 6 ]
5.5.2 箭头函数(★)
①this指向
普通函数:this指向调用者,没有调用者指向global
箭头函数:没有自己的this,this指向’声明时’外部作用域的this
②箭头函数写法
极简写法(形参只有一个时,可省略括号,只有一句返回语句时,箭头后面可直接写表达式)
let result = arr.filter(item => item > 5)
箭头函数完整写法=>
let result = arr.filter((item) => {
retrun item > 5
})
③ES5写法=>
let result = arr.filter(function(item) {
retrun item > 5
})
let arr = ['tom','terry','jacky'];
//1.普通函数
let result1 = arr.filter(function (item) {
return item == 'tom';
})
//return后产生新的数组
console.log(result1);//[ 'tom' ]
//2.箭头函数
let result2 = arr.filter((item) => {
return item == 'tom';
})
console.log(result2); //[ 'tom' ]
//3.有且只有一条返回语句--->箭头函数的极简形式
let result3 = arr.filter(item => item=='tom');
console.log(result3); //[ 'tom' ]
//箭头函数指向声明时外部作用的this
let arrowFun = () =>{
console.log(this);
}
let obj={
name:'张三',
age:12,
gender:'male',
// sayName:()=> {
// console.log(this);//{}
// },
// sayName(){
// return () =>{
// console.log(this);//{} 指向声明时外部作用域的this
// }
// },
//return后面的箭头函数就相当于前面全局声明的arrowFun函数
sayName () {
return arrowFun;
}
}
this.name = 'lisi';
console.log('全局',this); //全局 { name: 'lisi' }
//进行新赋值 gender 直接使用module.exports.gender
module.exports.gender = 'male'; //module.exports { name: 'lisi', gender: 'male' }
//不能是module.exports给它地址,这样会改变其指向,比如:
// module.exports = {
// a:1,
// b:2
// }
//console.log('module.exports',module.exports);//module.exports { a: 1, b: 2 }
// this指向就是当前模块的默认导出
console.log('module.exports',module.exports);//{ name: 'lisi', gender: 'male' }
obj.sayName()();//执行obj-->sayName-->arrowFun函数
5.6 迭代器Iterator
(一) 定义
实现了iterator接口的数据,即是可迭代的数据。它是一种接口,为各种不同的数据结构
提供统一的访问机制
。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
(二) Iterator 的作用
(1)为各种数据结构,提供一个统一的、简便的访问接口;
(2)使得数据结构的成员能够按某种次序排列,通过调用.next()方法,指向下一个成员
;
(3)ES6创造了一种新的遍历命令for...of循环
,Iterator接口主要供for...of消费
。
(三) Iterator 的遍历过程
1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说这个遍历器对象本质上,就是一个指针对象。
2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
(四)for of
使用,可以遍历实现了iterator接口的数据
for-of实现原理就是调用迭代器的next()方法,第一次调用将指针指向数据结构的第一个成员,依次调用依次指向,直到没有成员可以指向,done为true
具体的迭代过程:
1.创建一个指针对象,指向当前的数据结构起始位置;
2. 第一次调用指针对象的next方法,指向数据结构的第一个成员;
3. 第二次调用指针对象的next方法,指向数据结构的第二个成员;
4. 直到done为true,指向数据结构的结束位置;
for of 遍历的是键值对中的值
for in 遍历的是键值对中的键
(五) 访问
{value: 1, done: false}
{value: 2, done: false}
{value: undefined, done: true}
iterator.next() 通过调用.next方法,可以使迭代器对象中的指针向下移一位
value有值,done就为false
value为undefined时,done为true
(六) for of 实现(.next())
(七) 原生具备 Iterator 接口的数据结构如下
Array、Map、Set、String、arguments、NodeList 等
具体实现:
let arr1 = ['tom','vicky','jacky'];
let keys =arr1.keys();
let values =arr1.values();
let entries =arr1.entries();
//1.通过 ".next" 每调用一次就向下指引一次
console.log(typeof(values.next())); //object对象
console.log(values.next());//{ value: 'tom', done: false }
console.log(values.next());//{ value: 'vicky', done: false }
console.log(values.next());//{ value: 'jacky', done: false }
console.log(values.next());//{ value: undefined, done: true }
//当done为true后,不在执行
// 2.使用while循环 for of循环的重构(实现) 内部工作机制/逻辑
let item; //保证item是个对象 将value.next取出的值传给item
//values是迭代对象,执行下一步后,将取出来的值赋值给item,一般情况下,item的值为false,
//为了整个循环可以执行,对item = values.next()进行取反,然后执行,等到取反为true,执行结束。
while(!(item = values.next()).done){
console.log(item.value);//tom vicky jacky
}
//for of循环
for(let value of values){
console.log(value);//tom vicky jacky
}
for(let entry of entries){
console.log(entry);//[ 0, 'tom' ] [ 1, 'vicky' ] [ 2, 'jacky' ]
}
5.7 Set集合
5.7.1 定义
set 类似于数组
,但是成员的值都是唯一的,没有重复的值。set 本身是一个构造函数,用来生成 set 数据结构展。set 构造函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
5.7.2 特性
不允许重复值出现
应用:数组去重 Array.form(new Set(arr))
数组去重方法: https://blog.youkuaiyun.com/hey_myx/article/details/120536673
5.7.3 API
set.prototype.size 返回Set实例的成员总数。
set.prototype.add(value) 添加某个值,返回Set结构本身
set.prototype.delete(value) 删除某个值,返回一个布尔值,表示删除是否成功。
set.prototype.has(value) 返回一个布尔值,表示该值是否为Set的成员。
set.prototype.clear() 清除所有成员,没有返回值。
set.prototype.keys() 返回键名的遍历器
set.prototype.values() 返回键值的遍历器
set.prototype.entries() 返回键值对的遍历器
set.prototype.forEach() 使用回调函数遍历每个成员
对set-API具体实现如下:
//数组去重
let arr= [1,2,3,5,4,3,2,1,3,1];
let set = new Set(arr);
//添加值
set.add(100);
//console.log(set);//未添加值前 Set(5) { 1, 2, 3, 5, 4 }
console.log(Array.from(set));//[ 1, 2, 3, 5, 4 ,100]
console.log(set.size); //6
console.log(set.delete(1));//删除1
console.log(set.has(1));//检测是否还有1
// console.log(set.clear());//清除所有对象
console.log(set);//Set(5) { 1, 2, 3, 5, 4 ,100}
let keys =set.keys();
let values =set.values();
let entries =set.entries();
//console.log(entries.next());//{ value: [ 2, 2 ], done: false }
//这里的forEach保存在set.prototype原型中 ,数组中的保存在数组原型中
set.forEach((value)=>{
console.log(value);
})
5.8 Map集合
5.8.1 定义
类似于对象
,key-value对应的集合。也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。Map 可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
5.8.2 特点
key值不局限于字符串,可以是任意数据类型
5.8.3 API
Map.prototype.size 返回 Map 结构的成员总数。
Map.prototype.set(key, value) set方法设置键名key对应的键值为value,然后返回整个map结构。如果key已经有值,则键值会被更新,否则就新生成该键。
Map.prototype.get(key) get方法读取key对应的键值,如果找不到key,返回undefined。
Map.prototype.has(key) has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
Map.prototype.delete(key)delete方法删除某个键,返回true。如果删除失败,返回false。
Map.prototype.clear() 清除所有成员,没有返回值
Map.prototype.keys() 返回键名的遍历器
Map.prototype.values() 返回键值的遍历器
Map.prototype.entries() 返回键值对的遍历器
Map.prototype.forEach() 使用回调函数遍历每个成员
对map-API的具体实现如下:
let obj1 = {
name:'张三',
age:12,
gender:'male'
}
let map = new Map(Object.entries(obj1));
map.set(true,0); //true => 0,
map.set({a:1,b:2},['tom','jacky']);//{ a: 1, b: 2 } => [ 'tom', 'jacky' ]
console.log(map.size);//5
console.log(map.get(true));//0
console.log(map);//Map(3) { 'name' => '张三', 'age' => 12, 'gender' => 'male' )
map.forEach((value,key)=>{
console.log(value,key);
})
5.9 class类
5.9.1 介绍
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。ES6 的class可以看作是构造函数或者一个语法糖。
语法糖:
具有特殊功能的代码写法,内部封装了一些方法,让一些复杂代码的编写及其用法变得简单。
5.9.2 构造器
constructor方法是类的默认方法
,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class Person{ } => class Person{
constructor(){}
}
5.9.3 实例方法、属性
定义在类体中的方法称为实例方法。本质上该方法应该是声明在Person.prototype中,可供所有的实例调用,因此称为实例方法。
以上概念的具体实现:
// 定义一个类
class Person{
//静态属性 使用static关键字修饰
static weight = "50KG"
// 类的默认方法constructor 显式添加 调用添加的构造器
constructor(name,age,gender){
// 维护实例的私有属性
this.name=name;
this.age=age;
this.gender=gender;
}
// 其实是存放在构造器函数的原型对象当中的
// 公有方法--供每个方法去使用
// 对于实例方法 不需要任何关键字修饰
sayName(){
//this.name 实例来调用sayName
console.log('my name is',this.name);
}
//静态方法 使用static关键字修饰
static sayWeight(){
console.log('my weight is',this.weight);
}
}
// 实例化 传递实例的属性值
let tom = new Person('tom',20,'男');
console.log(tom);//Person { name: 'tom', age: 20, gender: '男' }
tom.sayName();//my name is tom
//通过class类 调用静态属性和方法
console.log(Person.weight);//50KG
Person.sayWeight();//my weight is 50KG
5.9.4 静态方法、属性
通过static关键字来定义静态属性和静态方法。静态属性和静态方法是定义在类【构造函数】上的,所以可以通过类【构造函数】直接访问。在静态方法中,this指向当前类【构造函数】。
// 实例方法实例属性写在哪
// 实例方法和属性可以由类构造函数创建的实例调用呀
// 静态属性和静态方法写在哪
// 类构造函数调用
class Person{
// 实例属性 实例方法
constructor(name){
// 维护实例的私有属性
this.name=name;
}
// 实例的私有属性
test=['hello'];
sayName(){
console.log(this.name);
}
// 静态属性
static personAttr='hello';
// 静态方法
static personMethod(){
console.log('我是静态方法');
}
}
let p=new Person();
let p1=new Person();
p.test.push('hello');//私有的 只能给p添加
console.log(p);
console.log(p1,'-------------');
// 静态属性和静态方法只能有类属性来调用
console.log(Person.personAttr);
console.log(Person.personMethod());
5.9.5 继承(★)
①ES5继承
实例使用属性和方法
1.从实例对象本身查找属性或者方法
2.如果实例没有,从构造函数的原型对象中找
3.如果还没有,从父构造函数的原型对象中找
1.经典继承又称构造函数继承
function Animal(type,age,weight){
this.type=type;
this.age=age;
this.weight=weight;
}
Animal.prototype={
constructor:Animal,
sayType:function(){
console.log(this.type);
}
}
function Dog(type,age,weight,name,color){
// 经典继承又称为构造函数继承
Animal.call(this,type,age,weight);
this.name=name;
this.color=color;
}
处理完构造函数处理原型对象
2.原型链继承
//子构造函数的原型指向父构造函数的原型对象
Dog.prototype=new Animal();
Dog.prototype.constructor=Dog;
Dog.prototype.sayColor=function(){
console.log(this.color);
}
var d1=new Dog('狗',1,'10kg','40cm','可乐','white');
console.log(d1);//Dog {type:'狗', age:1, weight:'10kg', length:'40cm', name:'可乐', color:'white'}
d1.sayType();
d1.sayColor();
②ES6继承
class可以通过extends关键字实现继承,子类可以没有构造函数,系统会默认分配。子类提供了构造函数则必须要显式调用super。
// 类是一个对象 父类构造函数
class Animal{
// 约束静态属性
static animalAttr = 'Animal静态属性';
constructor(name,age){
this.name =name;
this.age=age;
// console.log('这是父构造器');
}
animalFun(){
console.log('Animal实例方法');
}
static animalStaFun(){
console.log('Animal静态方法');
}
}
// 子类构造函数继承自Animal
class Dog extends Animal{
constructor(name,age,color,weight){
super(name,age);//传继承属性
this.color=color;
this.weight=weight;
// console.log('这是子类构造器');
}
}
//1.继承Animal类的实例属性和实例方法 原型链继承
let dog = new Dog('二狗',1,'black','10kg');
console.log(dog); //Dog { name: '二狗', age: 1, color: 'black', weight: '10kg' }
// 1.继承 子类的原型对象继承自父类的原型对象
dog.animalFun(); //Animal实例方法
// 调用静态方法 --只能由类构造函数去调用
// 2.继承 子类的对象指向父类的对象
// 2.继承Animal类的静态属性和静态方法 构造函数继承
console.log(Dog.animalAttr);//Animal静态属性 Animal静态方法
Dog.animalStaFun();
// 验证
// 子类的原型对象继承自父类的原型对象
console.log(Dog.prototype.__proto__ === Animal.prototype);//true
console.log(dog.__proto__.__proto__ === Animal.prototype);//true
// 类的对象指向父类的对象
console.log(Dog.__proto__ === Animal);//true
5.10 Symbol
5.10.1 介绍
ES6 引入的一种新的原始数据类型Symbol,表示独一无二的值。Symbol函数可以接受参数,表示对于这个唯一值的描述。
5.10.2 使用
Symbol()函数会返回symbol类型(基本数据类型)的值
let s = Symbol()
typeof s ; //’symbol’
symbol类型的值是独一无二的
在对象中使用symbol
用于对象的属性名,就能保证不会出现同名的属性。
这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖
let sy1 = Symbol();
obj[sy1] = 'hello'
let obj2 = {
...obj1,
// key为变量时,需要用[]包裹
[sy1]: 'world'
}
具体实现:
// Symbol用于对象的属性名,就能保证不会出现同名的属性,symbol值代表独一无二的值
//传的参数是对结果的描述
let sy1 =Symbol("sy1");
let sy2 =Symbol("sy2");
console.log(sy1,sy2);//Symbol(sy1) Symbol(sy2)
console.log(sy1 ===sy2);//false
console.log(typeof sy1);//symbol 基本数据类型
let obj1={
name:'zs',
age:12,
gender:'male',
//解决key值冲突,可以使用symbol值,作为一个唯一的值 对key需要加[]
[sy1]:'zhangsan'
}
console.log(obj1[sy1]);//zhangsan
5.10.3 Symbol.for(key)
和Symbol()不同的是,用Symbol.for()方法创建的symbol会被放入一个全局symbol注
册表中。并不是每次都会创建一个新的symbol,它会首先检查给定的 key 是否已经在注册表中了。
假如是,则会直接返回上次存储的那个。否则,它会再新建一个。
比如:调用Symbol.for("cat")30次,每次都会返回同一个Symbol值,但是如果调用
Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。如果想要用同一个变量,可以使用
Symbol.for('name')注册一个全局的,下次如果要获取该symbol值,则再次Symbol.for
('name')。
5.10.4 Symbol.keyFor(sy1)
检测symbol值是否在全局登记过,返回key或者undefined。
返回一个已登记的Symbol类型值的key,用来检测该字符串参数作为名称的Symbol值是否已被登记。
//1.Symbol()
let sy1= Symbol('one');
let sy2= Symbol('one');
console.log(sy1,sy2);//Symbol(one) Symbol(one)
console.log(sy1 === sy2);//false
//2.Symbol.for()
let sy3 = Symbol.for('two');//第一次查找没有直接创建
let sy4 = Symbol.for('two');//第二次查找直接获取
console.log(sy3,sy4);//Symbol(two) Symbol(two)
console.log(sy3 == sy4);//true
//3.Symbol.keyfor
console.log(Symbol.keyFor(sy1));//undefined
//undefiend --》表示并不是在全局中注册
console.log(Symbol.keyFor(sy2));//undefined
console.log(Symbol.keyFor(sy3));//two
console.log(Symbol.keyFor(sy4));//two
eg:
// 1.根据Symbol值去拿key
let key = Symbol.keyFor(sy3);
//2.根据key去找值
let value =Symbol.for(key);
//3.判断是否相等
console.log(value == sy3);//true
5.10.5 应用:消除魔术字符串:
魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。
风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。
//将字符串全部都提到一起
let shapes = {
// 这里必须保证每一项的value值不一样,所以使用到了Symbol
SJX:Symbol("三角形"),
JX:Symbol("矩形"),
YX:Symbol("圆形")
}
function computedArea(shape,options) {
let result = 0;
switch (shape) {
case shapes.SJX:
result = .5*options.width*options.height;
break;
case shapes.JX:
result = options.width * options.height;
break;
case shapes.YX:
result = Math.PI * options.r * options.r;
break;
default:
result = -1;
break;
}
return result;
}
console.log(computedArea(shapes.SJX,{width:10,height:10}));
6 Js严格模式
6.1 严格模式的优缺点
优点
:
提高代码解析和运行速度;
禁用了一些不合理的语法,减少了代码的怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
为未来新版本的Javascript做好铺垫。
缺点
:
某些代码在严格模式下会报错,尤其引入公用与第三方模块的时候需要注意;
有些严格模式的特性在不同浏览器的支持情况不同,需要注意兼容问题;