关于js中function和class的内在关系及区别的理解
在js中,从es6开始引进class,根本上是基于js中已经存在的原型继承的语法糖,class语法并没有引进一种新的面向对象的继承机制。
一、定义class
class事实上是一种特殊的funcion,就像可以定义funcion表达式和funcion声明一样,class语法也有2种形式:class表达式和class声明。
1、class声明
定义一个class,可以使用class关键字加上类名。需要注意的是,funcion声明和class声明有一个重要的却别:funcion声明是hosting(状态提升)的,而class不是,class需要先声明再使用。
1 class Rectangle { 2 constructor(height, width) { 3 this.height = height; 4 this.width = width; 5 } 6 }
2、class表达式
class表达式类名可选,已经命名的class可以通过实例的'.name'属性获取,依据class自身的命名。
1 // unnamed 2 let Rectangle = class { 3 constructor(height, width) { 4 this.height = height; 5 this.width = width; 6 } 7 }; 8 console.log(Rectangle.name); 9 // output: "Rectangle" 10 11 // named 12 let Rectangle = class Rectangle2 { 13 constructor(height, width) { 14 this.height = height; 15 this.width = width; 16 } 17 }; 18 console.log(Rectangle.name); 19 // output: "Rectangle2"
二、class中body和method的定义
class的body部分包含在花括号{}中,这里是定义class成员的地方,比如constructor和method。
1、constructor
constructor方法是一个特殊的方法,用来创建并初始化一个对象。在一个class中只能有一个命名为constructor的特殊方法,如果包含多个将会报错。
constructor中可以通过super关键字,调用父类的constructor方法。
2、prototype methods(原型方法)
1 class Rectangle { 2 constructor(height, width) { 3 this.height = height; 4 this.width = width; 5 } 6 // Getter 7 get area() { 8 return this.calcArea(); 9 } 10 // Method 11 calcArea() { 12 return this.height * this.width; 13 } 14 } 15 16 const square = new Rectangle(10, 10); 17 18 console.log(square.area); // 100
3、static methods(静态方法)
通过static关键字为一个class创建静态方法,static methods的调用无需对class实例化,也不能被实例对象所调用。
1 class Point { 2 constructor(x, y) { 3 this.x = x; 4 this.y = y; 5 } 6 7 static distance(a, b) { 8 const dx = a.x - b.x; 9 const dy = a.y - b.y; 10 11 return Math.hypot(dx, dy); 12 } 13 } 14 15 const p1 = new Point(5, 5); 16 const p2 = new Point(10, 10); 17 18 console.log(Point.distance(p1, p2)); // 7.0710678118654755
4、static和prototype method的封装
当static或prototype method被调用的时候,如果没有对this赋值,那么this将是undefine状态。这和是否采用static模式无关,因为class类体中的代码已经默认执行static模式。
1 class Animal { 2 speak() { 3 return this; 4 } 5 static eat() { 6 return this; 7 } 8 } 9 10 let obj = new Animal(); 11 obj.speak(); // Animal {} 12 let speak = obj.speak; 13 speak(); // undefined 14 15 Animal.eat() // class Animal 16 let eat = Animal.eat; 17 eat(); // undefined
如果以上代码是基于传统的function的语法,则this值是global object。
三、extends创建子类
extends关键字用于在class声明或表达式中,创建另一个class的子类。
1 class Animal { 2 constructor(name) { 3 this.name = name; 4 } 5 6 speak() { 7 console.log(this.name + ' makes a noise.'); 8 } 9 } 10 11 class Dog extends Animal { 12 constructor(name) { 13 super(name); // call the super class constructor and pass in the name parameter 14 } 15 16 speak() { 17 console.log(this.name + ' barks.'); 18 } 19 } 20 21 let d = new Dog('Mitzie'); 22 d.speak(); // Mitzie barks.
如果在子类中有constructor方法,在使用"this"之前需要调用super()。也可以继承自function-based的class。
1 function Animal (name) { 2 this.name = name; 3 } 4 5 Animal.prototype.speak = function () { 6 console.log(this.name + ' makes a noise.'); 7 } 8 9 class Dog extends Animal { 10 speak() { 11 console.log(this.name + ' barks.'); 12 } 13 } 14 15 let d = new Dog('Mitzie'); 16 d.speak(); // Mitzie barks.
需要注意的是:class不能继承自常规对象(non-constructible),如果想继承自常规对象,可以使用Object.setPrototypeOf()替代。
1 const Animal = { 2 speak() { 3 console.log(this.name + ' makes a noise.'); 4 } 5 }; 6 7 class Dog { 8 constructor(name) { 9 this.name = name; 10 } 11 } 12 13 // If you do not do this you will get a TypeError when you invoke speak 14 Object.setPrototypeOf(Dog.prototype, Animal); 15 16 let d = new Dog('Mitzie'); 17 d.speak(); // Mitzie makes a noise.
四、super关键字调用父类
关键字super用于调用父类相应的方法,这是相较于原型继承的一个好处。
1 class Cat { 2 constructor(name) { 3 this.name = name; 4 } 5 6 speak() { 7 console.log(`${this.name} makes a noise.`); 8 } 9 } 10 11 class Lion extends Cat { 12 speak() { 13 super.speak(); 14 console.log(`${this.name} roars.`); 15 } 16 } 17 18 let l = new Lion('Fuzzy'); 19 l.speak(); 20 // Fuzzy makes a noise. 21 // Fuzzy roars.