在之前的文章中我们讲过原型,原型链和原型链继承的文章,在 ES6 中为我们提供了更为方便的 class,我们先来看一下下面的例子:
1 function Person(name) { 2 //构造函数里面的方法和属性 3 this._name = name; 4 this.getName = function () { 5 console.log(this._name); 6 }; 7 this.setName = function (name) { 8 this._name = name; 9 }; 10 } 11 12 var p = new Person("zhangsan"); 13 p.getName(); // zhangsan 14 p.setName("lisi"); 15 p.getName(); // lisi
在上面的代码中我们利用构造函数做了一个可以获取姓名和修改姓名的操作。在 ES6 中我们可以这样写:
1 class Person { 2 //类的构造函数,实例化的时候执行,new的时候执行 3 constructor(name) { 4 this._name = name; 5 } 6 getName() { 7 console.log(this._name); 8 } 9 setName(name) { 10 this._name = name 11 } 12 } 13 14 var p = new Person('zhangsan'); 15 16 p.getName(); // zhangsan 17 p.setName('lisi'); 18 p.getName(); // lisi
在 ES6 中,我们可以定义一个 class 类,在勒种需要写一个 constructor 的构造函数,然后就可以像之前那样进行代码编写,这么写看起来似乎并没有省多少事,我们再来看下面的代码:
1 function Person(name) { 2 //构造函数里面的方法和属性 3 this._name = name; 4 this.getName = function () { 5 console.log(this._name); 6 } 7 } 8 9 //原型链上面的属性和方法可以被多个实例共享 10 Person.prototype._age = 30; 11 Person.prototype.getInfo = function () { 12 console.log(this._name, this._age); 13 }; 14 15 function Foo(name, age) { 16 //对象冒充实现继承 17 Person.call(this, name, age); 18 } 19 20 Foo.prototype = new Person(); 21 22 //实例方法是通过实例化来调用的,静态是通过类名直接调用 23 var p = new Foo('zhangsan', 20); 24 p.getName(); // zhangsan 25 p.getInfo(); // zhangsan 30 26 27 //静态属性和方法 28 Person.instance = "静态属性"; 29 Person.staticFunction = function () { 30 console.log('静态方法'); 31 }; 32 //执行静态属性和方法 33 console.log(Person.instance); // 静态属性 34 Person.staticFunction(); // 静态方法
我们在构造函数 Person 中定义 _name 属性和 getName() 的方法,同时在 Person 的原型链上增加了 _age 的属性和 getInfo() 的方法。我们这时定义一个 Foo 的方法,利用 call 方法进行对象冒充实现继承,如果我们注释掉第 20 行的代码,在运行到第 25 行时程序会报错 TypeError: p.setName is not a function,这时由于对象冒充继承无法继承原型链上的属性和方法,即 Person.prototype 上的属性和方法。如果我们注释掉第 17 行,则第 24 行输出 undefined,第 25 行输出 undefined 30,这时由于原型链继承可以继承构造函数里面的属性和方法,但是实例化子类的时候没法给父类传参。所以我们的 Foo 方法如果既想继承父类属性和方法,又想继承父类原型链上的属性和方法,就得使用原型链继承和对象冒充继承混合的方式。
另外我们可以直接在 Person 的构造函数上加静态属性和方法,即第 27 行以后。
接下来我们看一下 ES6 为我们提供的 class 类方法如何实现
1 class Person { 2 constructor(name) { 3 this._name = name; 4 } 5 getName() { 6 console.log(this._name); 7 } 8 getInfo() { 9 console.log(this._name, this._age); 10 } 11 } 12 13 class Foo extends Person { 14 constructor(name, age) { 15 //实例化子类的时候把子类的数据传给父类 16 super(name); 17 this._age = age; 18 } 19 getInfo() { 20 console.log("重写getInfo-->", this._name, this._age); 21 } 22 static staticFunction() { 23 console.log("Foo 的静态方法") 24 } 25 } 26 27 var p = new Foo('zhangsan', 30); 28 p.getName(); // zhangsan 29 p.getInfo(); // 重写getInfo--> zhangsan 30 30 31 32 Foo.staticFunction(); //Foo 的静态方法 33 34 //静态属性和方法 35 Person.instance = "静态属性"; 36 Person.staticFunction = function () { 37 console.log('Person 的静态方法'); 38 }; 39 //执行静态属性和方法 40 console.log(Person.instance); // 静态属性 41 Person.staticFunction(); // Person 的静态方法
在 ES6 中,我们可以通过 extends 关键字来使子类 Foo 继承父类 Person 的属性和方法,属性需要在 constructor 构造函数内通过 super 关键字来继承,同时我们也可以重写父类的方法 getInfo,这样看起来我们的代码更加工整简介。
同时我们既可以通过像 ES5 那样向 class 类中添加静态属性和方法,也可以通过关键字 static 向 class 类中添加静态属性和方法。
以上就是 ES6 中 class 类的基本使用方法,接下来我们看一下通过 class 类实现的单例模式:
class DB { constructor() { console.log('实例化会触发构造函数'); this.connect(); } connect() { console.log('连接数据库'); } find() { console.log('查询数据库'); } } var db1 = new DB(); db1.find(); // 实例化会触发构造函数 // 连接数据库 // 查询数据库 var db2 = new DB(); db2.find(); // 实例化会触发构造函数 // 连接数据库 // 查询数据库
如上代码,我们定义了一个 Db 的 class 类来模拟后台数据库的连接,按照上面的代码运行的话,我们在需要跟后台建立连接的地方,都需要创建出一个实例 DB,这样就出现一个问题,我们每次连接数据库都是需要耗费时间的,这显然不是很友好的,如果我们把代码改成下面这样:
1 class DB { 2 static getInstance() { /*单例*/ 3 if (!DB.instance) { 4 DB.instance = new DB(); 5 } 6 return DB.instance; 7 } 8 constructor() { 9 console.log('实例化会触发构造函数'); 10 this.connect(); 11 } 12 connect() { 13 console.log('连接数据库'); 14 } 15 find() { 16 console.log('查询数据库'); 17 } 18 } 19 20 var db1 = DB.getInstance(); 21 db1.find(); 22 // 实例化会触发构造函数 23 // 连接数据库 24 // 查询数据库 25 var db2 = DB.getInstance(); 26 db2.find(); 27 // 查询数据库
我们在 class 类中加入了一个静态方法 getInstance,在该方法里先判断是否存在 instance 的静态属性,如果没有的话就给 instance 静态属性天际一个 new DB() 的实例,然后 return DB.instance,这样我们在实例化 DB 类的时候不直接实例化,而是调用 DB 的 getInstance 静态方法,这样在第一次调用的时候发现 DB 中没有 instance 这个静态属性,进入 if 判断中实例化 DB,然后走 constructor 的构造函数,就建立了数据库的连接,当我们再次调用 DB.getInstance 静态方法的时候,DB 类里已经有了 instance 的静态属性,所以 if 判断语句不执行,不再走 constructor 构造函数,直接对数据库进行操作就可以了,这种方式我我们就叫做单例模式。