JavaScript中的Class
- 類的基本語法
- 類的繼承
- 修飾器
1. 類的基本語法
可以看成ES5中構造函數的語法糖,它的大部分功能ES5都可以做到
1.1 定義一個類
ES5
function Student(name,age){
this.name = name
this.age = age
}
Student.prototype = {
constructor:Student,
showName(){
return this.name
}
}
Class
class Student {
constructor(name, age) {
this.name = name
this.age = age
}
showName() {
return this.name
}
}
console.log(Student.prototype);//{constuctor showName}
class的方法constructor與showName都是定義在原型上的,每個方法之間不用逗號隔開
1.2 constructor方法
class默認的方法,沒有定義就默認加一個空的方法,在用new生成實例時自動調用該方法
class Student {
constructor(name, age) {
this.name = name
this.age = age
}
showName() {
return this.name
}
}
const std1 = new Student("張三")
console.log(std1);//{name:"張三",age:undefined}
與ES5不同的是,定義類不存在變量提升,就是實例化一定要在定義之後
1.3 私有方法和私有屬性
只在類的內部調用和使用的方法與屬性
私有方法
const private1 = Symbol('private1')
const prop1 = Symbol('prop1')
class Studnet {
//公有方法
foo(parm1, parm2) {
//內部使用私有方法
this[private1](parm1)
private2.call(this, parm2)
}
//私有方法
[private1](parm) {
return this[prop1] = parm
}
}
使用Symbol或者外部的構造函數的方式,可以實現方法內部的私有,實例化的對象不能訪問到這些方法
私有屬性
class ClassA {
constructor(prop1, prop2) {
const prop = Symbol('prop')
this[prop] = prop1
this.age = prop2
}
}
let obj = new ClassA(1)
console.log(obj[prop]); // ReferenceError
console.log(obj[(Object.getOwnPropertySymbols(obj)[0])]);// 1
使用Symbol可以實現一定意義上的私有屬性,但是使用Object.getOwnPropertySymbols()方法仍然可以訪問到這些屬性
class Studnet {
#a
constructor(a) {
this.#a = a
this.a = a
}
get() {
return this.#a
}
}
let student = new Student()
console.log(Object.getOwnPropertyNames(student)) // ['a'] 並不能訪問到屬性#a
用#來標識私有屬性,在外部不能直接訪問這個屬性
1.4 靜態方法和靜態屬性
不能被實例繼承的方法與屬性
靜態方法
class Foo {
static fn1() {
this.fn2()
}
static fn2() {
console.log("Hello");
}
fn2() {
console.log("World");
}
}
Foo.fn1() //"Hello"
let foo = new Foo()
foo.fn2() //"World"
靜態方法可以和實例方法重名,靜態方法只能在類上調用
class Foo1 extends Foo {
}
Foo1.fn2()// "Hello"
子類可以繼承父類的靜態方法
靜態屬性
與靜態方法相同,在實例屬性前加static即可
class Foo {
static _name = "a"
_name = "b"
}
console.log(Foo._name);//"a"
let foo = new Foo()
console.log(foo._name);//"b"
靜態方法可以與實例方法重名,實例不能繼承到靜態方法,它屬於構造器,不屬於原型對象
同樣靜態方法可以被子類繼承
1.5 new.target屬性
在類中指向該類,父類被繼承時指向子類
class Foo {
constructor() {
console.log(new.target);
}
}
class Foo1 extends Foo { }
let foo = new Foo() //Foo
let foo1 = new Foo1()//Foo1
-
用該方法可以控制一些類必須在繼承後才能使用
class Foo { constructor() { if (new.target === Foo) { throw new Error("需要先繼承") } else { console.log(new.target); } } } class Foo1 extends Foo { } let foo = new Foo() //Error 需要先繼承 let foo1 = new Foo1() //Foo1
2 Class的繼承
使用extends關鍵字將父類中的屬性與方法拷貝到子類
2.1 super關鍵字
- 類繼承的實質是先創建父類的實例對象this,再用子類的構造函數修改this
-
super就為子類提供了父類的this對象,相當於Parent.prototype.constructor.call(this),執行父類構造函數並修改this
class Parent { constructor(x, y) { this.x = x this.y = y } getX() { console.log(this.x); } } class Child extends Parent { constructor(x, y, z) { super(x, y) this.z = z } getY() { console.log(this.y); } } let obj1 = new Child(1, 2, 3) console.log(obj1);//{x:1,y:2,z:3} obj1.getX()//1 obj1.getY()//2 -
super指向父類的原型,可以作為一個對象去調用原型上的方法,並修改this指向子類
class Parent { constructor(x) { this.x = x this.y = "a" } getY() { console.log(this.y); } } class Child extends Parent { constructor(x, y) { super(x) this.y = y } runner() { super.getY() } } let obj1 = new Child(1, 2) obj1.runner()//2super.getY()相當於Parent.prototype.getY.call(this)
如果Child上沒有y屬性,則返回父類的y值為"a"
如果Child上有y屬性,但實例化的時候沒有傳值,則返回undefined,不會去原型鏈上找
2.2 原型鏈
class A { }
class B extends A { }
const a1 = new A()
const b1 = new B()
//子類的__proto__指向父類
B.__proto__ === A//true
//父類的原型對象是子類原型對象的原型
B.prototype.__proto__ === A.prototype//true
//父類實例的原型是子類實例的原型的原型就
b1.__proto__.__proto__ === a1.__proto__
關鍵還是在於第二個示例,父類的原型對象是子類原型對象的原型
因為類的原型對象就是實例對象的__proto__指向的對象,可以推出第三個示例
2.3混入(Minxin)
實現繼承多個類,先將多個類混入到一個類中,再繼承
Minxin的實現
//混入
function mix(...mixins) {
class Mix { }
for (let mixin of mixins) {
copyProperties(Max, mixin)
copyProperties(Max.prototype, mixin.prototype)
}
return Mix
}
//拷貝屬性
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor"
&& ket !== "prototype"
&& key !== "name"
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc)
}
}
}
ES6標準 阮一峯
如果只是方法的追加則使用Object.assign(target,source)即可
3 修飾器(Decorator)
修飾器可以用來修飾類的行為
修飾器還在提案中,但是babel轉碼器已經支持修飾器
可以安裝babel的模塊去編譯 使用了修飾器的寫法,轉碼成向下兼容的ES5寫法,這樣就可以得到想要的結果
-
這裏僅舉一個類修飾器的例子
@addStatic class A{} function addStatic(target){ target.hello = "Hello" }target就是類A,@ + 修飾函數 相當於給類A添加一個靜態屬性hello
屬性修飾器以及其他內容不再舉例