首页 > 代码库 > es6

es6


Promise

JavaScript 是单线程工作,这意味着两段脚本不能同时运行,而是必须一个接一个地运行。在浏览器中,JavaScript 与因浏览器而异的其他 N 种任务共享一个线程。但是通常情况下 JavaScript 与绘制、更新样式和处理用户操作处于同一队列。操作其中一项任务会延迟其他任务。

Promise 就是是一个容器,里面装着未来的结果。Prmise的状态不受外界的控制,仅受异步操作结果的影响:要么从pending到resolved, 要么从pending到rejected, 并且状态一旦改变就不会再变,和事件不同,事件是错过了这个事件,就监听不到结果,而Promise 会静止在它异步执行的结果上不会变,再添加监听事件,获取到的是该静止的结果;

promise的作用:
以前层层嵌套的回调函数,将异步操作以同步操作的方式展示出来,

promise的缺点:
1、 无法取消, 一旦新建它就会立即执行
2、如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
3、当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

Promise是一个构造函数,传入一个函数,带有resolve 和 reject 参数,用来生成promise实例, promise 有then的方法可以链式调用。
  1. var promise = new Promise(function(resolve,reject){
  2. // do something
  3. if (/* 操作成功 */) {
  4. resolve(value);
  5. } else {
  6. reject(err);
  7. }
  8. });
resolve 在异步操作成功的时候被调用,如果return 一个东西,那么将会被下一个then中的函数接收到,reject 在异步操作失败的时候被调用;

promise 实例上有两个方法,一个是then, 一个是catch;
then 方法接受两个函数作为参数,第一个函数将会在promise 实例变成resolved 状态(异步执行成功,resolved执行成功后)时被调用。第二个回调函数是Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。

用promise来封装一个imgloader的函数:
  1. function imgloader () {
  2. return new Promise(function(resolve,reject){
  3. var img = new Image();
  4. img.src = ‘http://javascriptplayground.com/img/logo.png‘;
  5. img.onload = function(){
  6. resolve(img);
  7. };
  8. img.onerror = function(){
  9. reject(‘img load error‘);
  10. }
  11. })
  12. }
上面代码中,使用Promise包装了一个图片加载的异步操作。如果加载成功,就调用resolve方法,否则就调用reject方法。

用Promise对象实现的Ajax操作的例子




es6有哪些新的特性:
技术分享


class
class只是语法糖,并没有为js引入一种新的对象继承模式,之前通过原型链,一样可以实现;
  1. //定义类
  2. class Point {
  3. constructor(x, y) {
  4. this.x = x;
  5. this.y = y;
  6. }
  7. toString() {
  8. return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)‘;
  9. }
  10. }

定义class 

class 就像是特殊的函数,和创建函数一样,有类声明(class declarations),和类表达式( class expressions )两种创建类的方式:
类声明
为了声明一个类,使用class 关键字,后面跟上类名(class 关键字后面的是类名)
  1. class Rectangle {
  2. constructor(height, width) {
  3. this.height = height;
  4. this.width = width;
  5. }
  6. }
类声明 和 普通函数声明不同的地方在于,类声明并没有函数提升,类表达式也没有函数提升:
  1. var obj = new Myclass(); //报错
  2. class Myclass (){ }
这是为了保证,子类继承父类的那个语句,不会提升至头部,否则将会出现父类还没有定义,子类就要继承, 看下面的例子:
  1. {
  2. let B = class {}; // let 声明 不存在函数提升
  3. class A extends B { //如果存在类哈函数提升的话,这行会提升到第一行,父亲还没声明,儿子怎么继承?
  4. }
  5. }
类声明不能和已经存在的类重名,(不管这个类之前是通过类声明的方式声明还是通过类表达式的方式声明的), 否则将会报错;
  1. class f1 {};
  2. class f1 {};
  3. var f2 = class {};
  4. class f2 {}; // 报错了
  5. class f3 {};
  6. var f3 = class {}; // 报错了
  7. var f4 = class {};
  8. var f4 = class {}; // 如果两个函数表达式重名了,那么不会报错

类表达式
类表达式是定义类的另外一种方式
  1. var myClass = class [className] [extends] {
  2. // class body
  3. }
