js 总结ES6中Class以及继承

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_37473645/article/details/89716231

一、Class

1、ES6中的class可以看作是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更加面向对象编程的语法而已。


class Point { // 定义一个Point类
    constructor(x,y){ // 构造方法
        this.x=x;
        this.y=y;
    }
    // =>类的所有方法都定义在类的prototype属性上。
    // =>在类的实例上调用方法,其实就是调用原型的方法。
    // =>类的内部所有方法都是不可枚举的
    toString(){ // 定义toString方法  
        return '('+this.x+','+this.y+')';
    }
}
 // =>Object.assign方法可以方便地一次向类添加多个方法。
 Object.assign(Point.prototype,{
     toValue1(){ return "添加方法1"},
     toValue2(){ return "添加方法2"}
 })

console.log(typeof Point); // =>function =>类的数据类型就是函数
console.log(Point === Point.prototype.constructor) // =>true =>类的本身就指向构造函数

var point1=new Point(); // 使用的时候也是直接对类使用new命令,跟构造函数的用法完全一致。
console.log(point1.toString()); // =>(undefined,undefined)
var point2=new Point(1,1); // 使用的时候也是直接对类使用new命令,跟构造函数的用法完全一致。
console.log(point2.toString()); // =>(1,1)

2、类和模块的内部默认使用严格模式,所以不需要使用 use strict 指定运行模式。只要将代码写在类或模块之中,那么就只有严格模式可用。

3、constructor 方法是类的默认方法,通过 new 命令生成对象实例时自动调用该方法。一个类必须有 constructor 方法,如果没有显示定义,一个空的 constructor 方法会被默认添加。

4、类必须使用 new 来调用,否则就会报错。

5、实例的属性除非显示定义在其本身(即 this 对象)上,否则都是定义在原型(即 Class )上。 

6、类的实例共享一个原型对象。

7、Class表达式

// =>Class也可以使用表达式的形式定义。
// =>注意这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指当前类。
const MyClass = class Me{
    getClassName(){
        return Me.name;
    }
}

8、类不存在变量提升

9、ES6不提供私有方法,不支持私有属性。目前有一个提案为class加私有属性。方法是在属性名前,使用#来表示。

10、类的方法内部如果含有 this,它将默认指向类的实例。

11、name 属性返回 class 后面的类名。

class Point{

}
console1.log(Point.name); //=>"Point"

12、在类的内部可以使用 get 和 set 关键字对某个属性设置存储函数和取值函数,拦截该属性的存取行为。

 

class MyClass{
    constructor(){
        // ...
    }
    get prop(){
        return 'getter';
    }
    set prop(value){
        console.log('setter'+value);
    }
}
let inst=new MyClass();
inst.prop=123; // =>setter123
console.log(inst.prop); // =>getter

13、存值函数和取值函数是设置在属性的Descriptor对象上的。

14、如果某个方法之前加上星号(*),就表示该方法是一个Generator函数。

15、类相当于实例中的原型,所有在类中定义的方法都会被实例继承。如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类调用,称之为“静态方法”。

class Foo{
    static classMethod(){
        return 'hello';
    }
}
console.log(Foo.classMethod()); // =>hello
let foo=new Foo();
foo.classMethod(); // =>Uncaught TypeError: foo.classMethod is not a function

16、父类的静态方法可以被子类继承。

class Foo{
    static classMethod(){
        return 'hello';
    }
}
class Bar extends Foo{
}

console.log(Bar.classMethod()); // =>hello

17、 静态方法也可以从super对象上调用。

class Foo{
    static classMethod(){
        return 'hello';
    }
}
class Bar extends Foo{
    static classMethod(){
        return super.classMethod()+',too';
    }
}

console.log(Bar.classMethod()); // =>hello,too

18、静态属性指的是Class本身的属性,即Class.propname,而不是定义在实例对象(this)上的属性。Class内部只有静态方法,没有静态属性。正确写法如下:

class Foo{

}
Foo.prop=1;
console.log(Foo.prop); // =>1

19、Class的实例属性可以直接用等式写入类的定义之中。

20、Class的静态属性只要在实例属性写法前面加上 static 关键字就可以了。

21、new.target属性用于确定构造函数是怎么调用的。

二、ES6中Class的继承

首先我们要知道一下这些要点:

1、Class通过 extends 关键字实现继承。

