ES6
一、ECMA历史
组成部分: ECMA、DOM、BOM
- 什么是ECMA?全称 ECMAScript 简写 ECMA 或 ES
ECMA是一个标准,JS是实现
例如: HTML5 是标准,chrome,FF,IE10是实现
将来也可能有 xxxscript 实现ECMA
- 目前的版本:
- 低级浏览器: 主要支持es3.1,3.2
- 高级浏览器: 正从ES5过度到ES6
- 版本进化过程
1996 ES1.0 js稳定 Netscape将js提交给ECMA组织 1998 ES2.0 正式发布 1999 ES3.0 浏览器广泛支持 2007 ES4.0 ES4过于激进,被废除 2008 ES3.1 4.0变为缩水版的3.1 代号:Harmony 2009 ES5.0 ES5 正式发布,同时公布了javascript.next-6.0前身 2011 ES5.1 成为ISO国际标准 2013.3 ES6.0 ES6.0制定草案 2013.12 ES6.0 发布草案 2015.6 ES6.0 ES6.0预计发布,同时发布 es7.0正在制定草案
- 兼容性:
目前为止ES5 ES6支持情况还凑合 nodejs用的就是chrome内核,在node中已经可以使用es6的很多特性了 ES5和ES6已经逐渐沦为后台语言
1. 在浏览器中如何使用?需要编译工具,把ES6语法编译成ES5 babel traceur ---google出的编译器 bootstrap 引导程序 <script src="traceur.js"></script> <script src="bootstrap.js"></script> <script type="module"> let a = 12; </script> <script src="https://traceur-compiler.googlecode.com/git/bin/traceur.js" type="text/javascript"></script> <script src="https://traceur-compiler.googlecode.com/git/src/bootstrap.js" type="text/javascript"></script> <script> traceur.options.experimental = true; </script> <script type="module" src="js/calc.js"></script> 2. 在线编译:主要用于测试 a. babeljs.io/repl b. google.github.io/traceur-compiler/demo/repl.html 3. 在node中可以直接使用 "use strict" let a = 12; console.log(a); a. 直接用,需要添加 use strict b. node --harmony_destructuring xx.js
二、定义变量 let和const
- ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
let定义的变量不能在相同作用域下,重复声明同一个变量
function () { let a = 10; var a = 1; } function () { let a = 10; let a = 1; }
以下现象用来理解块级作用域
现象1:
{ let a = 10; var b = 1; } console.log(a); console.log(b);
结论:let声明的变量尽在 {}内有效
现象2: let声明的变量仅在当前作用域中有效
for(let i=0;i<10;i++){} console.log(i);
现象3:
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); //======== var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); i是let声明的,所以当前的 i仅在本轮循环有效,直接会被解析成数字。
现象4:循环语句是父作用域,循环体是一个独立的作用域,互不影响。
for (let i = 0; i < 3; i++) { let i = ‘abc‘; console.log(i); }
现象5: 不存在变量提升
// var 的情况 console.log(foo); var foo = 2; // let 的情况 console.log(bar); let bar = 2;
现象6: 暂时性死区
var tmp = 123; if (true) { tmp = ‘abc‘; // ReferenceError let tmp; }
报错:因为当使用let时,当前快作用域被封闭,let定义的变量仅能够在之后使用
为什么需要块级作用域?
- 内层变量可能会覆盖外层变量。
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = ‘hello world‘; } } f();
- 用来计数的循环变量泄露为全局变量。
var s = ‘hello‘; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
块级作用域试题
1.
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
- 自执行函数封闭作用域,可以用块作用域替代
// IIFE 写法 (function () { var tmp = ...; ... }()); // 块级作用域写法 { let tmp = ...; ... }
3.
function f() { console.log(‘I am outside!‘); } (function () { if (false) { // 重复声明一次函数f function f() { console.log(‘I am inside!‘); } } f(); }());
二、const 声明一个只读的常量。一旦声明,常量的值就不能改变
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
- const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) { const MAX = 5; } MAX
三、解构赋值
- 什么是解构赋值? 左边一种结构,右边一种结构,左右一一对应进行赋值
- 分类
- 数组解构赋值 **
- 对象解构赋值 **
- 字符串解构赋值
- 布尔值解构赋值
- 函数参数解构赋值
- 数值解构赋值
1 解构赋值基本方式
let [x,y] = [1,2]; console.log(x,y);//1,2
2 ...解构赋值
{ let a,b,rest; [a,b,...rest] = [1,2,3,4,5,6]; console.log(a,b,rest);//rest [3,4,5,6] }
3 对象解构赋值
//对象的解构赋值 { let a,b; ({a,b} = {a:1,b:2}); console.log(a,b); }
4 默认值(没有找到配对有默认值使用默认值,没有默认值值为 undefined)
// 解构赋值的默认值 { let a,b,c,rest; [a,b,c=3,d] = [1,2]; console.log(a,b,c,d); }
5 变量交换
//利用解构赋值交换变量中的值 { let a = 1; let b = 2; [a,b] = [b,a]; console.log(a,b); }
6 函数返回多个值
function f(){ return [1,2]; } let [a,b] = f(); console.log(a,b);
7 选择性的接收 返回值
{ function f(){ return [5,6,7,8,9]; } let [a,,,,b] = f(); console.log(a,b); }
8 我只关心第一个,其他的先放数组里
{ function f(){ return [5,6,7,8,9]; } let [a,...b] = f(); console.log(a,b); }
9 对象结构赋值
{ let o = {name:‘yuonly‘,age:20}; let {age,name} = o; console.log(name,age); }
10 可以替代 arguments
// ... 可以替代arguments { function test3(...arg){ for(let v of arg){ console.log(‘rest‘,v); } } test3(1,2,3,4,‘a‘); }
四、对象扩展
1. 简洁表示法
//简洁表达 { let a = 1; let b = 2; //ES5 let es5 = { a:a, b:b } console.log(es5); //ES6 let es6 = { a, b } console.log(es6); } { //如果有方法的处理方式 // ES5 let es5 = { hello:function(){ console.log(‘hellow‘); } } //ES6 let es6 = { hello(){ console.log(‘hello‘); } } }
2. 属性表达式
let a = ‘b‘; let es5_obj = {//es5中对象的 key必须是固定的 a:‘c‘ } //es6 let es6_obj = {//属性值可以是变量 [a] : ‘c‘ } console.log(es6_obj,‘es6_obj‘);
3. 扩展运算符(ES7语法,支持不好,了解)
{ //babel不支持,无法编译 let {a,b,...c} = {a:‘test‘,b:‘kill‘,c:‘ddd‘,d:‘ccc‘} c = { c:‘ddd‘, d:‘ccc‘ } }
4. object新增API
{ // is :判断两个值是否相等 console.log(‘字符串‘,Object.is(‘abc‘,‘abc‘)); console.log(‘数组‘,Object.is([],[]));//两个不同的地址 //assign : 潜拷贝 console.log(‘拷贝‘,Object.assign({a:‘a‘},{b:‘b‘})); // entries let test = [k:123,o:456]; for(let [key,value] of Object.entries(test)){ console.log([key,value]); } }
五、类
1 基本语法
{ //基本定义和生成实例 class Person{ constructor(name=‘laozhou‘){ this.name = name; } } let p1 = new Person(‘小王‘); console.log(‘构造函数和实例‘,p1); }
2 类的继承
{ //继承 class Father{ constructor(name=‘laozhou‘){ this.name = name; } } class Child extends Father{ } console.log(‘子类的实例‘,new Child()); console.log(‘子类自己传‘,new Child(‘小刘‘)) } { //子类如何覆盖父类的值 class Father{ constructor(name=‘laozhou‘){ this.name = name; } } class Child extends Father{ constructor(name=‘child‘){ //this.type = ‘boy‘;//super必须在第一行,否则报错 super(name);//调用父亲类的构造函数 this.type = ‘boy‘; } } console.log(‘子类覆盖父类属性的实例‘,new Child()); }
3 静态属性
//静态属性 { class Person{ constructor(name=‘laozhou‘){ this.name = name; } } //静态属性的定义,是直接给类下的属性赋值,该属性就是静态属性 Person.type = ‘text‘;//type就是静态属性 }
4 静态方法
//静态方法 { class Person{ constructor(name=‘laozhou‘){ this.name = name; } static tell(){//相当于原型下的方法,是属于类本身的,用类来调用 console.log(‘tell‘); } } Person.tell(); }
5 getter-setter
{ //getter setter class Father{ constructor(name=‘laozhou‘){ this.name = name; } //此处longName不是函数而是属性,相当于新增了一个属性 longName get longName(){ return ‘yu_‘+this.name; } set longName(value){ this.name = value; } } let f1 = new Father(); console.log(‘getter‘,f1.longName); f1.longName = ‘hello‘; console.log(‘setter‘,f1.longName); }
六、promise
promise是异步变成的解决方案(解决回调地狱)
1 基本写法
{ //es5 中的异步操作 let ajax = function(callback){ console.log(‘执行‘); setTimeout(function(){ callback && callback.call(); },1000); } ajax(function(){ console.log(‘timeout1‘); }) } { //ES6 let ajax = function(){ console.log(‘执行2‘); return new Promise(function(resolve,reject){ setTimeout(function(){ resolve();//resolve后会有then方法,执行后续操作 },1000); }) } ajax().then(function(){ console.log(‘promise‘,‘timeout‘); }) }
2 promise.all 所有的 异步操作都成功后,在去执行一个动作。例子,图片都加在完成之后再显示到页面
//promise.all :把多个promise实例当做一个promise实例,当所有的状态都完成之后,才会触发新的promise对象的then方法 //应用,比如广告中有三张图,需要三张图都加载再去更新dom { //所有图片加载完成在加载页面 function loadImg(src){ return new Promise((resolve,reject)=>{ let img = document.createElement(‘img‘); img.src = src; img.onload = function(){ resolve(img); } img.onerror = function(){ reject(err); } }) } function showImg(imgs){ imgs.forEach(function(img){ document.body.appendChild(img); }) } Promise.all([ loadImg(‘https://img10.360buyimg.com/n4/s260x260_jfs/t3079/363/6070979658/42126/5b5c2f39/589af086N4a3bed39.jpg‘), loadImg(‘https://img14.360buyimg.com/n4/s260x260_jfs/t4345/284/2534342556/119746/550dee37/58d34a9dNf0bd717f.jpg‘), loadImg(‘https://img13.360buyimg.com/n4/s260x260_jfs/t3259/170/5587914266/144572/64259fc5/5873347aN14e34822.jpg‘), ]).then(showImg); }
3 promise.race 谁先到加载谁
{ //promise.race 谁先回来处理谁,先到先得 function loadImg(src){ return new Promise((resolve,reject)=>{ let img = document.createElement(‘img‘); img.src = src; img.onload = function(){ resolve(img); } img.onerror = function(){ reject(err); } }) } function showImgs(img){ let p = document.createElement(‘p‘); p.appendChild(img); document.body.appendChild(p); } Promise.race([ loadImg(‘https://img10.360buyimg.com/imgzone/jfs/t5149/215/1247256617/226900/c371168/590dcc2bN2234f761.jpg‘), loadImg(‘https://img14.360buyimg.com/n4/s260x260_jfs/t4345/284/2534342556/119746/550dee37/58d34a9dNf0bd717f.jpg‘), loadImg(‘https://img13.360buyimg.com/n4/s260x260_jfs/t3259/170/5587914266/144572/64259fc5/5873347aN14e34822.jpg‘), ]).then(showImgs); }
4 promise可以让代码执行的步骤更清晰,过程更可控
//使用promise后,更清晰 { let oBox = document.getElementById(‘box‘); function toWidth(w){ return new Promise(function(resolve,reject){ let timer = setInterval(function(){ var oldWidth = oBox.clientWidth; if(oldWidth>w){ clearInterval(timer); resolve(); } oBox.style.width = oldWidth + 10 + ‘px‘; },100); }) } function toHeight(h){ return new Promise(function(resolve,reject){ let timer = setInterval(function(){ var oldHeight = oBox.clientHeight; if(oldHeight>h){ clearInterval(timer); resolve(); } oBox.style.height = oldHeight + 10 + ‘px‘; },100); }) } function toSmall(size){ return new Promise(function(resolve,reject){ let timer = setInterval(function(){ var oldWidth = oBox.clientWidth; var oldHeight = oBox.clientHeight; if(oldWidth<size || oldHeight<size){ clearInterval(timer); resolve(); } console.log(123); oBox.style.width = oldWidth - 10 + ‘px‘; oBox.style.height = oldHeight - 10 + ‘px‘; },100); }) } //使用promise后,更清晰,可以随意更改流程。 toHeight(400) .then(function(){ return toWidth(600); }) .then(function(){ return toHeight(300); }) .then(function(){ return toSmall(100); }) .then(function(){ return toHeight(400); }) } //ES5 { function toWidth(w,callback){ let timer = setInterval(function(){ var oldWidth = oBox.clientWidth; if(oldWidth>w){ clearInterval(timer); callback && callback(); } oBox.style.width = oldWidth + 10 + ‘px‘; },100); } function toHeight(h,callback){ let timer = setInterval(function(){ var oldHeight = oBox.clientHeight; if(oldHeight>h){ clearInterval(timer); callback && callback(); } oBox.style.height = oldHeight + 10 + ‘px‘; },100); } function toSmall(size,callback){ let timer = setInterval(function(){ var oldHeight = oBox.clientHeight; var oldWidth = oBox.clientWidth; if(oldHeight<size){ clearInterval(timer); callback && callback(); } oBox.style.width = oldWidth - 10 + ‘px‘; oBox.style.height = oldHeight - 10 + ‘px‘; },100); } //代码可读性差,而且很难控制执行顺序 toWidth(500,function(){ toHeight(300,function(){ toSmall(100,function(){ toWidth(400,function(){ toHeight(100,function(){ }) }) }); }) }); }
七、模块化
1. 基本概念:每一个文件都是独立的作用域,一个模块,都是独立存在,不会互相影响
2. ES6模块化语法
方式一: 优点:可以只导出部分模块。弊端,导出模块特别多的时候,不能一眼看出都导出了那些模块
//模块的导出 module1.js export let a = 123; export function test(){ console.log(‘test‘); } export class Hello{ test(){ console.log(‘class‘); } } //模块的导入 entry.js a as A 起别名 A,外部使用A来操作a import {a as A,test,Hello} from ‘./lesson/6.module/module1.js‘; console.log(A,test,Hello); //导入所有模块放入 obj对象下 import * as obj from ‘./lesson/6.module/module1.js‘; console.log(obj.a);
方式二:推荐方式
//导出 module2.js let num = 123; let test = function(){ console.log(‘test‘); } class Person{ test(){ console.log(‘Person‘); } } //1,批量导出,一目了然导出了那些东西给外部 2, 没有起名字,将命名权给倒入方。 export default { num, test, Person } //导入 entry.js import M from ‘./lesson/6.module/module2.js‘; console.log(M,M.num); M.test()
八、函数扩展(箭头函数)
- 参数默认值
- rest参数
- 扩展运算符(rest参数的逆运用)
- 箭头函数
- 尾调用
1 参数默认值
{ function test(x,y=10){ console.log(‘默认值‘,x,y); } test(1); test(99,88); } // 有默认值的参数后面,不能有 没有默认值的参数 { function test(x,y=10,c){//会报错,在默认值后面不能有 没有默认值的参数 console.log(‘默认值‘,x,y); } } //函数的作用域 { let x = ‘test‘; function test2(x,x=y){ console.log(‘作用域——‘,x,y); } test2(‘new‘); } //没有的时候用上面的 { let x = ‘test‘; function test2(c,y=x){ console.log(‘作用域2‘,x,y); } test2(‘new‘); }
2 rest参数
{ //此时的arg相当于是 arguments function test3(...arg){ for(let v of arg){ console.log(‘rest:‘,v); } } test3(1,2,3,4,5); }
3 扩展运算符(rest参数的逆运用)
{ console.log(...[1,2,3]); }
4 箭头函数
{ let arrow = v=>v*10; console.log(arrow(3)); <!-- let arrow2 = (v)=>{return v*50}; console.log(arrow2(2)); --> } 相当于是 { let arrow = function(v){ return v*10; } console.log(arrow(3)); }
5 尾调用 :函数的最后一句话是函数,就是为调用
{ function tail(x){ console.log(‘tail‘,x); } function go(x){ return tail(x); } go(123); }
时间: 2024-10-14 13:56:19