目录
Class类
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰更像面向对象编程的语法而已。所以ES6 的类,完全可以看作构造函数的另一种写法。
class Person{ constructor(name,age){ this.name = name; this.age = age; } sayName(){ console.log("i am ",this.name); } }
构造函数
constructor
方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。class Person{ }
等同于
class Person{ // 构造函数 constructor(){} }
实例属性和方法
定义在类体中的属性称为实例属性,定义在类体中的方法称为实例方法。如下,temp、name、age就是实例属性,sayName方法就是实例方法,本质上该方法应该是声明在
Person.prototype
中,可供所有的实例调用,因此称为实例方法。class Person{ // 实例属性 temp = 'briup'; constructor(name,age){ // 实例属性 常用 this.name = name; this.age = age; // 实例方法 不常用 this.sayName = function(){ console.log("i am ",this.name); } } } let p1 = new Person('Ronda',22); let p2 = new Person('Fairy',23); console.log(p1.sayName === p2.sayName); // false 因为是实例方法,所有每个实例内都会有自己的方法 console.log(p1.name); // Ronda console.log(p1.temp); // briup p1.sayName(); // i am Ronda
原型属性和方法
原型属性和方法顾名思义就是在构造函数的原型对象上存在的属性和方法。
class Person { constructor(name, age) { this.name = name; this.age = age; } // 原型方法 常用 sayName() { console.log("i am ", this.name); } } // 原型属性 不常用 Person.prototype.temp = 'briup'; let p1 = new Person('Ronda', 22); let p2 = new Person('Fairy', 23); p1.sayName(); // i am Ronda console.log(p1.sayName === p2.sayName); // true console.log(p1.temp); // briup console.log(p2.temp); // briup
静态属性和方法
通过
static
关键字来定义静态属性和静态方法。静态属性和静态方法是定义在类上的,所以可以通过类直接访问。在静态方法中,this
指向当前类。class Person { // 静态属性 static num = 200; // 静态方法 static number() { console.log(this); // [class Person] { num: 200 } return this.num; } } console.log(Person); // [class Person] { num: 200 } console.log(Person.number()); //200 console.log(Person.num); //200
继承
class
可以通过extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要简洁很多。子类中可以没有构造函数,系统会默认提供一个。子类如果提供了构造函数就必须显示调用super。super函数类似于之前的借用构造函数。子类必须在
constructor
方法中调用super
方法,否则新建实例时会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象。class Animal { constructor(name){ this.name = name; } sayName(){ console.log("my name is ",this.name); } } class Dog extends Animal{ constructor(name,age){ super(name); this.age = age; } } let dog = new Dog('乐乐','1'); dog.sayName(); // 打印出:my name is 乐乐 console.log(Dog);//[class Dog extends Animal] console.log(Dog.prototype);//Animal{} console.log(Object.getPrototypeOf(Dog));//[class Animal] console.log(Object.getPrototypeOf(Dog.prototype));//{} console.log(Dog.__proto__ === Animal); //true console.log(Dog.prototype.__proto__ === Animal.prototype); //true
class 作为构造函数的语法糖,同时有prototype属性和proto属性,因此同时存在两条继承链 1.子类的proto属性,表示构造函数的继承,总是指向父类。
2.子类prototype属性的proto属性,表示方法的继承,总是指向父类的prototype属性。
super 关键字
super
这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。第一种情况,
super
作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super
函数。class A {} class B extends A { constructor() { super(); } }
第二种情况,
super
作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。class A { p() { return 2; } } class B extends A { constructor() { super(); // 这里 super() 代表调用父类构造函数 console.log(super.p()); // 2 这里的super是对象,因为在普通方法内使用,所以super对象是父类的原型对象 } } let b = new B();
这里需要注意,由于
super
指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super
调用的。class A { constructor() { this.p = 2; } } class B extends A { m() { return super.p; } } let b = new B(); console.log(b.m()) // undefined console.log(b.p); // 2
下方案例中,super指向父类本身
class A { // 静态属性 static num = 200; constructor() { this.num = 20; } // 静态方法 static number() { return this.num; } } class B extends A { static getNumber() { // super是A类本身 return super.number(); } } console.log(B.getNumber()); //200