ECMAScript 6学习网站:http://es6.ruanyifeng.com/
1.let和var的比较
共同点:定义变量
区别:
1.范围不同
- var变量可以跨代码块
- let只在代码块内部有效
{
var a = "jarry";
let b = 20;
}
console.log(a);
if(typeof(b) == "undefined"){
alert("undefined");
}
案例
var a = [];
for(let i = 0; i < 5; i++){
a[i] = function(){
console.log(i);
}
}
a[2]();
2.定义顺序
console.log(a); //let 引用错误
let a = "jarry";
var 不会报错的原因是因为加载这个文件后,已经知道有这个变量,只是在执行的时候,还没有声明,所以不会报错,只是为undefined
2.const常量
只能一次赋值
const PI = 3.14159;
PI = 3.14;
1.对象常量
对象的属性可以修改,对象的引用不能修改
const obj = {name:"jack"};
obj.name = "jarry";
2.冻结对象
防止修改对象的属性
const obj = Object.freeze({name:"jason"});
obj.name = "jarry";
console.log(obj.name);
3.解构赋值
1.数组解构赋值
必须按照顺序
let [a,b] = ["jack",20];
console.log(a);
console.log(b);
old version
var obj = {name:"jack",age:20};
var a = obj.name;
var b = obj.age;
2.对象解构赋值
可以乱序
var {name,age} = {name:"jack",age:20};
console.log(name);
console.log(age);
3.用途
- 1.交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
- 2.从函数返回多个值
// 返回一个数组
function func1() {
return [1, 2, 3];
}
let [a, b, c] = func1();
//old version
function func1() {
return [1, 2, 3];
}
var arr = func1();
let a = arr[0];
let b = arr[1];
let c = arr[2];
- 3.返回一个对象
function func2() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = func2();
4.参数有序和无序
- 参数是一组有次序的值
//数组解构赋值
function func3([x, y, z]) {}
func3([1, 2, 3]);
- 参数是一组无次序的值
//对象解构赋值
function func4({x, y, z}) { }
func4({z: 3, y: 2, x: 1});
4.模板字符串(增强版的字符串)
原始的字符串拼接方式:
let name = "jack";
let age = 20;
let str = name + " is " + age + "years old.";
模板字符串:
- 嵌入变量
let str2 = `${name} is ${age} years old.`;
//str2.log + tab键
console.log(str2);
- 保持原来的字符串格式
let str3 = `I love
you!`;
console.log(str3);
5.箭头函数
允许使用“箭头”(=>)定义函数
- 普通
//原始方式
let func = function(n){
console.log(n);
}
func(2);
//n =>代表function(n)
let func1 = n => console.log(n);
func1(3);
- 带有返回值的例子:
//原始方式
let func1 = function(n){
return n * 2;
}
//自动返回=>的值
let func2 = n => n * 2;
- 多个参数
//原始方式
let func1= function(a,b){
a+=5;
return a+b;
}
//多个参数
let func2 = (a,b) => {
a+=5;
return a+b;
};
console.log(func3(3,4));
- 箭头函数返回对象
var initObj = id => ({id:id,name:"default"});
console.log(initObj(2));
- 对象解构赋值
//拼接得到完整的名称
let full = ({first, last}) => first + " " + last;
console.log(full({first: "Michael", last: "Jackson"}));
常见案例:
- 数组的map遍历
//原始方式
var arr =[3,4,2,1].map(function (n) {
return n*2;
})
console.log(arr);
//箭头方式
var arr2=[1,2,4].map(n=>n*2);
console.log(arr2);
- sort排序(升序)
//原始方式
var arr = [1,4,5,2].sort(function(a,b){
return a - b;
});
//箭头方式
var arr2 = [1,4,5,2].sort((a,b) => b - a);
console.log(arr2);
- 不传参数
可以传多个也可以不传
let func5 = () => 5;
let func6 = (m,n) => m * n;
function func4(a){
//参数数组
console.log(arguments[2]);
}
func4(6,9,10);
6.Symbol 第七种数据类型
表示独一无二的值,防止对象属性的覆盖
- 类型
let s1 = Symbol();
console.log(s1);
console.log(typeof s1);
运行结果是:
Symbol()
symbol
- 属性覆盖
let obj = {name : "jack"};
obj.name = function(){
console.log("jack");
}
obj.name();
- 使用Symbol给对象添加属性
let obj = {name:"jason"};
//另外一个模块
let name = Symbol();
let age = Symbol();
//name是一个Symbol值,作为属性名
obj[name] = function(){
console.log("jack");
};
obj[age] = 10;
console.log(obj.name);
console.log(obj[age]);
obj[name]();
7.==(等于)与 ===(恒等于)的区别
==(等于)
1、如果两个值类型相同,进行 === 比较。
2、如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较:
- a、如果一个是null、一个是undefined,那么[相等]。
- b、如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。
- c、如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。
- d、如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的toString或者valueOf方法。 js核心内置类,会尝试valueOf先于toString;例外的是Date,Date利用的是toString转换。
// console.log({} == "abc");
// console.log("1" == 1);
// console.log("abc" == 'abc');
// let a = null;
// console.log(a == undefined);
//
// console.log(true == "1");
===(恒等于)
1、如果类型不同,就[不相等]
2、如果两个都是数值,并且是同一个值,那么[相等];(!例外)的是,如果其中至少一个是NaN,那么[不相等]。(判断一个值是否是NaN,只能用isNaN()来判断)
3、如果两个都是字符串,每个位置的字符都一样,那么[相等];否则[不相等]。
4、如果两个值都是true,或者都是false,那么[相等]。
5、如果两个值都引用同一个对象或函数,那么[相等];否则[不相等]。
6、如果两个值都是null,或者都是undefined,那么[相等]。
8.class关键字
class 语法糖
在原有语法基础上修改语法,提高语法的可读写,原来的语法仍然可以使用
- 老版本
function User(name,age){
this.name = name;
this.age = age;
}
User.prototype.sayHi = function(){
console.log(this.name);
}
- class 语法糖
class User{
//构造函数
constructor(name,age){
this.name = name;
this.age = age;
}
sayHi(){
console.log(this.name);
}
}
let u1 = new User("jack",20);
u1.sayHi();
console.log(typeof User);
事实上,类的所有方法都定义在原型的上的
console.log(u1.sayHi === User.prototype.sayHi);
返回结果是true
9.继承
var count = 0;
class Point{
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
return this.x + " " + this.y;
}
//静态方法
static count(){
return count++;
}
}
//本质上还是通过原型来实现继承
class My3DPoint extends Point{
constructor(x,y,z){
super(x,y); //向父类构造函数传参
this.z = z;
}
toString(){
return "3D:"+this.z + " "+super.toString();
}
}
let p1 = new My3DPoint(20,30,40);
// console.log(p1.toString());
//// console.log(p1 instanceof My3DPoint);
// console.log(p1 instanceof Point);
// console.log(p1.constructor === My3DPoint);
My3DPoint.count();
console.log(Point.count());
console.log(Point.count());*/
10.new.target
//根据new.target是否返回undefined,来判断构造函数是怎么调用的
function Person(name) {
if (new.target !== undefined) {
this.name = name;
} else {
throw new Error('必须使用new生成实例');
}
}
// 另一种写法
function Person(name) {
if (new.target === Person) {
this.name = name;
} else {
throw new Error('必须使用new生成实例');
}
}
var p1 = new Person('张三'); // 正确
var p2 = Person.call(person, '张三'); // 报错*/
11.几种继承
1.原型继承
//父类型
function SuperType(){
this.flag = true;
}
SuperType.prototype.getXX = function(){
return this.flag;
}
//子类型
function SubType(){
this.subFlag = false;
}
SubType.prototype = new SuperType();
//拷贝过程(可以这么理解)
//new SuperType() -> SubType.prototype -> s1
//检查是否继承了父类型的属性
var s1 = new SubType();
console.log(s1.getXX());
原型继承的问题:共享属性
function SuperType(){
this.x = 10;
this.colors = ["red","green","blue"];
}
SuperType.prototype.getXX = function(){
return "xx";
}
//子类型
function SubType(){
this.subFlag = false;
}
//继承
//原型的问题:当原型包含引用类型的属性时,这些属性会被所有实例共享
SubType.prototype = new SuperType();
var s1 = new SubType();
s1.colors.push("white");
s1.x = 9;
console.log(s1.colors);
var s2 = new SubType();
console.log(s2.colors);
console.log(s2.x);
这个结果:s2也会有white
2.构造函数继承
function SuperType(){
this.colors = ["red","green","blue"];
}
function SubType(){
//继承属性
SuperType.call(this);
//相当于
/!*function SuperType(){
s1.colors = ["red","green","blue"];
}*!/
}
var s1 = new SubType();
s1.colors.push("white");
console.log(s1.colors);
var s2 = new SubType();
console.log(s2.colors);*/
构造函数继承的问题:
方法都在构造函数中定义,函数复用是个问题,而且在父类原型中定义的函数,对于子类也是不可见的。
3.组合继承(综合有点,避免缺点)
//原型+构造函数
function SuperType(name){
this.name = name;
this.colors = ["red","green","blue"];
}
SuperType.prototype.sayHi = function(){
console.log("sayHi");
}
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age = age;
}
//继承函数
SubType.prototype = new SuperType();
//构造函数不能错
SubType.prototype.constructor = SubType;
//子类自己的函数
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var s1 = new SubType("jack",20);
s1.sayHi();
s1.sayAge();
s1.colors.push("white");
console.log(s1.colors);
var s2 = new SubType("jason",20);
console.log(s2.colors);
//根据constructor判断,当前对象由哪一个构造函数实例化出来的
console.log(s2.constructor === SubType);
12.Generator函数
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
和普通函数的区别
- 1.返回值function*
- 2.yield
传统函数,一旦调用,从头至尾执行,根本停不下来
从语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态
function* func(){
yield "step1"; //状态
console.log("step1");
yield "step2";
console.log("step2");
return "result";
}
调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)
let state = func();
执行流程:
next方法:第一次调用,Generator函数开始执行,直到遇到第一个yield语句为止。
state.next();
next方法返回一个对象,它的value属性就是当前yield语句的值step1。
state.next();
state.next();
console.log(state.next());
上面代码返回对象的内容:{ value: 'step1', done: false }
其中:done属性的值false,表示遍历还没有结束。
** Symbol.iterator 迭代器**
let arr = ['a', 'b', 'c'];
//数组迭代器对象
let iter = arr[Symbol.iterator]();
//迭代器iter.next()函数,返回值:{ value: 'a', done: false }
console.log(iter.next());
//遍历数组的所有元素
while(true){
var obj = iter.next();
if(obj.done === true){
break;
}
console.log(obj.value);
}
for of循环
for(let item of iter){
console.log(item);
}
案例
显示进度条->请求数据->获取数据成功,隐藏进度条
- 普通方式
//显示进度条
function showLoadingProgress(){
//不断刷新进度条
//DOM
var progress = document.getElementsByTagName("progress")[0];
//定时器,每隔500毫秒,进度累加
var interval = setInterval(function(){
progress.value += 10;
if(progress.style.display === "none"){
clearInterval(interval); //取消定时器
}
},500);
}
//请求数据
function loadUIDataAsynchronously(callback){
//模拟网络延时
setTimeout(function(){
//拿到数据了
let dataNode = document.getElementById("data");
dataNode.innerText = "假装从服务器拿到了数据,显示在这里...";
//隐藏进度条
callback();
},3000);
}
//隐藏进度条
function hideLoadingProgress(){
var progress = document.getElementsByTagName("progress")[0];
progress.style.display = "none";
}
function loadUI(){
//1.显示进度条
showLoadingProgress();
//2.请求数据
loadUIDataAsynchronously(hideLoadingProgress);
}
//当页面加载完成之后,执行loadUI
window.onload = loadUI;
- 使用Generator函数
//显示进度条
function showLoadingProgress(){
//不断刷新进度条
//DOM
var progress = document.getElementsByTagName("progress")[0];
//定时器,每隔500毫秒,进度累加
var interval = setInterval(function(){
progress.value += 10;
if(progress.style.display === "none"){
clearInterval(interval); //取消定时器
}
},500);
}
//请求数据
function loadUIDataAsynchronously(){
//模拟网络延时
setTimeout(function(){
//拿到数据了
let dataNode = document.getElementById("data");
dataNode.innerText = "假装从服务器拿到了数据,显示在这里...";
//隐藏进度条
loader.next();
},3000);
}
//隐藏进度条
function hideLoadingProgress(){
var progress = document.getElementsByTagName("progress")[0];
progress.style.display = "none";
}
//异步操作的同步化表达
function* loadUI(){
//1.显示进度条
showLoadingProgress();
//2.请求数据
yield loadUIDataAsynchronously();
//3.隐藏进度条
hideLoadingProgress();
}
var loader;
window.onload = function(){
loader = loadUI();
loader.next();
}
next传参
yield句本身没有返回值,或者说总是返回undefined
next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。
Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数, 就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。 也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
function* func2(){
console.log("started");
let r1 = yield ;
console.log(r1); //输出yield返回值
let r2 = yield ;
console.log(r2);
}
let state = func2();
state.next();
state.next("a");
state.next();
//循环输出,通过传参,随时让循环重新开始
function* func3(){
for(let i=0; i < 5; i++){
let reset = yield i;
console.log(reset);
if(reset) i = -1;
}
}
let state = func3();
console.log(state.next());
console.log(state.next());
//通过next传参,让yield有返回值
console.log(state.next(true));
console.log(state.next());
console.log(state.next());
案例2
按照流程执行:step1(value1) -> step2(value2) -> step3(value3) -> step4(value4)
- 普通方式:
function step4(value3,callback){
var value4 = `step4_${value3}`;
callback(value4);
}
function step3(value2,callback){
var value3 = `step3_${value2}`;
callback(value3);
}
function step2(value1,callback){
var value2 = `step2_${value1}`;
callback(value2);
}
function step1(callback){
var value1 = `step1_start `;
callback(value1);
}
//给什么人看?
//回调嵌套
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
console.log(value4);
});
});
});
});*/
- Generator 方式
function step4(value3){
return `step4_${value3}`;
}
function step3(value2){
return `step3_${value2}`;
}
function step2(value1){
return `step2_${value1}`;
}
function step1(){
return `step1_start `;
}
//控制流管理
function* stepFunc () {
let value1; yield value1 = step1();
let value2; yield value2 = step2(value1);
let value3; yield value3 = step3(value2);
let value4; yield value4 = step4(value3);
console.log(value4);
}
//顺序执行
for(var i of stepFunc());
13.Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大
例子
要求:
Ajax请求网络,json数据
请求成功:显示数据
请求失败:抛出异常
局部刷新
Promise对象的三种状态
Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)
function ajax(url){
var promise = new Promise(function(resolve,reject){
var request = new XMLHttpRequest();
request.open("GET",url)
request.onreadystatechange = handler;
request.responseType = "json"; //服务器响应数据的类型 json
request.setRequestHeader("Accept","application/json");
request.send();
//回调
function handler(){
//4(完成)响应内容解析完成
if(this.readyState !== 4){
return;
}
if(this.status == 200){
//请求成功:显示数据
resolve(this.response);
}
else{
reject(new Error(this.statusText));
}
}
});
return promise;
}
//指定resolve状态、reject状态的回调函数
ajax('test1.json').then(
//resolve
json => alert(json.message),
//reject,可选的
e => console.log(e.message)
);