2、super 关键字既可以当作函数使用,也可以当作对象使用。

        ① super 作为函数调用时表示父类的构造函数,用来新建父类的this对象

        同时需要注意:

        a、子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。

        b、如果子类没有定义 constructor 方法,那么这个方法就会被默认添加。

        c、在子类的构造函数中,只有调用 super 方法之后才能够使用 this 关键字,否则会报错。

        d、super 虽然代表了父类的构造函数,但是返回的是子类的实例,即 super 内部的 this 指定的是子类。

        e、super () 只能用在子类的构造函数之中,用在其他地方会报错。

        ② super 作为对象时在普通方法中指向父类的原型对象;在静态方法中指向父类

        同时需要注意:

        a、由于 super 指向父类的原型对象,定义在父类实例上的方法或属性是无法通过 super 调用的。如果定义在父类的原型上,super 就可以取到。

        b、ES6规定,通过 super 调用父类的方法时,super 会绑定子类的 this。

        c、如果通过 super 对某个属性赋值,这时 super 就是 this,赋值的属性会变成子类实例的属性。

        d、如果 super 作为对象用在静态方法之中,这时 super 将指向父类,在普通方法之中指向父类的原型对象。

class Point{ // 定义了一个名字为Point的类
    constructor(x,y){ // constructor是一个构造方法,用来接收参数
        this.x=x;
        this.y=y;
        this.z=1;
        this.a=3;
    }
    toString(){
        return "颜色";
    }
    print(){
        console.log(this.z);
    }
    static myMethod(msg){
        console.log('static',msg);
    }

    myMethod(msg){
        console.log('instance',msg);
    }
}
class ColorPoint extends Point{
    constructor(x,y,color){
        super(x,y); // 调用父类的constructor(x,y)
        this.z=2;
        this.a=4;
        super.a=5;
        this.color=color;
        console.log(super.a);
        console.log(this.a);
    }
    toString(){ // 这是一个类的方法,注意前面没有function
        return this.color+' '+super.toString(); // 调用父类的toString()
    }
    m(){
        super.print();
    }
    static myMethod(msg){
        super.myMethod(msg);
    }
    myMethod(msg){
        super.myMethod(msg);
    }
}
let color1=new ColorPoint(1,1,"red");
let color2=new ColorPoint(2,2,"blue");
console.log(color1);
console.log(color1.toString());
color1.m();
ColorPoint.myMethod("你好");
color1.myMethod("你好");
console.log("===================");
console.log(color2);
console.log(color2.toString());
color2.m();

3、Class 作为构造函数的语法糖,同时有 prototype 属性和 __proto__ 属性,因此同时存在两条继承链。

        ①子类 __proto__ 属性表示构造函数的继承,总是指向父类。

        ②子类 prototype 属性的 __proto__ 属性表示方法的继承,总是指向父类的 prototype属性。

class A{

}
class B extends A{

}
console.log(B.__proto__ === A); // =>true
console.log(B.prototype.__proto__ === A.prototype); // =>true

        补充:这两条继承链还可以理解为:作为一个对象,子类(B)的原型( __proto__ 属性)是父类(A);作为一个构造函数,子类(B)的原型( prototype 属性)是父类的实例。(感觉很像 组合继承/伪经典继承)

4、extends 关键字后只要是一个有 prototype 属性的函数,就能被继承(除了 Function.prototype 函数)。

下面讨论三种特殊情况:

        ①子类继承Object

        这种情况下,子类就是构造函数 Object 的复制,A的实例就是 Object 的实例。

class B extends Object{

}
console.log(B.__proto__ === Object); // =>true
console.log(B.prototype.__proto__ === Object.prototype); // =>true

        ②不存在任何继承

        这种情况下,父类作为一个基类(即不存在任何继承)就是一个普通函数,所以直接继承Function.prototype。但是,父类调用后返回的是一个空对象(即 Object 实例),所以 父类.prototype.__proto__ 指向构造函数( Object )的 prototype 属性。

class B {

}
console.log(B.__proto__ === Function.prototype); // =>true
console.log(B.prototype.__proto__ === Object.prototype); // =>true

        ③子类继承null

        这种情况和第二种情况很类似。父类作为一个基类(即不存在任何继承)就是一个普通函数,所以直接继承Function.prototype。

class B extends null{

}
console.log(B.__proto__ === Function.prototype); // =>true
console.log(B.prototype.__proto__ === undefined); // =>true

        但是,父类调用后返回的对象不继承任何方法,所以它的 __proto__ 指向 Function.prototype ,即执行了下面的代码。

class B extends null{
    constructor(){
        return Object.create(null);
    }
}

5、子类实例的 __proto__ 属性的 __proto__ 属性指向父类实例的 __proto__ 属性。即子类的原型的原型是父类的原型

猜你喜欢

转载自blog.csdn.net/qq_37473645/article/details/89716231
今日推荐