跳至主要內容

11. 类对象

Harry Xiong大约 4 分钟Web 前端JavaScriptPromise 对象JS 异步async 与 await

类对象

class用法跟let和const一样,不存在变量提升,也不能重复声明类名,JS中的类(class)是在ES6中被推出为关键字,实际上也是通过原型构成的模拟类

ES5面对对象写法和传统的面向对象语言(如C++和JAVA)差异很大,很容易让新学习这门语言的人感到困惑。所以在ES6中提供了更接近传统语言的写法,引入了class这个概念,作为对象的模板,通过class关键字,可以定义类。

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

11.1 用法

//ES5写法
function Person(name,age,gender){
    this.name=name;
    this.age=age;
    this.gender=gender;
}
Person.prototype.sayName=function(){
    alert("hello"+this.name);
}


//ES6写法
class Person{
    constructor(name,age,gender){
        this.name=name;
        this.age=age;
        this.gender=gender;
    }
    
    sayName(){
        alert("hello"+this.name);
    }//其实就是ES6中函数的简写,但是必须用这种简写形式创建方法
}

注意:

  • 我们通常只会在原型中放方法,在ES5的语法中虽然在原型中可以放普通对象,但是其实不推荐这样做,所以在用class声明的类中只允许在constructor()函数中放入对象,而在constructor()函数外只能放方法

  • 构造函数的prototype属性在ES6的class中依然存在,事实上,class中定义的所有方法全部都作为prototype属性的方法保存

    class Fun{
        construct(){
            
        }
        
        say(){
                 
        }
        
        add(){
            
        }
    }
    //等同于
    function Fun(){
        construct(){
                   
        },
        say(){
                
        },
        add(){
                    
        }
    }
    
  • 如果不传入参数,constructor()函数可以不用写,但是在解析的时候会自动为这个类加上该函数方法

11.2 静态方法

类相对于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,只能通过类本身来调用,这就是静态方法

**注意:**ES6中规定class内部只有静态方法,并没有静态属性,也就是不能设置静态属性,如果要设置静态属性只能在外面手动设置

class Person{
    constructor(name,age,gender){
        this.name=name;
        this.age=age;
        this.gender=gender;
    }
    
    static say(){
        alert("hello");
    }
}
Person.say();//"hello"
Person.age=18;
console.log(Person.age);//18

11.3 继承

在class中通过extends关键字进行类的继承

class Person{
    constructor(name,age,gender){
        this.name=name;
        this.age=age;
        this.gender=gender;
    }
    
    sayName(){
        alert("hello"+this.name);
    }
}

class Studnet extends Person{
    constructor(name,age,gender,score){
        super(name,age,gender)//必须调用super()方法,不然新建对象时会报错
        this.score=score;
    }
}

let student=new Student("孙悟空",18,"男",100);
student.sayName();

注意:

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

  • 子类会将父类的静态方法与静态属性也一起继承,通过子类同样也能用父类的静态方法

11.4 super

super关键字既可以当作函数使用,又可以直接当作对象调用,这两种情况下的super的用法完全不同

  • **super作为函数使用时,代表调用父类的构造函数。**并且在子类的构造函数中必须执行一次super()方法 注意:

    • super当作函数使用时只能用在子类构造函数中,如果用在其它地方就会报错
    • 父类可以不写constructor()方法,因为在调用的时候会自动加上该函数,但是子类必须写该方法,因为super()方法必须写在constructor()方法中,而super()方法在继承中又是必写的,不写子类就不具备this,从而无法返回对象出来
class A{
    constructor(){
        
    }
}

class B extends A{
    constructor(){
        super();
    }
}

/*
子类B的构造函数之中的super()虽然代表的是父类A的构造函数,但是返回的确实子类B的实例,即super()内部的
this是指向B,所以super()相当于A.prototype.constructor.call(this)
*/
  • super当作对象使用时,在普通实例方法中指向父类的原型对象,在静态方法中指向父类

    class A{
        constructor(){
            say(){
                return "hello";
            }
        }
    }
    
    class B extends A{
        constructor(){
            super();
            console.log(super.say());//"hello"
        }
    }
    
    /*
    在创建实例对象时就会在控制台打印"hello",子类B中的super.say()就是将super当作一个对象进行使用,此时的super在普通实例方法中指向的是A.prototype,super.say()就相当于A.prototype.say()
    */
    

    由于this是指向子类的,所以如果通过super对某个属性进行赋值,这时的super就是this,赋值的属性就会变成子类实例的属性

    class A{
        constructor(){
            this.x=1;
        }
    }
    
    class B extends A{
        constructor(){
            super();
            console.log(this.x);//1
            this.x=2;
            console.log(this.x);//2
            super.x=3;
            console.log(super.x)//undefined,由于此时的super的指向是A.prototype
            console.log(this.x)//3
        }
    }
    
    /*
    super.x=3等同于this.x=3,而读取super.x的时候此时super的指向是A.prototype,也就是
    A.prototype.x,因为没有设置实例属性,所以此时的值为undefined
    */