<!DOCTYPE html>
//1.var let const声明变量
//var会变量提升,let就不会被变量提升
//var内层变量覆盖外层变量,let所声明的变量,只在let命令所在的代码块内有效(新增块级作用域)
//var用来计数的循环变量泄露为全局变量(循环结束为最后一轮i的值,新值覆盖旧值);let不会
//const也用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变。
 
//2.set map array增删改查对比
// let map = new Map();
// let set = new Set();
// let array = [];
// 增
// map.set('t', 1);
// set.add( { t : 1 } );
// array.push( { t:1 } );
// console.info( map, set, array ); // Map { 't' => 1 } Set { { t: 1 } } [ { t: 1 } ]
// 查
// let map_exist = map.has( 't' );
// let set_exist = set.has( {t:1} );
// let array_exist = array.find(item => item.t)
// console.info(map_exist, set_exist, array_exist); //true false { t: 1 }
// 改
// map.set('t', 2);
// set.forEach(item => item.t ? item.t = 2:'');
// array.forEach(item => item.t ? item.t = 2:'');
// console.info(map, set, array); // Map { 't' => 2 } Set { { t: 2 } } [ { t: 2 } ]
// 删
// map.delete('t');
// set.forEach(item => item.t ? set.delete(item):'');
// let index = array.findIndex(item => item.t);
// array.splice(index,1);
// console.info(map, set, array); // Map {} Set {} []
 
//3.class extends super 类 继承 指代父类实例
/*class Human {
constructor(name) {
this.name = name;
}
sleep() {
console.log(this.name + " is sleeping");
}
}
let man = new Human("Davis");
man.sleep(); //Davis is sleeping
class Boy extends Human {
constructor(name, age) {
super()
this.name = name;
this.age = age;
}
info(){
console.log(this.name + 'is ' + this.age + 'years old');
}
}
let son = new Boy('Faker','8');
son.sleep(); // Faker is sleeping
son.info(); // Faker is 8 years old
上面代码首先用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。
Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Boy类,该类通过extends关键字,继承了Human类的所有属性和方法。
super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。*/
 
//4.arrow function 箭头函数
/*ES6中新增的箭头操作符 => 简化了函数的书写。操作符左边为输入的参数,而右边则是进行的操作以及返回的值,
可以说是 ES6 最最常用的一个新特性了,看下面的实例:*/
//ES5
let arr1 = [1, 3, 5, 7, 9];
arr1.forEach (function(val, key) { return console.log(val, key); });
//ES6
let arr = [1, 3, 5, 7, 9];
arr.forEach(
(val,key)=>console.log(val,key)
);
//箭头函数this的方便性
//JavaScript语言的this对象指向是一个头疼的问题,在对象方法中使用this,必须非常小心。
//class Human {
// constructor(name) { this.name = name; }
// sleep() { setTimeout(function(){ console.log(this.name + " is sleeping"); }, 1000) }
//}
// let man = new Human("Davis");
// man.sleep(); // is sleeping 输出 Davis,这是因为setTimeout中的this指向的是全局对象
//解决方法有两种:
// 第一种,将this传给self,再用self来指代this
//sleep() { var self = this; setTimeout(function(){ console.log(self.name + " is sleeping"); }, 1000)
// 第二种,是用bind(this)
//sleep() { setTimeout(function(){ console.log(self.name + " is sleeping") }.bind(this), 1000)
//使用=>箭头函数,非常方便:
class Human {
constructor(name){ this.name = name; }
sleep() { setTimeout( () => { console.log(this.name + "is sleeping"); }, 1000) }
}
let man = new Human("Davis");
man.sleep(); // Davis is sleeping
//当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
//并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,它的this是继承外面的,
//因此内部的this就是外层代码块的this。
 
//5.template string 模板字符串
//ES6中允许使用反引号 ` 来创建字符串,创建的字符串里面可以包含变量${vraible}
let num = Math.random(); console.log(` num is ${num}`);
$("#main").html(` <h1>今天天气很好!</h1> <p>产生一个随机数${num}</p> `);
 
//6.destructuring 解构
//ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
//用途一,交换变量的值,不再需要中间变量
let a = 1; let b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
//用途二,提取JSON数据
let jsonData = { id: 1, title: "OK", data: [5, 6] };
let {id, title, data:number} = jsonData;
console.log(id, title, number); // 1, "OK", [5, 6]
//用途三,函数参数的定义
// 参数是一组有次序的值 function f([x, y, z]) { ... } f([1, 2, 3]);
// 参数是一组无次序的值 function f({x, y, z}) { ... } f({z: 3, y: 2, x: 1});
 
//7.default默认参数(指定参数的默认值)
// 传统指定默认参数
function say1(name) {
var name = name || 'Faker';
console.log( 'Hello ' + name );
}
// ES6默认参数
function say2(name='Davis') { //say2(name='Davis')这里的等号,指的是没有传这个参数,则设置默认值Davis,而不是给参数赋值
console.log(`Hello ${name}`);
}
say1(); // Hello Faker
say1('Tom'); // Hello tom
say2(); //Hello Davis
say2('Bob'); // Hello Bob
 
