對象
在javascript中,對象是一組無序的相關屬性和方法的集合,所有的事物都是對象,例如字元串,數值,數組,函數等。
對象是由屬性和方法組成的:
- 屬性:事物的特征,在對象中用屬性來表示(常用名詞)
- 方法:事物的行為,在對象中用方法來表示(常用動詞)
類 class
在ES6中新增了類的概念,可以使用
class
關鍵字來聲明一個類,之後使用這個類來執行個體化對象
類抽象了對象的公共部分,它泛指某一大類
對象特指某一個,通過類執行個體化一個具體的對象
class的基本文法
1.簡介
JavaScript語言中,生成執行個體對象的傳統方法是通過構造函數。
function Point(x,){
this.x = x
this.y = y
}
Point.prototype.toString = function() {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作為對象的模闆。通過
class
關鍵字,可以定義類。
基本上,ES6的
class
可以看作是一個文法糖,它的絕大部分功能,ES5都可以做到,新的
class
寫法隻是讓對象原型的寫法更加清晰,更像面向對象便形成的文法而已。
class Point {
constructor(x,){
this.x = x
this.y = y
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
ES6的類,完全可以看做構造函數的另一種寫法
class Point {
//...
}
typeof Point //'function'
Point === Point.prototype.constructor
上面代碼表明,類的資料類型就是函數,類本身就是指向構造函數
使用的時候也是直接使用
new
指令,跟構造函數的用法完全一緻。
class Bar {
doStuff() {
console.log('stuff')
}
}
var b = new Bar()
b.doStuff() //stuff
構造函數的
prototype
屬性,在ES6的類上面繼續存在。事實上,類的所有方法都定義在類的
prototype
屬性上面。在類的執行個體上面調用方法,其實就是調用原型上的方法。
clas B {}
let b = new B
b.constructor === B.prototype.constructor
上面代碼中,
b
是
B
類的執行個體,它的
constructor
方法就是
B
類原型的
constructor
方法
由于類的方法都定義在
prototype
對象上面,是以類的新方法可以添加在
prortotype
對象上面。
Object.assign
方法可以很友善地一次性添加多個方法。
class Point{
constructor(){
//...
}
}
Object.assign(Point.prototype,{
toString(){},
toValue(){}
})
prototype
對象的
constructor
屬性,直接指向類的本身,這與ES5的行為是一緻的。
Point.prototype.constructor === Point //true
另外,類的内部所有定義的方法,渎職不可枚舉的(non-enumerable)
class Point{
constructor(x,){
//...
}
toString() {
//...
}
}
Object.keys(Point.prototype)
//[]
Object.getOwnPropertyNames(Point.prototype)
2. constructor方法
constructor
方法是類的預設方法,通過
new
指令生成對象執行個體,自動調用該方法。
一個類必須有
constructor
方法,如果沒有顯示定義,一個空的
constructor
方法會被預設添加。
class Point {
}
//等同于
class Point {
constructor(){
}
}
constructor
方法預設傳回執行個體對象(即
this
),完全可以指定傳回另外一個對象
類必須使用
new
調用,否則會報錯。這是它跟普通函數的一個主要差別,後者不用
new
也可以執行。
3. 類的執行個體
生成類的執行個體的寫法,與ES5完全一緻,也是使用
new
指令,但必須使用
new
調用,否則報錯。
執行個體的屬性除非顯示定義在其本身(即定義在
this
對象上),否則都是定義在原型上(即定義在
class
上)。
//定義類
class Point {
//構造器
constructor(name,){
//執行個體屬性,這裡的this指向Point的執行個體對象
this.name = name
this.age = age
}
//執行個體方法
sayName() {
console.log(this.name)
}
//靜态方法
static say() {
console.log('靜态方法')
}
}
//靜态屬性
Point.staticAttr = '靜态屬性'
//建立執行個體
let point = new Point('zhangsan', 18)
//調用執行個體方法
point.sayName() //'zhangsan'
//通路靜态屬性,靜态方法
console.log(Point.staticAttr)
Point.say()
4. 靜态方法
類相當于執行個體的原型,所有在類中定義的方法,都會被執行個體繼承。如果在一個方法前,加上
static
關鍵字,就表示該方法不會被執行個體繼承,而是直接通過類來調用,這就稱為“靜态方法”。
class Point{
//靜态方法,隻能通過類來調用,執行個體是不會繼承靜态方法的
static say(){
console.log('靜态方法')
}
}
5. 執行個體方法
類的執行個體方法可以直接定義在類中,不用static修飾的方法都會被類的執行個體繼承
class Point{
//執行個體方法
sayName(){
console.log('執行個體方法')
}
}
6. 靜态屬性
直接在類上定義的屬性是類的靜态屬性,注意:并不是寫在class裡面,而是下面這種寫法:
class Point{
}
Point.staticName = 'lisi'
7. 執行個體屬性
類的執行個體屬性可以定義在構造函數中(constructor)
class Point{
constructor(id,name){
this.id = id
this.name = name
}
}
class的繼承
1. 簡介
class可以通過
extends
關鍵字實作繼承,這比ES5的通過修改原型鍊實作繼承,要清晰和友善很多。
class Point {
constructor(x,y){
this.x = x
this.y = y
}
toString(){
}
}
class ColorPoint extends Point {
constructor(x, y,){
super(x, y) //調用父類的constructor(x,y)
this.color = color
}
toString() {
return this.color + ' ' + super.toString() //調用父類的toString()
}
}
上面代碼定義了一個
ColorPoint
類,該類通過
extends
關鍵字,繼承了
Point
類的所有屬性和方法(包括靜态的和執行個體的)
子類必須在
constructor
方法中調用
super
方法,否則建立執行個體會報錯。這是因為子類自己的
this
對象,必須先通過父類的構造函數完成塑造,得到與弗雷同樣的執行個體屬性和方法,然後再對其經行加工,加上子類自己的執行個體屬性和方法。如果不調用
super
方法,子類就得不到
this
對象。
2. Object.getPrototypeOf()
Object.getPrototypeOf
方法可以用來從子類上擷取父類
Object.getPrototypeOf(ColorPoint) ===
是以,可以用這個方法判斷一個類是否繼承了另一個類。
3. super關鍵字
super
這個關鍵字,既可以當做函數調用,也可以當做對象使用。在這兩種情況下,它的用法完全不同。
- 第一種情況,
作為函數調用時,代表父類的構造函數。ES6要求,子類的構造函數必須執行一次super
函數。super
class A {}
class B extends A {
constructor() {
super()
}
}
上面代碼中,子類
B
的構造函數之中的
super()
,代表調用父類的構造函數。這是必須的,否則報錯。
注意:
super
雖然代表了父類
A
的構造函數,但是傳回的是子類
B
的執行個體,即
super
内部的
this
指的是
B
的執行個體,是以
super
在這裡相當于
A.prototype.constructor.call(this)
作為函數時,
super()
隻能用在子類的構造函數之中,用在其他地方就會報錯。
- 第二種情況,
作為對象時,在普通方法中,指向父類的原型對象;在靜态方法中,指向父類。super
class A {
p(){
return 2
}
}
class B extends A {
constructor() {
super()
console.log(super.p()) //2 這裡其實是調用了父類A的p方法
}
}
這裡需要注意,由于
super
指向父類的原型對象,是以定義在父類執行個體上的方法或屬性,是無法通過
super
調用的。
class A {
constructor(){
this.p = 2
}
}
class B extends A {
get m() {
return super.p
}
}
let b = new B()
b.m //undefined
4. 類的prototype屬性和__ptoto__屬性
大多數浏覽器的ES5實作之中,每一個對象都有
__proto__
屬性,指向對應的構造函數的
prototype
屬性。class作為構造函數的文法糖,同時有
prototype
屬性和
__proto__
屬性,是以同時存在兩條繼承鍊
class A {}
class B extends A {}
B.__proto__ === A //true
B.prototype.__proto__ === A.prototype //true
class A {}
class B {}
//B的執行個體繼承A的執行個體
Object.setPrototypeOf(B.prototype,A.prototype)
//B 繼承 A 的靜态屬性
Object.setPrototypeOf(B, A)
const b = new B()