前言:ECMAScript和JavaScript是什么关系?
JavaScript由ECMAScript,BOM,DOM组成,前者是后者的规范,后者是前者的实现
一.let和const命令
1)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
2)var声明的变量有变量提升的现象,let声明的变量没有
console.log(foo); //输出undifined
var foo = 2;
console.log(bar); //报错RefrenceError
let bar = 2;
3)暂时性死区
var tmp = 123;
if(true){
tmp = '123'; //referenceError
let tmp;
}
4)不允许重复声明
5)允许在块级作用域中声明函数,声明的行为类似let,在块级作用域之外不可应用
6)do表达式,使得块级作用域可以有返回值
let x = do { let t = f(); t = t * t + 1; }
//x的返回值为(t*t+1)
const命令
const声明一个常量,一旦声明,常量的值不能改变
const实际上保证的不是变量的值不能改动,而是变量指向的那个内存地址不得改动
二、变量的解构赋值
1)数组的解构复制
let [a,b,c] = [1,2,3]; //匹配模式
let [head, ...tail] = [1,2,3,4,5]; //head=1,...tail=[2,3,4,5];
//解构不成功,变量的值会等于undefined
//解构赋值允许指定默认值
let [x=1,y=x] = []; //x=1,y=1
let [x=1,y=x] = [2] //x=1,y=2
2)对象的解构赋值
let {foo,bar} = {foo: "aaaa",bar:"bbb"};
foo //"aaaa"
bar //"bbb"
let { foo:baz } = {foo: "aaa",bar: "bbb"}
baz //aaa
对象的解构赋值的内部机制是先找到同名属性,然后再赋值给对应的变量,真正被赋值的是后者,而不是前者
//如果将一个已经申明的变量用于解构赋值,需要将整个解构赋值语句放在一个圆括号里面
let x;
({x} = {x:1} );
3)字符串的解构赋值
const [a,b,c,d,e] = 'hello';
let {length: len} = 'hello'
len //5
三、函数的扩展
1)指定参数默认值,参数变量默认是申明的,所以不能用let或const再次申明
function log(x,y='world'){
console.log(x,y)
}
console.log(log.length); //1 length指必须传入的参数的个数
log('hello) // hello world
log('hello','china') //hello china
log('hello','') //hello
2)rest参数运算符...,用于获取多余的参数
//arguments变量的写法
function soortNumbers(){
return Array.prototype.slice.call(arguments).sort();
//arguments是一个类似数组多的对象,Array.prototype.slice.call先将其转化为数组
}
//rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
3)对象扩展运算符...
//数组操作的应用
let arr1 = [1,2,3];
let arr2 = [...arr1]; 用扩展运算符只会复制数据
arr2.push(4);
console.log(arr2); [1,2,3,4]
console.log(arr1); [1,2,3]
let arr1 = [1,2,3];
let arr2 = arr1;
arr2.push(4);
console.log(arr2); [1,2,3,4]
console.log(arr1); [1,2,3,4]
3)箭头函数
var sum = (num1,num2) => num11 + num2;
var sum = (num1,num2) =>{
return num1+ num2;
}
箭头函数中this指向定义时所在的对象
四、Promise是实现异步编程的一种解决方案,有pending,fulfilled,rejected三种状态
Promise对象的状态只能从pending——>fulfilled( resolved )或从pending——>rejected
const promise = new Promise(function(resolve,reject){
if(/异步操作成功/){
resolve(value);
}else{
reject(error);
}
})
//Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
promise.then(function(value){
console.log(value); //异步操作成功的回调函数
},function(err){
console.log(err); //异步操作失败的回调函数
})
let state = 1;
function step1(resolve,reject){
console.log('开始第一步');
if(state == 1){
resolve('第一步完成了');
}else{
reject('第一步完成失败')
}
}
function step2(resolve,reject){
console.log('开始第二步');
if(state == 1){
resolve('第二步完成了');
}else{
reject('第二步完成失败')
}
}
function step3(resolve,reject){
console.log('开始第三步'); if(state == 1){ resolve('第三步完成了');
}else{
reject('第三步完成失败')
}
}
new Promise(step1)
.then(function(val){
console.log(val);
return new Promise(step2)
})
.then(function(val){
console.log(val);
return new Promise(step3))
})
.then(function(val){
console.log(val);
})
五、class类和继承//定义类
//定义类
class People{
constructor(name,type){
this.name = name;
this.type = type;
}
toString(){
return `${this.name} + ${this.type}`;
}
}
let p1 = new People('wpy',‘nv');
//Object.assign方法可以很方便的一次向类添加多个方法
Object.assign(Point.prototype,{
toString(){},
toValue(){},
})
//类的继承
class Dog extends People{
constructor(name,type,age){
super(name,type); //调用父类的constructor(name,type);子类必须在constructor方法中调用super
this.age = aage;
}
toString(){
return this.age + '' + super.toString(); //调用父类的toString()
}
}
六、for...of和for...in的区别
let arr = ['a','b','c'];
for (let val of arr){ //遍历的是属性值
console.log(val); //输出a,b,c
}
for(let val in arr){ //for in 遍历的实际上是对象的属性名称
console.log(val); //输出下标值0 1 2
}
arr,forEach(function(element,index,arr){
console.log(element); //指向当前元素
console.log(index); //当前元素的下标值
console.log(arr); //当前数组
})
//in的用法
let obj={
a: '1',
b: '2'
}
console.log(a in obj); true
console.log(c in obj); false 判断对象中是否有某属性,返回true和false
七、模版字符串
`
<ul>
<li>${items}</li>
</ul>
`
es6新增字符串方法
let jspang = "技术胖"
let blog = "很高兴认识你,技术胖"
console.log(blog.includes(jspang)); 判断blog中是否含有jspang所包含的字符串,返回true或false
console.log(blog.startWidth(jspang)); 判断开头是否包含...
console.log(blog.endWidth(jspang));判断结尾是否包含...
console.log(jspang.repeat(21));复制字符串
八、模块化
es6实现了模块功能,完全可以取代ComminJS和AMD规范,成为浏览器和服务器通用多的模块解决方案
es6模块不是对象,而是通过export命令显示指定输出的代码,再通过import命令输入
1)如果你希望外部能够读取模块内部的某个变量,就必须用export关键字输出该变量
export var firstName = 'Michael'; export var lastName = 'Jackson'; export var year = 1958;
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year}; //推荐使用这种写法,因为这样就可以在脚本尾部一眼看出输出了哪些变量
2)一般情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名
function v1(){...}
function v2(){...}
export {
v1 as streamV1,
v2 as streamV2
}
export命令规定的是对外的接口,必须与模块内部的变量建立 一一对应的关系
3)模块的整体加载
impoort {arear,circumference} from './circle';
console.log('圆面积:' + area(4));
console.log('圆周长'+ circumference(14));
整体加载
impoort *as circle from './circle';
console.log('圆面积:' + circle.area;
console.log('圆周长'+ circle.circumference(14));
4)export和export default的区别
export default function crc32(){ //export default输出,用于指定模块的默认输出,一个模块只能有一个默认输出,所以export default命令只能用一次
}
import crc32 from 'crc32';import输入语句不需要使用大括号
export function crc32(){ //export输出
}
import {crc32} from 'crc32'; //import输入语句需要使用大括号export default 42;//正确
export 42;//报错
export var a=1; //正确
export default var a = 1; //错误
4)模块的加载实现
//传统方法
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
默认情况下,浏览器是同步加载javascript脚本,加入defer或async属性,脚本就会异步加载,渲染引擎遇到这一行命令就会开始下载外部脚本,但不会等它下载和执行,而是执行后面的命令
async和defer的区别:
defer是渲染完再执行,即DOM解构完全生成,以及其他脚本执行完成,如果有多个defer脚本,会按照它们再页面出现的顺序加载
async是下载完再执行,即一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染
//es6的加载规则
<script tyope="module" src="./foo.js"></script>
//浏览器中对于带有type="module"的script,都是异步加载,不会造成堵塞浏览器,等同于打开了浏览器的defer属性
5)es6模块的转码
//命令行傻瓜模式
npm init -y 生成package.json文件
npm install -g babel-cli 全局安装babel命令行
cnpm install --save-dev babel-preset-es2015 babel-cli xi
新建.babelrc文件
{
"presets":[
"es2015"
],
"plugins":[]
}
//改成命令行自动模式
在package.json中配置
"scripts":{
"build": "babel src/index.js -o dist/index.js
}
九.set和map数据结构
1)Set类似于数组,但成员的值唯一,没有重复的值
const s = new Set(); //只能放数组,不能放对象
[2,3,5,4,2,2].forEach(x = >s.add(x)); // [].forEach(function(value,index,array){})
for(let i of s){ //of结构遍历数组
console.log(i);
}
for(let key in object){ //遍历对象
console.log(key);
console.log(object[key]);
}
//数组去重
[...new Set(array)]
//Set实例的属性和方法
操作方法:
add(value) 添加某个值,返回Set结构本身
delete(value) 删除某个值,返回一个布尔值,表示删除是否成功
has(value) 返回一个布尔值,表示该值是否为Set的成员
clear() 清除所有成员,没有返回值
Array.from()方法可以将Set结构转为数组
function dedupe(array){
return Array.from(new Set(array));
}
dedupe([1,2,3,3,4,4]);
遍历操作
keys() 返回键名的遍历器
values() 返回键值的遍历器
entries() 返回键值对对的遍历器
forEach() 使用回调函数遍历每个成员
2)WeakSet结构与Set类似,也是不重复的值的集合
//weakset的成员只能是对象,不能是其他类型的值
//weakset中的对象都是弱引用
//没有size属性
const ws = new WeakSet();
let obj = {a:'jspang',b:'技术胖'};
ws.add(obj); //需要通过add()添加元素,不能直接通过new WeakSet({});
方法:
WeakSet.prototype.addd(value) 向WeakSet实例添加一个新成员
WeakSet.prototype.delete(value) 清楚WeakSet实例对的指定成员
WeakSet.prototype.has(value) 返回一个布尔值,表示某个值是否存在
3)Map
1)Map的实例和操作方法
//1.size属性
const map = new Map();
map.set('foo',true);
map.set('bar',false);
map.size //2
//2.set(key,value) 设置键名key对应的键值为value
const m = new Map();
m.set('edition',6);
m.set(262,'standard');
m.set('wpy','dmn');
//3.get(key) 获取key对应的键值
//4.has(key) 返回一个布尔值,表示某个键是否在当前Map对象之中
//5.delete() 删除某个键值对,返回true,删除失败,返回false
//6.clear() 清楚所有成员,没有返回值
2)遍历方法
keys() 返回键名的遍历器
values() 返回键值的遍历器
entries() 返回所有成员的遍历器
forEach() 遍历map的所有成员
3)与其他数据结构的相互转换
//map转为数组用扩展运算符...
const myMap = new Map()
.set(true,7)
.set({foo: 3},['abc']);
[...myMap]
//数组转为Map,将数组传入Map构造函数
4)WeakMap
WeakMap结构与Map结构类似,也是用于生成键值对的集合
WeakMap只接受对象作为键名,不接受其他类型的值作为键名
十、es6数字操作
1).二进制声明
let binary = 0B010101;
2)八进制的声明
let octal = 0o666;
//es6中将所有的方法都放在了Number对象中
let a = 11/4;
Number.isFinite(a);判断a是否为数字,返回true或false
Number.isNaN(a); 判断a是否为NaN
Number.isInteger(a); 判断a是否为整数 //判断的范围在-(Math.pow(2,53)-1)到Math.pow(2,53)-1之间
Number.MAX_SAFE_INTEGER;integer的最大安全值
Number.MIN_SAFE_INTEGER;integer的最小安全值
Number.parseFloat(a); 将a转换成浮点型
Number.parseInt(a); 将a转换成整型
十一、es6新增的数组知识
//es6中将所有与数组有关的方法都放在了Array中
Array.from(json); 将json字符串转换成数组
Arraay.of('wpy','ai','lll'); 将字符串转换成数组
实例方法
let arr = [1,3,4,5,56];
1)arr.find(function(value,index,arr){
return value>5 //查找数据
})
2)arr.fill('要替换进来的新数据',数组的起始位置,数组的结束位置);替换数组元素,左闭右开区间
3)for of数组循环
for(let item of arr){
console.log(item);输出的是数组值
}
for(let item of arr.keys()){
console.log(item);输出的是数组的下标
}
for(let [index,val] of arr.entries()){
console.log(index,val);输出键值对
}
let arr = ['jspang','技术胖','大胖'];
let list = arr.entries(); 生成数组条目
console.log(list.next().value); //jspang
console.log(list.next().value); //技术胖
console.log(list.next().value); //大胖
十二、es6新增的的对象知识
1)Object.is() 判断两个对象是否相等
let obj1={name: 'jspang'};
let obj2={name: 'jspang};
console.log(Object.is(obj1.name,obj2.name));true
===同值相等,is严格相等
console.log(+0 === -0); trye
console.log(NaN === NaN); false
console.log(Object.is(+0,-0)); false
console.log(Object.is(NaN,NaN)); true
2)Object.assign() 合并对象
let a = {a: 1};
let b = {b: 2};
let c = {c: 3};
let d = Object.assign(a,b,c);
十三、Symbol(fo循环的时候不输出)
let obj = {name: 'jspaang',skill:'web'};
let age = Symnbol();
obj[age] = 18;
console.log(obj); {name: 'jspang',skill:'web',age:18};
for(let item in obj){
console.log(obj[item]); //jspang web 循环时不输出age
}
console.log(obj[age]); //可以通过这种方式输出
十.Proxy实例的方法
//proxy预处理
new Proxy({目标对象},{预处理对象})
let pro = new Proxy({
add:function(val){
return val+100;
},
name: 'i am wpy'
},{
get:function(target,key,property){ //打印pro.name之前执行的函数
console.log('come in get');
return target[key];
},
set:function(target,key,value,receiver){
console.log(`setting ${key} = ${value}`);
return target[key] = value;
}
});
console.log(pro.name);
pro.name = '技术胖'; //setting name = 技术胖
let target = function(){
return 'i am wpy';
}
let handler = {
apply(target,ctx,args){
console.log('ddo apply');
return Reflect.apply(...arguments);
}
}
let pro = new Proxy(target,handler);
console.log(pro());