就像函数表达式一样,在类表达式中,类名是可有可无的。若定义的类名,则该类名只有的类的内部才可以访问到。
  1. // 方式一
  2. const MyClass = class {};
  3. // 方式二:给出类名
  4. const MyClass = class Me {
  5. getClassName() {
  6. return Me.name;
  7. }
  8. };
如果class 后面没有名字,那么该类.name  就是 函数表达式的名字:
  1. var Foo = class {
  2. constructor() {}
  3. bar() {
  4. return ‘Hello World!‘;
  5. }
  6. };
  7. var instance = new Foo();
  8. instance.bar(); // "Hello World!"
  9. Foo.name; // "Foo"
如果 class 后面有名字,那么该名字只能在函数内被访问到,同时该类 . name 就是class 后面的名字:
  1. var Foo = class NamedFoo {
  2. constructor() {}
  3. whoIsThere() {
  4. return NamedFoo.name;
  5. }
  6. }
  7. var bar = new Foo();
  8. bar.whoIsThere(); // "NamedFoo"
  9. NamedFoo.name; // ReferenceError: NamedFoo is not defined
  10. Foo.name; // "NamedFoo"

采用类表达式,可以写出立即执行的Class。如下:
  1. let person = new class {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. sayName() {
  6. console.log(this.name);
  7. }
  8. }(‘Zhang San‘);
  9. person.sayName(); // Zhang San

类体和方法定义
类的成员需要定义在一对大括号内{},大括号内的代码的大括号本身组成了类体。类成员包括类构造器类方法 (包括静态方法和实例方法)。
类体中的代码都强制在严格模式中执行,即默认”use strict”。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。

构造器(constructor方法)

constructor方法是一个特殊的类方法,仅在实例化的时候被调用。一个类只能拥有一个名为constructor的方法,否则会抛出SyntaxError异常。

如果没有定义constructor方法,这个方法会被默认添加,即,不管有没有显示定义,任何一个类都有constructor方法。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,子类就得不到this对象。

  1. class Point {}
  2. class ColorPoint extends Point {
  3. constructor() {}
  4. }
  5. let cp = new ColorPoint(); // ReferenceError
上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。

原型方法

定义类的方法时,方法名前面不需要加上function关键字。另外,方法之间不需要用逗号分隔,加了会报错。

  1. class Bar {
  2. constructor() {}
  3. doStuff() {}
  4. toString() {}
  5. toValue() {}
  6. }
上面的写法就等同于下面:
  1. Bar.prototype = {
  2. doStuff() {},
  3. toString() {},
  4. toValue() {}
  5. };
所以,在类的实例上调用方法,实际上就是调用原型上的方法。既然类的方法都是定义在prototype上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。
  1. class Point {
  2. constructor() {
  3. // ...
  4. }
  5. }
  6. Object.assign(Point.prototype, {
  7. toString() {},
  8. toValue() {}
  9. });
另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
  1. class Point {
  2. constructor(x, y) {
  3. // ...
  4. }
  5. toString() {
  6. return ‘(‘ + x + ‘, ‘ + y + ‘)‘;
  7. }
  8. }
  9. Object.keys(Point.prototype); // []
  10. Object.getOwnPropertyNames(Point.prototype); // ["constructor", "toString"]
  11. Object.getOwnPropertyDescriptor(Point, ‘toString‘);
  12. // Object {writable: true, enumerable: false, configurable: true}

静态方法

static关键字用来定义类的静态方法。静态方法是指那些不需要对类进行实例化,使用类名就可以直接访问的方法。静态方法经常用来作为工具函数。
  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. static distance(a, b) {
  7. const dx = a.x - b.x;
  8. const dy = a.y - b.y;
  9. return Math.sqrt(dx*dx + dy*dy);
  10. }
  11. }
  12. const p1 = new Point(5, 5);
  13. const p2 = new Point(10, 10);
  14. console.log(Point.distance(p1, p2));

静态方法不可以被实例继承,是通过类名直接调用的。但是,父类的静态方法可以被子类继承。

  1. class Foo {
  2. static classMethod() {
  3. return ‘hello‘;
  4. }
  5. }
  6. class Bar extends Foo {
  7. }
  8. Bar.classMethod(); // "hello"

extends关键字

