基本上,ES6 的class能够看做只是一个语法糖,它的绝大部分功能,ES5 均可以作到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。编程
//类的写法
class Point{
getName(){
return 'lili';
}
}
//构造函数的写法
function Point2() {}
Point2.prototype.getName = function () {
return 'lili';
}
复制代码
其实class也是函数,类里的方法也是定义在prototype上。类的方法都是不可枚举的,经过Object.keys不能得到,可是构造函数原型上的方法能够得到。bash
//类
typeof Point; //'function'
Point === Point.prototype.constructor //true
Object.getOwnPropertyNames(Point.prototype)
//[ 'constructor', 'getName' ]
Object.keys(Point.prototype)
//[]
//构造函数
Object.getOwnPropertyNames(Point2.prototype);//包括自身的,可枚举和不可枚举的
//[ 'constructor', 'getName' ]
Object.keys(Point2.prototype);
[ 'getName' ]
复制代码
类中能够显示定义constructor方法,若是没有定义,则隐式建立一个空的constructor方法。app
class point {}
//等同于
class Point{
constructor(){
}
}
复制代码
constructor方法,默认返回值是类的实例对象(this)。能够自定义返回值 ,若是这样作改变this值了,最好不要这样作。函数
class Point{
constructor(x, y){
this.x = x;
this.y = y;
return {}; //不推荐用法
}
}
let point = new Point(1, 2); //{}
复制代码
注意:生成实例,类必须使用new关键字定义,不然报错。构造函数不是必须使用newpost
class Point{}
let point = Point.call({}); //报错
function Point2(x) {
this.x = x;
return this;
}
let point2 = Point2.call({}, 1);//ok {x: 1}
复制代码
类的全部实例共享一个原型对象学习
class Point {
y = 10; //定义属性方式
constructor(x){
this.x = x; //定义属性方式
}
setZ(z){
this.z = z; //定义属性
}
getX(){
return this.x;
}
}
let point = new Point(1);
let point2 = new Point(2);
point.hasOwnProperty('x'); //true
point.hasOwnProperty('getX'); //false
point.__proto__ === point2.__proto__ === Point.prototype //true
Object.getOwnPropertyNames(Point.prototype)
//[ 'constructor', 'getX' ]
复制代码
this是实例对象,定义在this上的属于实例的自身的属性,如x,y,z。getX方法是定义在原型对象上的,全部的实例共享的。ui
静态属性如今尚未this
静态方法spa
class Point {
static getX() {
this.getY();
}
static getY(){
console.log('y');
}
getY(){
console.log('yy');
}
}
let point = new Point();
Point.getX();
//'y'
复制代码
私有属性和私有方法尚未实现。只能约定下划线打头方法为私有的。或利用Symbol值的惟一性生成prototype
const bar = Symbol('bar');
class Point {
[bar](){
console.log('x')
}
}
let point = new Point()
console.log(Reflect.ownKeys(Point.prototype));
//[ 'constructor', Symbol(bar) ]
复制代码
通常状况下外界取不到bar的值,因此成了私有方法。可是也不是绝对不行,Reflect.ownKeys()依然能够拿到它们。
new是从构造函数生成实例对象的命令。ES6 为new命令引入了一个new.target属性,该属性通常用在构造函数之中,返回new命令做用于的那个构造函数。若是构造函数不是经过new命令或Reflect.construct()调用的,new.target会返回undefined,所以这个属性能够用来肯定构造函数是怎么调用的。
class Point {
constructor() {
console.log(new.target === Point); //true
}
}
let point = new Point();
复制代码
只能在构造函数constructor中使用new.target。其余地方使用会报错
function Point2(name) {
if (new.target === Point2) {
this.name = name;
} else {
throw new Error('必须使用 new 命令生成实例');
}
}
let point3 = new Point2('sha');
let point2 = Point2.call({}, 'li'); //抛出异常
复制代码
利用new.target,限定构造函数建立实例,只能使用new操做符。
利用这个特色,能够写出不能独立使用、必须继承后才能使用的类
class Shape {
constructor() {
if (new.target === Shape) {
throw new Error('本类不能实例化');
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super();
// ...
}
}
var x = new Shape(); // 报错
var y = new Rectangle(3, 4); // 正确
复制代码
继承就是子类继承父类的属性和方法。注意如下几点:
class Point {
static hello() { //静态方法会被子类继承
console.log('hello world');
}
constructor(x, y) {
this.x = x;
this.y = y;
}
getX(){
return this.x;
}
}
class ColorPoint extends Point{
constructor(x, y, color){
this.color = color; //错误
super(x, y);
this.color = color; //正确
}
getX(){
console.log(this); //ColorPoint { x: 10, y: 1, color: 'red' }
return this.color + ' ' + super.getX();
}
}
let cp = new ColorPoint(10, 1, 'red');
console.log(cp.getX()); //red 10
ColorPoint.hello(); //hello world
Object.getPrototypeOf(ColorPoint) === Point //true
cp instanceof Point //true
cp instanceof ColorPoint //true
复制代码
ES5 的继承,实质是先创造子类的实例对象this,而后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制彻底不一样,实质是先将父类实例对象的属性和方法,加到this上面(因此必须先调用super方法),而后再用子类的构造函数修改this。若是不调用super方法,子类就得不到this对象。
super这个关键字,既能够看成函数使用,也能够看成对象使用。
第一种状况,super做为函数调用时,表明父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。而且super函数只能用在子类的构造函数之中,用在其余地方就会报错。
第二种状况,super做为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
在子类普通方法中经过super调用父类的方法时,方法内部的this指向当前的子类实例。因为super指向父类的原型对象,因此定义在父类实例上的方法或属性,是没法经过super调用的。
在子类的静态方法中经过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。
class A {
constructor() {
console.log(new.target.name);
this.x = 10;
}
getX(){
return this.x;
}
}
class B extends A {
constructor() {
super();
this.x = 20;
}
}
let a = new A() // A
let b = new B() // B
b.getX(); //20
复制代码
B中super()在这里至关于A.prototype.constructor.call(this)。new.target指向当前正在执行的函数
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1 静态方法
var child = new Child();
child.myMethod(2); // instance 2 普通方法
复制代码
子类的__proto__属性,表示构造函数的继承,老是指向父类。
子类prototype属性的__proto__属性,表示方法的继承,老是指向父类的prototype属性。
class A {
}
class B extends A {
}
B.__proto__ === A // true 属性继承
B.prototype.__proto__ === A.prototype // true 方法继承
复制代码
注:本文是读阮一峰老师《ECMAScript 6 入门》的学习笔记