Javascript之class的前世今生
最早JavaScript中所谓的类,其实是一种设计模式:一个构造函数(consturctor)和一个用于在该类实例间共享属性和方法的原型对象(Objcet.prototype)的结合。为了达到继承,通过函数来模拟类来创建对象。
今天我主要讲讲js类的发展,一是理清class的实质,二是掌握继承extends的实现,三是梳理ES6以前的关于这方面的知识。
ES6 class的前世
(1)创建对象
js引入class的目的,其实就是为了创建对象,这里梳理一下ES6以前创建对象的方式。
一、最基础的new
var friend = new object();
friend .name = "girl";
friend .speak = function(){
console.log(this.name + "_said:I miss you");
}
二、字面向量
var friend = {
name: 'girl',
speak: function(){
console.log(this.name + "_said:I miss you");
}
};
方法一、二虽然可以创建对象,但是不易封装复用
三、工厂模式
function friend(name){
var o = new object();
o.name = name;
o.speak = function(){
console.log(this.name + "_said:I miss you");
}
return o;
}
var somebody = new friend("girl");
somebody.speak();
比起一二,friend函数确实是封装了一个属性和一个方法。然而有个问题,这个函数无法解决对象识别问题,就是创造出来的实例如somebody是Object类型,因为o是从Object里new出来的。
四、构造函数
function friend(name){
this.name = name;
this.speak = fucntion(){
console.log(this.name + "_said:I miss you");
}
}
var somebody = new friend("girl");
somebody.speak();
虽然这个方法解决了三的问题,但还是有缺陷。每次创建friend对象,每个对象都会有一个speak方法,消耗很大。
五、原型封装
function friend(name){
this.name = name;
}
friend.prototype.speak = function(){
console.log(this.name + "_said:I miss you");
}
var somebody = new friend("girl");
somebody.speak();
和构造函数不同的,这里通过原型封装的新对象的方法是所有实例都可以共享的。
(2)继承
现在已经能完成创建对象的要求了,但是类还有继承的要求。
六、基于原型链的继承
基于原型链的特点,通过将子类构造函数的原型作为父类构造函数的实例,这样就连通了子类-子类原型-父类原型,原型链的特点就是逐层查找,从子类开始一直往上直到所有对象的原型Object.prototype,找到属性方法之后就会停止查找,所以下层的属性方法会覆盖上层。
方法一:
function friend(name){
this.name = name;
}
function girlFriend(name){}
girlFriend.prototype = new friend(name);
girlFriend.prototype.constructor = girlFriend;
var a = new girlFriend("L");
console.log(a.name); //L
1.把子类girlFriend的原型对象指向父类的实例化对象,这样即可以继承父类friend原型对象上的属性和方法
2.这时子类的constructor属性会指向friend,手动把constructor属性指向子类girlFriend,就可以在父类的基础上添加属性和方法了
方法二:
function friend(name){
this.name = name;
}
function girlFriend(name){
friend.call(this,name);
}
var a = new girlFriend("L");
console.log(a.name); //L
ES6 class的今生
class friend{
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + "_said:I miss you");
}
}
class girlFriend extends friend {
constructor(name) {
super(name); //如果子类中存在构造函数,则需要在使用“this”之前首先调用super()
this.name = name;
}
speak() {
super.speak();
}
}
1.子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
2.直接调用super(name),就可以直接继承父类的属性和方法
可对比ES5的两种继承方法理解代码