//8.rest参数
//rest参数只包括那些没有给出名称的参数;
//rest参数是Array的实例,可以直接应用sort, map, forEach, pop等方法;
//rest参数之后不能再有其它参数(即,只能是最后一个参数);
//函数的length属性,不包括rest参数;
function fn(x, y, ...rest){
console.log(rest)
}
fn(1, "cat", "dog", 2); //rest ["dog", 2]
console.log(fn.length); //2 [1,cat]
 
//9.Proxy 代理
//Proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。
//让我们对一个对象有了很强的追踪能力,同时在数据绑定方面也很有用处。
//定义被监听的目标对象
let man1 = { name: 'Davis', age: 21 };
//定义处理程序
let handle = {
set(receiver, property, value) {
console.log(property, 'is changed to', value);
receiver[property] = value;
};
//创建代理以进行侦听
man1 = new Proxy(man, handle);
//做一些改动来触发代理
man1.age = 22; //age is change to 22
man1.name = "Faker"; // name is change to Faker
 
//10.Promise对象状态
//Promise/A+规范, 规定Promise对象是一个有限状态机。它三个状态:pending(执行中)Resolved(已完成)Rejected(已失败)
//其中pending为初始状态,Resolved和rejected为结束状态(表示promise的生命周期已结束)。
let val = 1;
// 我们假设step1, step2, step3都是ajax调用后端或者是在Node.js上查询数据库的异步操作
// 每个步骤都有对应的失败和成功处理回调
// step1、step2、step3必须按顺序执行
function step1(resolve, reject) {
console.log('步骤一:执行');
if (val >= 1) {
resolve('Hello I am No.1');
} else if (val === 0) {
reject(val);
}
}
function step2(resolve, reject) {
console.log('步骤二:执行');
if (val === 1) {
resolve('Hello I am No.2');
} else if (val === 0) {
reject(val);
}
}
function step3(resolve, reject) {
console.log('步骤三:执行');
if (val === 1) {
resolve('Hello I am No.3');
} else if (val === 0) {
reject(val);
}
}
new Promise(step1).then(function(val){
console.info(val);
return new Promise(step2);
}).then(function(val){
console.info(val);
return new Promise(step3);
}).then(function(val){
console.info(val);
return val;
}).then(function(val){
console.info(val);
return val;
});
// 执行之后将会打印
//步骤一:执行
//Hello I am No.1
//步骤二:执行
//Hello I am No.2
//步骤三:执行
//Hello I am No.3
//Hello I am No.3
//常用关键点:
//在Promise定义时,函数已经执行了;Promise构造函数只接受一个参数,即带有异步逻辑的函数。这个函数在 new Promise 时已经执行了。只不过在没有调用 then 之前不会 resolve 或 reject。
//在then方法中通常传递两个参数,一个 resolve 函数,一个 reject 函数。reject就是出错的时候运行的函数。resolve 函数必须返回一个值才能把链式调用进行下去。
//resolve 返回一个新 Promise
//返回一个新Promise之后再调用的then就是新Promise中的逻辑了。
//resolve 返回一个值
//返回一个值会传递到下一个then的resolve方法参数中。
 
//11.Generator
//Generator函数跟普通函数的写法有非常大的区别:
//function关键字与函数名之间有一个 *;
//函数体内部使用yield语句,定义不同的内部状态;
function* f() { yield 'a'; yield 'b'; yield 'c'; return 'ending'; }
let fn = f();
console.log(fn.next()); // { value: 'a', done: false }
console.log(fn.next()); // { value: 'b', done: false }
console.log(fn.next()); // { value: 'c', done: false }
console.log(fn.next()); // { value: 'ending', done: true }
//第一次输出fn.next()返回一个简单的对象{value: "a", done: false},'a'就是f函数执行到第一个yield语句之后得到的值,false表示f函数还没有执行完,只是在这暂停。
//第二次,返回的就是{value: "b", done: false},说明f函数运行到了第二个yield语句,返回的是该yield语句的返回值'b'。返回之后依然是暂停。
//第三次,第四次同理,这样整个f函数就运行完毕了。
//只有当yield后面跟的函数先执行完,无论执行体里面有多少异步回调,都要等所有回调先执行完,才会执行等号赋值,以及再后面的操作。这也是yield最大的特性
function req(url) { $.get(url, function(res){ it.next(res); }); }
// 生成器函数
function* ajaxs() { console.log(yield req('a.html'));
console.log(yield req('b.html'));
console.log(yield req('c.html'));
}
var it = ajaxs(); // 遍历器对象 it.next();
// a.html
// b.html
// c.html
 
//12.export、import
//export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口;
//import用于在一个模块中加载另一个含有export接口的模块。
//导出一组对象
导出模块文件app.js:
class Human{
constructor(name) {
this.name = name;
}
sleep() {
console.log(this.name + " is sleeping");
}
}
function walk() {
console.log('i am walking');
}
function play() {
console.log('i am playing');
}
export { Human, walk }
//模块导出了两个对象:Human类和walk函数,能被其他文件使用。而play函数没有导出,为此模块私有,不能被其他文件使用。
//main.js导入app.js模块
import { Human, walk } from 'app.js';
 
//13.Default导出
//使用关键字default,可将对象标注为default对象导出。default关键字在每一个模块中只能使用一次。
//... //类,函数等
export default App;
//main.js导入app.js模块
import App from 'app.js';