在ES5中,我們經(jīng)常使用方法或者對(duì)象去模擬類的使用,并基于原型實(shí)現(xiàn)繼承,雖然可以實(shí)現(xiàn)功能,但是代碼并不優(yōu)雅,很多人還是傾向于用 class 來(lái)組織代碼,很多類庫(kù)、框架創(chuàng)造了自己的 API 來(lái)實(shí)現(xiàn) class 的功能。
ES6 時(shí)代終于有了 class (類)語(yǔ)法,能讓我們可以用更簡(jiǎn)明的語(yǔ)法實(shí)現(xiàn)繼承,也使代碼的可讀性變得更高,同時(shí)為以后的JavaScript語(yǔ)言版本添加更多的面向?qū)ο筇卣鞔蛳禄A(chǔ)。
一、 類的定義
- ES5使用函數(shù)模擬類
function Person( name , age ) {
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
return '我叫' + this.name + ',今年' + this.age + '歲';
}
var p = new Person('xiaoming',33); // Person {name: "xiaoming", age: 33}
p.say();
使用ES5語(yǔ)法定義了一個(gè)Person類,該類有name age food三個(gè)屬性和一個(gè)原型eat方法。
2.ES class定義類
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
return '我叫' + this.name + ',今年' + this.age + '歲';
}
}
var stu = new Person("xiaoming",22);
p.say();
上面代碼定義了一個(gè)同樣的Person類,constructor方法就是構(gòu)造方法,而this關(guān)鍵字則代表實(shí)例對(duì)象;
注:雖然ES6引入了class關(guān)鍵字,但并沒(méi)有真的引入類這個(gè)概念,通過(guò)class定義的仍然是函數(shù),數(shù)據(jù)類型中也沒(méi)有class類型。
console.log(typeof Person);//還是'function'
所以說(shuō),class僅僅是通過(guò)更簡(jiǎn)單直觀的語(yǔ)法去實(shí)現(xiàn)原型鏈繼承。這種對(duì)語(yǔ)言功能沒(méi)有影響、但是給程序員帶來(lái)方便的新語(yǔ)法,被稱為語(yǔ)法糖。
二、類的傳參
在類的參數(shù)傳遞中,我們?cè)赾onstructor( )進(jìn)行傳參,傳遞參數(shù)后可以直接使用this.xxx進(jìn)行調(diào)用。
class Person{
constructor(x,y){
this.x = x;
this.y = y;
}
add(){
return this.a + this.b;
}
}
let stu = new Person(11,22);
stu.add();//11+22=33
用constructor來(lái)傳遞參數(shù),然后用了一個(gè)add方法,把參數(shù)相加。
三、靜態(tài)方法
- 使用函數(shù)模擬類時(shí),通過(guò)函數(shù)名.靜態(tài)方法名,來(lái)定義靜態(tài)方法:
function Person(name,age){
this.name = name;
this.age = age;
Person.say = function(){
return "I can say!"
}
}
Person.say();//I can say!
var student = new Person("xiaoming",11);
student.say();//報(bào)錯(cuò)
靜態(tài)方法是不需要實(shí)例化,可以通過(guò)類名直接調(diào)用,注意的是靜態(tài)方法不會(huì)繼承到類實(shí)例中,因此靜態(tài)方法經(jīng)常用來(lái)作為工具函數(shù)。比如我們經(jīng)常用的Math.random()。
- 在ES6 class類定義中,可以使用static關(guān)鍵字定義靜態(tài)方法:
class A{
constructor(){};
static say(){
console.log("I can say!");
}
}
A.say();//I can say
var a = new A();
a.say()//error
跟之前一樣,通過(guò)類名直接調(diào)用,不會(huì)繼承到類實(shí)例中。
static關(guān)鍵字是ES6的另一個(gè)語(yǔ)法糖,static 使靜態(tài)方法聲明也成為了一個(gè)一等公民。
四、封裝和繼承
- ES6之前子類對(duì)象是通過(guò)使用原型來(lái)實(shí)現(xiàn)繼承父類,子類函數(shù)和父類函數(shù)共享原型的形式來(lái)實(shí)現(xiàn):
function Person(name,age){
this.name = name;
this.age = age;
}
function father(name,age,firstname,lastname){
Person.call(this,name,age);//通過(guò)call來(lái)繼承父類的方法來(lái)實(shí)現(xiàn)自己的功能
this.lastname = lastname;
this.firstname = firstname;
}
console.log(new father('zhangsan',11,'zhang','san'));
//father {name: "zhangsan", age: 11, lastname: "san", firstname: "zhang"}
function son(name,age,firstname,lastname){
father.call(this,name,age,firstname,lastname);
};
son.prototype = Object.create(father.prototype);
son.constructor = son;
console.log(new son('zhangsi',5,'zhang','si'));
//son {name: "zhangsi", age: 5, lastname: "si", firstname: "zhang"}
- ES6中新的extends關(guān)鍵字解決了這個(gè)麻煩問(wèn)題:
語(yǔ)法:class son extends father{}
下面定義了一個(gè)Child類,該類通過(guò)extends關(guān)鍵字,繼承了Parent類的所有屬性和方法。
class Parent{
constructor(firstname,lastname){
this.firstname = firstname;
this.lastname = lastname;
}
}
class Child extends Parent{
constructor (firstname,lastname,name){
super(firstname,lastname);
this.name = name;
}
say(){
console.log("I can say!")
}
}
console.log(new Child("蒙奇","D","路飛"));
//Child {firstname: "蒙奇", lastname: "D", name: "路飛"}
- super關(guān)鍵字
super表示超,超類,父類。既可以當(dāng)作函數(shù),也可以當(dāng)作對(duì)象使用。
(1)super作為函數(shù)調(diào)用,代表父類的構(gòu)造函數(shù)。
注意,ES6中規(guī)定,子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù)。格式:
class Parent{}
class Child extends Parent{
constructor (){
super();
// ...
}
// ...
}
class Parent{}
class Child extends Parent{
constructor (){
// super(); 如果調(diào)用super,程序會(huì)報(bào)錯(cuò)Uncaught ReferenceError: this is not defined
// ...
}
// ...
}
new Child();
注意,super雖然代表父類中的構(gòu)造函數(shù),但是返回的是子類的實(shí)例,即super內(nèi)部的this指的是子類Child,在這種情況下,super()相當(dāng)于Parent.prototype.constructor.call(this)。
(2)super作為對(duì)象,代表父類的原型對(duì)象。
class Parent{
constructor(firstname,lastname,age){
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
}
say(){
console.log("I can say.");
}
}
class Child extends Parent{
constructor (firstname,lastname,age,score){
super(firstname,lastname,age); //super在這里代表父類的構(gòu)造函數(shù)
this.score = score;
super.say();//super在這里代表父類的原型對(duì)象,可以調(diào)用父類的方法
}
}
console.log(new Child("蒙奇","路飛",18,100));
// I can say.
// {firstname: "蒙奇", lastname: "路飛", age: 18, score: 100}
使用extends關(guān)鍵字實(shí)現(xiàn)繼承,那么子類中可以通過(guò)super關(guān)鍵字調(diào)用父類的東西
(3)4.3 getter(取值)、 setter(存值)
與 ES5 一樣,在“類”的內(nèi)部可以使用get和set,對(duì)某個(gè)屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為。
class Parent{
constructor(){}
get firstname(){
return "D.";
}
set lastname(value){
console.log('我的名是:' + value);
}
}
let p = new Parent();
console.log(p.firstname);//D.
console.log(p.lastname = "路飛") ;
//我的名是:路飛
//路飛