es6 引入的 class 类实质上是 JavaScript 基于原型继承的语法糖。javascript
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHi = () => {
return `Hello ${this.name}`;
};
// 等同于
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hello ${this.name}`;
}
}
复制代码
类由两部分组成:类声明,类表达式java
class Animal {
constructor(name) {
this.name = name;
}
}
复制代码
类其实是个特殊的函数
,普通函数声明和类函数声明有一个重要的区别就是函数 声明会提高,而类声明不会。若是先访问,后声明就会抛出相似于下面的错误。es6
let animal = new Animal();
// Uncaught ReferenceError: Cannot access 'Animal' before initialization
class Animal {}
复制代码
类表达式能够是被命名的或匿名的,(ps: 类表达式也一样受到类声明中提到的提高问题的困扰。)函数
// 匿名类
let Animal = class {
constructor(name) {
this.name = name;
}
};
// 命名类
let Animal = class Cat {
constructor(name) {
this.name = name;
}
getClassName() {
return Cat.name;
}
};
复制代码
此时类名字Cat
只能在 class 内部使用,指代当前类,在类的外部只能用Animal
。ui
constructor
方法是类的默认方法,经过new
建立对象实例时,自动会调用该方法, 一个类必须拥有constructor
方法,若是没有写,JavaScript 引擎会默认加上空的constructor
方法。class Animal {}
// 等同于
class Animal {
constructor() {}
}
复制代码
constructor
方法默认返回实例对象(既this
),彻底能够指定返回另一个对象this
class Animal {
constructor() {
return Object.create(null);
}
}
new Animal() instanceof Animal; // false;
复制代码
上面代码中,constructor
函数返回一个全新的对象,结果致使实例对象不是Animal
类的实例。spa
严格模式 类和模块的内部,默认就是严格模式,好比,构造函数,静态方法,原型方法,getter 和 setter 都在严格模式下执行。prototype
类的实例 类的实例,经过 new 建立, 建立时会自动调用构造函数code
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return "My name is " + this.name;
}
}
let animal = new Animal("rudy");
animal.sayHi(); // My name is rudy
复制代码
get
和set
关键字,对某个属性设置存取 函数和取值函数,拦截该属性的存取行为。class Animal {
constructor(name) {
this.name = name;
}
get name() {
return "rudy";
}
set name(value) {
console.log("setter, " + this.value);
}
}
let animal = new Animal("rudy");
animal.name = "Tom"; // setter, Tom
console.log(a.name); // rudy
复制代码
static
修饰符修饰的方法称为静态,它们不须要实例化,直接经过类来调用。class Animal {
static sayHi(name) {
console.log("i am " + name);
}
}
let animal = new Animal();
Animal.sayHi("rudy"); // i am rudy
animal.sayHi("rudy"); // Uncaught TypeError: animal.sayHi is not a function
复制代码
this.xxx
来定义,但最近 ES7 中能够直接在类里面定义:class Animal {
name = "rudy";
static value = 11;
sayHi() {
console.log(`hello, ${this.name}`);
}
}
let animal = new Animal();
animal.sayHi(); // hello, rudy
Animal.value; // 11
animal.value; // undefiend
复制代码
extends
关键字实现继承,子类中使用super
关键字来调用父类的构造函数和方法。class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return "this is " + this.name;
}
}
class Cat extends Animal {
constructor(name, value) {
super(name); // 调用父类的 constructor(name)
this.value = value;
}
sayHi() {
return `omg, ${super.sayHi()} it is ${this.value}`;
}
}
let cat = new Cat("Tom", 11);
cat.sayHi(); // omg, this is Tom it is 11;
复制代码
super
这个关键字,既能够当着函数使用,也能够当着对象使用。两种状况下,用法彻底不一样。第一种状况,super
做为函数调用时,表明父类的构造函数。ES6 要求,字类的构造函数必须执行一次super
函数。对象
class Animal {}
class Cat extends Animal {
constructor() {
super();
}
}
复制代码
上面代码中,子类Cat
的构造函数中的super()
,表明调用父类的构造函数,这是必须的,否在 JavaScript 引擎会报错。 注意,super
虽然表明了父类Animal
的构造函数,可是返回的是字类Cat
的实例,既super
内部的this
指的是Cat
的实例,所以super()
在这里至关于 Animal.prototype.constructor.call(this)
。
class Animal {
constructor() {
console.log(new.target.name); // new.target指向当前正在执行的函数
}
}
class Cat extends Animal {
constructor() {
super();
}
}
new Animal(); // Animal;
new Cat(); // Cat;
复制代码
能够看出,在super()
执行时,它指向的是子类Cat
的构造函数,而不是父类Animal
的构造函数,也就是说super
内部的this
指向是Cat
。
做为函数时,super()
只能用在子类的构造函数之中,用在其余地方就会报错。
class Animal {}
class Cat extends Animal {
hi() {
super(); // Uncaught SyntaxError: 'super' keyword unexpected here
}
}
复制代码
第二种状况,super
做为对象时:
- 在普通方法中,指向父类的原型对象,
- 在静态方法中,指向父类。
class Animal {
getName() {
return "rudy";
}
}
class Cat extends Animal {
constructor() {
super();
console.log(super.getName());
}
}
let cat = new Cat(); // rudy;
复制代码
上面代码中,子类Cat
中的super.getName()
,就是将super
看成一个对象使用,这时,super
在普通方法中,指向的是Animal.prototype
,super.getName()
至关于Animal.prototype.getName()
。
这里须要注意,因为super
指向的是父类原型对象,因此定义在父类实例上的方法和属性,是没法经过super
获取到的。
class Animal {
constructor() {
this.name = "rudy";
}
}
class Cat extends Animal {
constructor() {
super();
}
getName() {
return super.name;
}
}
let cat = new Cat();
cat.getName(); // undefined;
复制代码
上面代码中,name
是父类实例的属性,而不是父类原型对象的属性,因此super.name
引用不到它。
用在静态方法中,super
将指向父类,而不是父类的原型对象。
class Animal {
static getName(name) {
console.log("static", name);
}
getName(name) {
console.log("instance", name);
}
}
class Cat extends Animal {
constructor() {
super();
}
static getName(name) {
super.getName(name);
}
getName(name) {
super.getName(name);
}
}
Cat.getName("rudy"); // static rudy;
let cat = new Cat();
cat.getName("tom"); // instance tom;
复制代码
在上面代码中,super
在静态方法中指向父类,在普通方法中指向父类的原型对象。
另外,在字类的静态方法中经过super
调用父类的方法时,方法内部的this
指向当前的子类,而不是子类实例。
class Animal {
constructor() {
this.name = "rudy";
}
static print() {
console.log(this.name);
}
}
class Cat extends Animal {
constructor() {
super();
this.name = 2;
}
static print() {
super.print();
}
}
Cat.name = "Tom";
Cat.print(); // Tom;
复制代码