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的两种继承方法理解代码