Class
在JavaScript中,是通過構造函數來生成實例對象。
function A(a){
this.a=a
}
A.prototype.sayA=function(){
console.log(this.a)
}
var a=new A('a')
a.sayA()
在ES6中引入了class(類)的概念,通過class
關鍵字可以定義類。這種寫法使得對象原型的寫法更加清晰、更像面對對象編程的語法了。
class A{
constructor(a){
this.a=a
}
sayA(){
console.log(this.a)
}
}
var a=new A('a')
a.sayA()
在定義類的方法時,不需要加上function關鍵字,使用的時候也是直接對類進行new
命令,跟構造函數是一樣的。
構造函數的prototype
屬性,在 ES6 的類中繼續存在,類的所有方法都定義在類的prototype
屬性上面。
class A{
constructor(){
}
sayA(){}
sayB(){}
}
相當於
A.prototype={
sayA(){},
sayB(){}
}
因此在類的實例中調用方法,其實調用的是原型上的方法。而且,類的內部定義的所有方法都是不可枚舉的,而es5中的是可枚舉的。
class A{
constructor(){
}
sayA(){}
sayB(){}
}
Object.keys(A.prototype) //[]
function A(){}
A.prototype.sayA=function(){}
A.prototype.sayB=function(){}
Object.keys(A.prototype) //['sayA','sayB]
constructor
constructor
方法是類的默認方法,通過new
命令生成對象實例時,自動調用該方法。一個類必須有constructor
方法,如果沒有顯式定義,一個空的constructor
方法會被默認添加。
class A(){}
//等同
class A(){
constructor(){}
}
類的實例
實例的屬性除非顯式定義在其本身(即定義在this
對象上),否則都是定義在原型上(即定義在class
上)。
class A{
constructor(a){
this.a=a
}
sayA(){
console.log(this.a)
}
}
var a=new A('a')
console.log(a.hasOwnProperty('a')) //true
console.log(a.hasOwnProperty('sayA')) //false
console.log(a.__proto__.hasOwnProperty('sayA')) //true
類的所有實例共享一個原型對象。
var a=new A('a')
var b=new A('b')
console.log(a.__proto__===b.__proto__) //true
可以通過__proto__
屬性對類添加方法。
getter、setter
在類的內部可以使用get
和set
關鍵字,對某個屬性設置存取函數,攔截屬性的存取行爲。
class A{
constructor(a){
this.a=a
}
get value(){
return this.a
}
set value(val){
this.a=val
}
}
var a=new A()
a.value=3
console.log(a.value) //3
屬性表達式
類的屬性名,可以通過表達式設置
let methodsName1='sayA'
class A{
constructor(){}
[methodsName1](){}
}
class表達式
與函數一樣,類也可以用表達式的形式進行定義。
const Aclass= class A{
sayA(){
return A.value
}
}
這個類的名字是A
,A
只在 Class 的內部可用,指代當前類。在 Class 外部,這個類只能用AClass
引用。如果類的內部沒有用到的話,可以直接寫成
const Aclass=class{}
採用class表達式的寫法,可以寫出立即執行的class
const Aclass=new class A{
constructor(value){
this.value=value
}
sayA(){
console.log(this.value)
}
}('a')
Aclass.sayA() //a
靜態方法
在方法前面加上static
關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱爲“靜態方法”。
class A{
static aMethod(){
console.log('hi')
}
}
A.aMethod() //hi
var a=new A()
a.aMethod() //TypeError: a.aMethod is not a function
如果靜態方法包含this
關鍵字,這個this
指的是類,而不是實例。靜態方法可以與非靜態方法重名。
class A{
static aMethod1(){
this.aMethod2() //this指向了類A,而不是實例
}
static aMethod2(){
console.log('hi2')
}
aMethod1(){ //與靜態方法重名了
console.log('sup?')
}
}
A.aMethod1() //hi2
var a=new A()
a.aMethod1() //sup?
父類的靜態方法,可以被子類繼承。
class A{
static aMethod(){
console.log('hi')
}
}
class AA extends A{
}
AA.aMethod() //hi
靜態方法可以從super
對象上調用的。
class A{
static aMethod(){
return `hi`
}
}
class AA extends A{
static aMethod(){
console.log(`${super.aMethod()} , sup?`)
}
}
AA.aMethod() // hi , sup?
私有屬性
私有屬性只能在類的內部調用,在類的外部調用就會報錯。引入一個新的前綴#
表示私有屬性。
class A{
#val='value',
sayValue(){
console.log(this.#val)
}
}
var a=new A()
console.log(a.#val) //Private field '#val' must be declared in an enclosing class
console.log(a.sayValue()) //value
注意點
1、不存在變量提升
new A(); // ReferenceError
class A {}
2、es6默認就是嚴格模式
3、generator方法
在某個方法之前加上星號(*
),就表示該方法是一個 Generator 函數。
class A {
constructor(...args) {
this.val = args;
}
* [Symbol.iterator]() {
for (let arg of this.val) {
yield arg;
}
}
}
for (let x of new A('a', 'b')) {
console.log(x);
}
// a
// b
Symbol.iterator
方法返回類的默認遍歷器,for...of
循環會自動調用這個遍歷器。
4、this指向
類的方法內部如果含有this
,它默認指向類的實例,但是類的內部採用的是嚴格模式,很容易報錯。
class A{
sayA(a='a'){
this.log(a)
}
log(val){
console.log(val)
}
}
const a=new A()
const {sayA}=a;
sayA() //Uncaught TypeError: Cannot read property 'log' of undefined
將方法提取出來單獨使用,this
會指向該方法運行時所在的環境(由於 class 內部是嚴格模式,所以 this 實際指向的是undefined
),從而導致找不到方法而報錯。
一個比較簡單的解決方法是,在構造方法中綁定this
,就不會找不到方法了。
class A{
constructor(){
this.sayA=this.sayA.bind(this) //對方法進行綁定
//this.sayA=()=>this //通過箭頭函數也可以實現。
}
sayA(a='a'){
this.log(a)
}
log(val){
console.log(val)
}
}
const a=new A()
const {sayA}=a;
sayA()