extends关键字用于实现类之间的继承。子类继承父类,就继承了父类的所有属性和方法。 extends后面只可以跟一个父类。

  1. class ColorPoint extends Point {
  2. constructor(x, y, color) {
  3. super(x, y); // 调用父类的constructor(x, y)
  4. this.color = color;
  5. }
  6. toString() {
  7. return this.color + ‘ ‘ + super.toString(); // 调用父类的toString()
  8. }
  9. }
extends关键字不能用于继承一个对象,如果你想继承自一个普通的对象,你必须使用 Object.setPrototypeof ( )

es5 的继承和 es6 的继承
原型链继承,就是通过将子类构造函数的原型作为父类构造函数的实例(sub.prototype=new super),这样就连通了子类-子类原型-父类;
  1. //先来个父类,带些属性
  2. function Super(){
  3. this.flag = true;
  4. }
  5. //为了提高复用性,方法绑定在父类原型属性上
  6. Super.prototype.getFlag = function(){
  7. return this.flag;
  8. }
  9. //来个子类
  10. function Sub(){
  11. this.subFlag = false;
  12. }
  13. //实现继承
  14. Sub.prototype = new Super;
  15. //给子类添加子类特有的方法,注意顺序要在继承之后
  16. Sub.prototype.getSubFlag = function(){
  17. return this.subFlag;
  18. }
  19. //构造实例
  20. var es5 = new Sub;
但是这样的原型链继承有问题:我们的目标是构造函数的属性私有化,方法复用化,所以我们把属性放在函数内,把方法放到原型上;但是原型链继承显然,父类的属性和方法都放到了子类的原型上;
为了解决上面的做法,我们混合使用 构造函数call 继承;
  1. function Super(){
  2. this.flag = true;
  3. }
  4. Super.prototype.getFlag = function(){
  5. return this.flag; //继承方法
  6. }
  7. function Sub(){
  8. this.subFlag = flase
  9. Super.call(this) //继承属性
  10. }
  11. Sub.prototype = new Super;
  12. var obj = new Sub();
  13. Sub.prototype.constructor = Sub;
  14. Super.prototype.getSubFlag = function(){
  15. return this.flag;
  16. }
但是还有个小问题是,子类.prototype = new 父类,子类.prototype的constructor 就指向了父类,所以我们要重写一下:
  1. Sub.prototype.constructor = Sub;
ES6的继承实现方法,其内部其实也是ES5组合继承的方式,通过call构造函数,在子类中继承父类的属性,通过原型链来继承父类的方法。
我们将 extend 用babel 进行转码:
  1. function _inherits(subClass, superClass) {
  2. // 确保superClass为function
  3. if (typeof superClass !== "function" && superClass !== null) {
  4. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  5. }
  6. // 把子类.prototype 继承了父类.prototype(new 父类), 同时把子类prototype的constructor进行了重写;
  7. // 给subClass添加constructor这个属性
  8. subClass.prototype = Object.create(superClass && superClass.prototype, {
  9. constructor: {
  10. value: subClass,
  11. enumerable: false,
  12. writable: true,
  13. configurable: true
  14. }
  15. });
  16. // 将父类设为子类的prototype
  17. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  18. }
里面 子类. prototype = Object.create ( 父类.prototype )这句话,实际上和下面的代码类似:
  1. 子类.prototype = new 父类
不同的是, 子类. prototype = Object.create ( 父类.prototype )不会继承父类的constructor里面的属性,只会继承父类prototype上的方法;
一个关于 Object.create(car.prototype) 用法的代码:
  1. function Car (desc) {
  2. this.desc = desc;
  3. this.color = "red";
  4. }
  5. Car.prototype = {
  6. getInfo: function() {
  7. return ‘A ‘ + this.color + ‘ ‘ + this.desc + ‘.‘;
  8. }
  9. };
  10. //instantiate object using the constructor function
  11. var car = Object.create(Car.prototype);
  12. car.color = "blue";
  13. alert(car.getInfo()); //displays ‘A blue undefined.‘ ??! // 看见了吧,只会继承方法,不能继承属性
