好家伙,我们先来复习一下
关于Java,类的三大特征:
1、封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
2、继承,继承性更符合认知规律,使程序更易于理解,同时节省不必要的重复代码。
3、多态,体现为覆盖和重载,Js没有重载,有时子类并不想原封不动地继承父类的方法,而是想作一定的修改。
这时我们可以对方法进行重写,也就是方法覆盖
先来试用一下
function functionPerson(){}
class classPerson{
}
console.log(functionPerson.prototype)
console.log(classPerson.prototype)
(猜猜看跑完什么结果?)
所以我们知道,虽然ECMAScript 6类表面上看起来可以支持正式的面向对象编程,但实际上它背后使用的仍然是原型和构造函数的概念。
(还是原型链那一套)
类可以包含构造函数方法、实例方法、获取函数、设置函数和静态类方法,但这些都不是必需的。
使用new操作符实例化Person的操作等于使用new调用其构造函数。
唯一可感知的不同之处就是,JavaScript解释器知道使用new和类意味着应该使用constructor函数进行实例化。
使用new调用类的构造函数会执行如下操作。
(1)在内存中创建一个新对象。
(2)这个新对象内部的[[Prototype]]指针被赋值为构造函数的prototype属性。
(3)构造函数内部的this被赋值为这个新对象(即this指向新对象)。
(4)执行构造函数内部的代码(给新对象添加属性)。
(5)如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
类定义语法把在类块中定义的方法作为原型方法。
(例子跟下面的放一起了)
可以在类上定义静态方法。这些方法通常用于执行不特定于实例的操作,也不要求存在类的实例。
与原型成员类似,静态成员每个类上只能有一个。
静态类成员在类定义中使用static关键字作为前缀。在静态成员中,this引用类自身。其他所有约定跟原型成员一样:
class Person {
constructor() {
// 添加到this的所有内容都会存在于不同的实例上
this.locate = () => console.log('instance', this);
}
// 定义在类的原型对象上
locate() {
console.log('prototype', this);
}
//定义在类本身上
static locate(){
console.log('class', this);
}
}
let p = new Person();
p.locate(); // instance, Person {}
Person.prototype.locate(); // prototype, {constructor: ... }
Person.locate(); // class, class Person {}
(书中原例,)
类继承使用的是新语法,但背后依旧使用的是原型链。
使用extends关键字,就可以继承任何拥有[[Construct]]和原型的对象。
很大程度上,这意味着不仅可以继承一个类,也可以继承普通的构造函数(保持向后兼容)
class Person {}
function Cat() {}
class Somebody extends Person {}
class Bosicat extends Cat {}
let a = new Somebody();
let b = new Bosicat();
console.log(a instanceof Person);
console.log(b instanceof Cat);
(通过继承function这点看出,Js终于洗清了他的嫌疑,它确实和Java没关系)
派生类的方法可以通过super关键字引用它们的原型。
这个关键字只能在派生类中使用,而且仅限于类构造函数、实例方法和静态方法内部。
在类构造函数中使用super可以调用父类构造函数。
class Cat{
constructor(){
this.age = '2'
}
}
class Bosicat extends Cat{
constructor(){
super();
console.log(this);
}
}
new Bosicat;
在使用super时要注意几个问题。
(1)super只能在派生类构造函数和静态方法中使用。
(2)不能单独引用super关键字,要么用它调用构造函数,要么用它引用静态方法
(3)调用super()会调用父类构造函数,并将返回的实例赋值给this。
(4)super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入。
(5)如果没有定义类构造函数,在实例化派生类时会调用super(),而且会传入所有传给派生类的参数
(6)在类构造函数中,不能在调用super()之前引用this。
(7)如果在派生类中显式定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象。