1ES6中的ClassES6提出了类(Class)的概念,使得对象原型更像一门面向对象的语言。在ES6中,对象是由类定义的,类默认有构造方法和自定义方法,但类中包含的方法是不可枚举的。类Point{constructor(){this.x=x;这个.y=y;}toString(){返回this.x+this.y;}}注:constructor方法对应ES5的构造函数;创建类的方法不需要使用function关键字,方法不需要用逗号隔开。在ES5中定义对象时,结合了构造函数模式和原型模式。构造函数模式用于定义实例属性,原型模式用于定义共享方法和共享属性,节省内存,支持向构造函数传递参数。函数点(x,y){this.x=x;这个.y=y;}Point.prototype.toString=function(){返回this.x+this.y;}2ES5和ES6创建实例和对象的比较1)ES5中的Person构造函数和Person原型对象与实例Person1和Person2的关系:构造函数的prototype属性和实例的__proto__属性指向原型对象,原型对象的构造函数属性指向构造函数。构造函数的prototype属性在ES6中依然存在,与ES5相同:类的方法定义在类的原型上,调用类实例上的方法实际上就是调用原型上的方法.//实例的构造方法是类原型(prototype)的构造方法classB{constructor(){}}letb=newB();console.log(b.constructor===B.prototype.constructor);//true2)ES6类可以看做构造函数的另一种写法(相当于ES5中的构造函数不是构造函数方法):类是函数类型,类本身指向类的constructor属性类的原型对象,与ES5相同。classPoint{//...}console.log(typeofPoint)//"function"console.log(Point===Point.prototype.constructor)//true3)使用类创建实例对象也是直接使用class上的new命令与ES5中构造函数的用法一致。4)类内部定义的方法是不可枚举的,这一点与ES5不同。5)构造方法一个类必须有构造方法,如果没有,默认加上constructor(){}。类虽然是函数,但是和ES5不同,直接调用类而不传递new会导致类型错误,也就是说这个函数只有和new一起使用才有意义。6)类的实例和ES5一样,除非实例的属性显式定义在自身上(即定义在this对象上),否则定义在原型上(即定义在类上)).classPoint{constructor(x,y){this.x=x;这个.y=y;}toString(){返回'('+this.x+','+this.y+')';}}varpoint=newPoint(2,3);point.toString()//(2,3)point.hasOwnProperty('x')//truepoint.hasOwnProperty('y')//truepoint.hasOwnProperty('toString')//falsepoint.__proto__.hasOwnProperty('toString')//true上面代码中x和y都是实例对象点自身的属性(因为定义在这个变量上),所以hasOwnProperty方法返回true,而toString是原型的属性对象(因为它是在Point类上定义的),所以hasOwnProperty方法返回false。这些与ES5行为一致。与ES5一样,一个类的所有实例共享一个原型对象。varp1=新点(2,3);varp2=新点(3,2);p1.__proto__===p2.__proto__//true可以通过实例的__proto__属性给Class原型添加方法,添加后所有原型都有这个方法,和ES5一样。varp1=新点(2,3);varp2=新点(3,2);p1.__proto__.printName=function(){return'Oops'};p1.printName()//“糟糕”p2.printName()//"Oops"7)Class没有变量提升器,这与ES5完全不同。新富();//ReferenceErrorclassFoo{}上面代码中,先使用Foo类,后定义,所以会报错,因为ES6不会将类声明提升到代码头。这个规定的原因与继承有关,继承必须保证子类在父类之后定义。8)在严格模式类和模块内部,默认是严格模式,所以不需要使用usestrict来指定运行模式。只要你的代码是写在类或模块中的,就只有严格模式可用。考虑到未来所有的代码实际上都会以模块的形式运行,ES6实际上是将整个语言升级为严格模式。3ES6Class的继承Class通过extends关键字实现继承,区别于ES5通过修改原型链实现继承(巧的是SASS也是通过@extend实现样式继承):classColorPointextendsPoint{}子类必须在constructor方法调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承了父类的this对象,然后对其进行处理。如果不调用super方法,子类就无法获取到这个对象。classColorPointextendsPoint{constructor(x,y,color){super(x,y);//调用父类的构造函数(x,y)this.color=color;}toString(){returnthis.color+''+super.toString();//super代表父类的原型,调用父类的toString()}}上面代码中,super关键字同时出现在constructor方法和toString方法中,代表父类的constructor用于创建父类的this对象。super关键字可以用作函数或对象。第一种情况,super作为函数调用时,代表父类的构造函数,只能在子类的构造函数中使用。ES6要求子类的构造函数必须执行一次超函数。第二种情况,当super是对象时,指的是父类的原型对象。ES5继承的本质是先创建子类的实例对象this,然后将父类的方法添加到this中(Parent.apply(this))。(ES5通过原型模式继承:创建一个子类,然后将父类的实例赋值给子类原型,即重写子类原型,替换为新类型的实例。)ES6的继承机制是完全不同的。就是先创建父类的实例对象this(所以必须先调用super方法),然后用子类的构造函数修改this。如果子类没有定义构造方法,则默认添加该方法。在子类的构造函数中,this关键字只能在调用super之后使用,否则会报错。这是因为子类实例的构造是基于父类实例的处理,只有super方法才能返回父类实例。classPoint{constructor(x,y){this.x=x;这个.y=y;}}classColorPointextendsPoint{constructor(x,y,color){this.color=颜色;//ReferenceErrorsuper(x,y);this.color=颜色;//正确}}和ES5一样,子类创建的实例是父类和子类的实例:letcp=newColorPoint(25,8,'green');cpinstanceofColorPoint//truecpinstanceofPoint//true4补充1)类的prototype属性和__proto__属性(这部分我还没看懂,太绕了。)ES5中每个对象都有一个__proto__属性,指向相应的构造函数的原型属性。ES6中没有构造函数,Class就是构造函数的语法糖。它同时具有prototype属性和__proto__属性,所以同时存在两条继承链。(1)子类的__proto__属性表示构造函数的继承,始终指向父类。(2)子类的prototype属性的__proto__属性表示方法的继承,始终指向父类的prototype属性。这个结果是因为类继承是按照下面的模式实现的。classA{}classB{}//B的实例继承了A的实例Object.setPrototypeOf(B.prototype,A.prototype);常量b=新B();//B的实例继承了A的静态属性ObjectsetPrototypeOf(B,A);常量b=新B();这两条继承链可以这样理解:作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型(原型属性)是父类的一个实例。2)可以使用Object.getPrototypeOfObject.getPrototypeOf方法从子类中获取父类。Object.getPrototypeOf(ColorPoint)===Point//true所以可以用这个方法来判断一个类是否继承了另一个类。3)实例的__proto__属性子类实例的__proto__属性的__proto__属性指向父类实例的__proto__属性。即子类实例的原型的原型就是父类实例的原型。varp1=新点(2,3);varp2=newColorPoint(2,3,'red');p2.__proto__===p1.__proto__//falsep2.__proto__.__proto__===p1.__proto__//true总的来说ES6是对ES5的正式改动,真正的内容不变,本质还是实现通过原型链继承。