当你只想继承类的原型,而不想继承类的constructor的时候,使用Object.create 是很棒的选择;
如果我们想子类继承父类的prototype ,同时子类也要有自己的属性,请看下面的代码:
  1. var Car2 = Object.create(null); //this is an empty object, like {}
  2. Car2.prototype = {
  3. getInfo: function() {
  4. return ‘A ‘ + this.color + ‘ ‘ + this.desc + ‘.‘;
  5. }
  6. };
  7. var car2 = Object.create(Car2.prototype, {
  8. //value properties
  9. color: { writable: true, configurable:true, value: ‘red‘ },
  10. //concrete desc value
  11. rawDesc: { writable: false, configurable:true, value: ‘Porsche boxter‘ },
  12. // data properties (assigned using getters and setters)
  13. desc: {
  14. configurable:true,
  15. get: function () { return this.rawDesc.toUpperCase(); },
  16. set: function (value) { this.rawDesc = value.toLowerCase(); }
  17. }
  18. });
  19. car2.color = ‘blue‘;
  20. alert(car2.getInfo()); //displays ‘A RED PORSCHE BOXTER.‘
每一个属性又是一堆属性的集合,又称descriptor, 分为 data descriptor 和 accessor(访问 ) descriptor
更多的知识:http://www.htmlgoodies.com/beyond/javascript/object.create-the-new-way-to-create-objects-in-javascript.html

两条继承链
一个继承语句同时存在两条继承链:一条实现属性继承,一条实现方法继承.
  1. class A extends B {}
  2. A.__proto__ === B; //继承属性
  3. A.prototype.__proto__ === B.prototype; //继承方法
ES6的子类的__proto__是父类,子类的原型的__proto__是父类的原型
第二条继承链理解起来没有什么问题,es6 本身就是对es5 混合模式继承的封装,在原型继承上,es6使用的是 
  1. 子类.prototype = Object.create (父类.prototype // 相当于 new 父类
子类的原型是父类的实例(暂时这样理解,其实子类并不能继承父类的属性,只能继承方法),所以子类.prototype(父类的实例)指向父类的prototype。

但是第一个继承链就不好理解了,在ES5中 子类.__proto__是指向Function.prototype的,因为每一个构造函数其实都是Function这个对象构造的。在ES6的继承中,有这样一句话:
  1. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
es6 的 extends 把子类的__proto__指向父类可以实现属性的继承,在ES5中在没有用借用继承的时候由于父类属性被子类原型继承,所有的子类实例实际上都是同一个属性引用。

可以这么说,在ES5继承和构造实例,ES6构造实例的时候可以理解__proto__指向构造函数的原型的,但是在ES6继承中,__proto__指继承自哪个类或原型。





super 关键字

super关键字可以用来调用其父类的构造器或方法。super 作为方法的时候,必须在 constructor 中调用,并且只能在 constructor 里面被调用

  1. class Cat {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. speak() {
  6. console.log(this.name + ‘ makes a noise.‘);
  7. }
  8. }
  9. class Lion extends Cat {
  10. speak() {
  11. super.speak();
  12. console.log(this.name + ‘ roars.‘);
  13. }
  14. }
ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。




类的Getter和Setter方法

与ES5一样,在类内部可以使用getset关键字,对某个属性设置取值和赋值方法。

  1. class Foo {
  2. constructor() {}
  3. get prop() {
  4. return ‘getter‘;
  5. }
  6. set prop(val) {
  7. console.log(‘setter: ‘ + val);
  8. }
  9. }
  10. let foo = new Foo();
  11. foo.prop = 1;
  12. // setter: 1
  13. foo.prop;
  14. // "getter"
上面代码中,prop属性有对应 的赋值和取值方法,因此赋值和读取行为都被自定义了。 
存值和取值方法是设置在属性的descriptor对象上的。
  1. var descriptor = Object.getOwnPropertyDescriptor(Foo.prototype, ‘prop‘);
  2. "get" in descriptor // true
  3. "set" in descriptor // true
上面代码中,存值和取值方法是定义在prop属性的描述对象上的,这与ES5一致。

类的Generator方法

如果类的某个方法名前加上星号(*),就表示这个方法是一个Generator函数。

  1. class Foo {
  2. constructor(...args) {
  3. this.args = args;
  4. }
  5. * [Symbol.iterator]() {
  6. for (let arg of this.args) {
  7. yield arg;
  8. }
  9. }
  10. }
  11. for (let x of new Foo(‘hello‘, ‘world‘)) {
  12. console.log(x);
  13. }
  14. // hello
  15. // world
上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器。


  1. 更多
  1. 是大法官






null


es6