1. 程式人生 > >Javascript ES6 Class繼承的原理

Javascript ES6 Class繼承的原理

1. 前言

ES6新增的Class(類)給我們程式設計帶來了極大方便,有人說這實際上是函式的語法糖,我是贊同的,但詞法作用域和原型鏈是Javascript的核心思想,Class是基於原型鏈實現的,函式也是基於原型鏈實現的,這兩者必然有一些共性,甚至使用 typeof 類識別符號考察類到底是一個什麼東西?控制檯會告訴你這是一個function。然而,Class作為一個新特性,它有函式該有的東西,它也有函式沒有的東西,這是它強大的地方。

2. Class本質上是一個特別的函式

    class Polygon {
        constructor(height, width) {
            this.name = 'Polygon';
            this.height = height;
            this.width = width;
        }
        sayName() {
            console.log('Hi, I am a ', this.name + '.');
        }
    }

    class Square extends Polygon {
        constructor(length) {
            super(length, length);
            this.name = 'Square';
        }

        get area() {
            return this.height * this.width;
        }

        set area(value) {
            this.area = value;
        }
    }
    var mySquare = new Square(4,4);

typeof Polygon //function   控制檯輸入typeof Polygon, 它會告訴你這是一個函式

console.dir(Polygon)  //反射檢視Polygon類的屬性

3. Class比函式多一根原型鏈

我們說函式supF是函式subF的父類,是因為subF.__protol__ = sup.prototype,即子函式的原型物件sub.prototype被連結到父函式supF的原型物件supF.prototype。但是兩個父子函式supF和subF本身是獨立的,沒有任何關係,它們是靠原型物件關聯到了一起。Class恰恰把這根連結上了。即子類自己的__protol__指向了父類。簡單理解:extend關鍵字將類和類原型連結到了兩根不同的鏈。

4. super偽引用

我們知道this繫結是動態繫結,super也是動態繫結的嗎?super一定指向物件原型鏈的上級嗎?類有兩根原型鏈,那到底是哪根的上級呢?對於Javascript而言,沒有類也是可以建立物件的,我們可以使用Object.setPrototypeOf(),隨時將一個物件連結到另一個物件,這讓super的指向更加複雜?其實也沒那麼複雜,如前言所講,Javascript的核心思想:詞法作用域鏈和原型鏈,函式被定義的位置和被呼叫的形式,這些能給你答案。

函式是一個獨立的特殊物件,它可以作為物件的方法和普通物件(object)關聯,這種關聯僅僅是一種動態關係,誰也不依賴誰,誰也可以沒有誰。但是函式被建立時關聯的物件很重要,它是函式一出生就看見的物件,函式長大了,會飛了,換了很多環境生活,但是它的故鄉只有一個,稱為[[HomeObject]],它出生時就會用[[HomeObject]]記錄這個故鄉(物件)。

簡單點說: super指向封閉函式定義的物件在原型鏈中的上級。

		class P {
			foo() {console.log("P.foo");}
		}

		class C extends P {
			//靜態方法的[[HomeObject]]指向Class 
			//例項方法的[[HomeObject]]指向例項
			foo() {
				/*
				super指向主調函式foo.[[HomeObject]].[[prototype]]
				即定義函式的物件的原型鏈上級
				*/
				super.foo();

			}
		}
		var c1 = new C();
		c1.foo();
		var D = {
			foo: function() {
				console.log("D.foo");
			}
		};

		var E = {
			foo: C.prototype.foo
		};
		Object.setPrototypeOf(E,D);
		E.foo();