ES6初学者,通过阅读本文可对ES6知识点有个大体的概念,强烈建议阅读阮一峰大大的ES6入门。
老司机可以直接绕路。
上一节主要是总结了Symbol、Set和Map的有关内容。本节要总结ES6中的类、类的继承的有关内容。
ES5中没有类的概念,只能通过构造函数来初始化对象实例。ES6中可以通过class关键字来定义类。
function Person(name){ this.name = name; } Person.prototype.hello = function(){ console.log('Hi',this.name) } var person1 = new Person('Peter'); person1.hello(); // Hi Peter console.log(person1 instanceof Person) // true console.log(person1 instanceof Object) // true
说明:
先创建一个构造函数Person,然后定义一个方法hello,并赋值给这个构造函数的原型。这样构造函数的所有实例都将共享这个方法。再通过new操作符得到一个实例person1,该实例可以调用hello方法。并且通过instanceof可以看出:该实例person1是构造函数Person的实例、也是对象Object的实例。
通过class关键字声明一个类,上述代码的ES6等价版本:
class Person{ constructor(name){ this.name = name } hello(){ console.log('Hi',this.name) } } person1 = new Person('Peter'); person1.hello(); // Hi Peter console.log(person1 instanceof Person) // true console.log(person1 instanceof Object) // true
说明:
在定义Person类中,先定义constructor方法,这个方法等价于定义Person构造函数。然后在类中直接定义hello方法,这等价于往构造函数的原型上添加方法,即hello方法是添加到Person.prototype属性上。
class Person { // 等价于定义Person构造函数 constructor(name) { this.name = name } // 等价于Person.prototype.hello hello() { console.log('Hi', this.name) } } person1 = new Person('Peter'); person1.hello(); // Hi Peter
(1)ES6中的类是语法糖,本质还是函数,一个具有构造函数方法行为的函数;
例如上述例子中:
console.log(typeof Person) // function
(2)类中通过constructor方法来定义构造函数,在用new关键字初始化实例时自动执行。并且,一个类必须显示定义constructor方法,如果没有,则会默认添加一个空的constructor方法。
class Person{ // 没有定义constructor方法 } // 等价于 class Person{ constructor(){} }
(3)类声明不能被提升,就像let声明不能被提升;
// Uncaught ReferenceError: Person is not defined let person1 = new Person('Peter'); class Person { constructor(name) { this.name = name; } hello() { console.log('Hi', this.name) } }
(4)类中定义方法时,不需要function关键字,写法是:方法名(){xxx}这种形式;并且各方法之间不要逗号分隔;
(5)类的名称只在类中为常量,因此在定义类的方法中是不能修改类的名称的,不过可以在声明类之后修改;
内部修改类名会报错:
class Person{ constructor(){ Person = "OterPerson" } } let person2 = new Person(); // Uncaught TypeError: Assignment to constant variable.
(6)类中的所有方法都是添加到类的原型上,即类的prototype属性上,并且都是不可枚举的,可以通过Object.keys()查看;
验证:
class Person { constructor(name) { this.name = name } hello() { console.log('Hi', this.name) } } console.log(Person.prototype); console.log(Object.keys(Person.prototype)); // [] console.log(Object.getOwnPropertyNames(Person.prototype)); // ["constructor", "hello"]
补充:
Object.keys(obj)
返回给定对象obj的所有可枚举属性的字符串数组
Object.getOwnPropertyNames(obj)
返回给定对象obj的所有属性的字符串数组
(7)调用类的构造函数,要通过new调用,不用new则会报错
验证:
class Person { constructor(name) { this.name = name; } hello() { console.log('Hi', this.name) } } let person1 = Person('Peter'); // Uncaught TypeError: Class constructor Person cannot be invoked without 'new'
(8)类的name属性,就是class关键字后面的标识符。
class Person { constructor(name) { this.name = name; } hello() { console.log('Hi', this.name) } } console.log(Person.name) // Person
(9)new.target
在类的构造函数中使用new.target,一般情况下,new.target等于类的构造函数
验证:
class Person { constructor(name) { this.name = name; console.log(new.target === Person) } hello() { console.log('Hi', this.name) } } let person1 = new Person('Peter') // true
引用:
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
function Parent(value) { this.value = value; } Parent.prototype.printValue = function () { return this.value } function Child(value) { Parent.call(this, value); } Child.prototype = Object.create(Parent.prototype, { constructor: { value: Child, enumerable: false, writable: true, configurable: true } }); var child1 = new Child('Peter'); console.log(child1) console.log(child1.printValue()); // Peter console.log(child1 instanceof Child); // true console.log(child1 instanceof Parent); // true
说明:
要Child继承Parent,就用一个从Parent.prototype创建的新对象重写Child的原型child.prototype,并且调用Parent.call()方法,child1:
使用extends关键字实现继承,通过调用super()可以访问基类的构造函数
上述代码的ES6等价版本:
class Parent { constructor(value) { this.value = value; } printValue() { return this.value } } class Child extends Parent { constructor(value) { // 等价于 Parent.call(this,value) super(value) this.value = value } } let child1 = new Child('Peter'); console.log(child1.printValue()); // Peter console.log(child1 instanceof Child) // true console.log(child1 instanceof Child) // true
(1)使用extends关键字即可实现继承,更符合面向对象的编程语言的写法
(2)关于super():
--只可以在子类的构造函数中使用super,其他地方使用会报错;
验证,在非子类中使用super(),会报错:
// Uncaught SyntaxError: 'super' keyword unexpected here class Parent { constructor(value) { super(value); this.value = value; } printValue() { return this.value } }
--在子类中指定了构造函数则必须调用super(),否则会报错。因为super负责初始化this,如果在调用super()前使用this,则会报错;
验证:
// Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor class Parent { constructor(value) { this.value = value; } printValue() { return this.value } } class Child extends Parent { constructor(value) { this.value = value } } let child1 = new Child('Peter');
--如果子类中没定义构造函数,则创建子类实例时,会自动调用super(),并且传入所有参数。
class Child extends Parent { // 没有构造函数 } // 等价于 class Child extends Parent { constructor(...args) { super(...args) } }
(3)子类也会继承父类的静态方法(static)
(4)关于new.target
如果是子类继承了父类,则父类中的new.target是子类的构造函数:
验证:
// 定义父类 class Rectangle{ constructor(length,width){ console.log(new.target === Rectangle) // false console.log(new.target === Square) // true this.length = length; this.width = width; } } // 定义子类 class Square extends Rectangle{ constructor(length){ super(length,length); } } let square1 = new Square(5);
本文主要通过对比ES5的实现方法,分别总结了ES6中类和类的继承的有关基本知识。如有问题,欢迎指正。
原创声明,本文系作者授权云+社区发表,未经许可,不得转载。
如有侵权,请联系 yunjia_community@tencent.com 删除。
我来说两句