先看兩篇文章關(guān)于 ES5 原型鏈的蛤虐,寫的特別好党饮,圖畫的就更好了,參考資料:
幫你徹底搞懂JS中的prototype驳庭、proto與constructor(圖解)
詳解JavaScript中的new操作符
忍不住盜圖一張:
原型鏈:f1.__proto__.__proto__.__proto__ === null;
一刑顺、TypeScript 類
1.1 介紹
TypeScript 的 class 和 ES2015 的 class 并不是完全一樣的概念,ES6 的 class 僅僅是一種語法糖饲常,但是這種語法糖已經(jīng)成為標(biāo)準(zhǔn)蹲堂,并且(新)瀏覽器內(nèi)核基本支持,而平時(shí)使用的時(shí)候不皆,基本都是通過 webpack 或者 gulp 這種進(jìn)行兼容或者打包贯城。
而 TypeScript 的 class 可以使得開發(fā)者在不依賴 ES5 的環(huán)境下直接使用,不需要等到下個(gè) JS 版本霹娄。類的形式在 JavaScript 中有兩種寫法能犯,ES5 的類和 ES6 的類,決定 TypeScript 翻譯成哪種在 tsconfig.json 里面配置:
"target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
1.2 TypeScript 類與 ES6 類的不同犬耻。
ES6 的類有 extends 和 static 這點(diǎn)和 TypeScript 沒啥不同踩晶,不同的是 ES6 的屬性修飾符只有默認(rèn)公開屬性和一個(gè)私有屬性(還是在提案中)。私有屬性還和 TypeScript 不同枕磁,使用 # 來定義的渡蜻,官方還給出了理由。
之所以要引入一個(gè)新的前綴#表示私有屬性计济,而沒有采用private關(guān)鍵字茸苇,是因?yàn)?JavaScript 是一門動(dòng)態(tài)語言,沒有類型聲明沦寂,使用獨(dú)立的符號(hào)似乎是唯一的比較方便可靠的方法学密,能夠準(zhǔn)確地區(qū)分一種屬性是否為私有屬性。另外传藏,Ruby 語言使用@表示私有屬性腻暮,ES6 沒有用這個(gè)符號(hào)而使用#彤守,是因?yàn)锧已經(jīng)被留給了 Decorator
ES 和 TS 比除了屬性修飾符不同,沒有抽象類之外哭靖,其他的都差不多具垫。
1.3 屬性修飾符
1、public 默認(rèn)
一些語言的默認(rèn)屬性修飾符是 protected试幽,而 TypeScript 中成員屬性默認(rèn)都是 public筝蚕,除了默認(rèn)之外,也可以強(qiáng)制手動(dòng)指定铺坞。
特點(diǎn)是:可以被繼承饰及、外部實(shí)例和內(nèi)部 this 可以訪問
2、private 私有屬性
特點(diǎn)是:只能內(nèi)部訪問
3康震、protected
構(gòu)造函數(shù)如果使用 protected 聲明燎含,說明這個(gè)類不能被實(shí)例化,只能被繼承腿短。
特點(diǎn)是:外部實(shí)例不能訪問
4屏箍、readonly 修飾符
readonly 修飾符用于將屬性設(shè)置為只讀,如果要設(shè)置值橘忱,則只能在聲明或者是在構(gòu)造函數(shù)中初始化內(nèi)容赴魁。
5、參數(shù)屬性
定義的屬性可以賦值默認(rèn)值钝诚,不在構(gòu)造函數(shù)進(jìn)行初始化颖御。
1.4 抽象類
抽象類作為其他子類的基類,一般不進(jìn)行直接實(shí)例化凝颇,特別的像接口潘拱,但和接口不同的是,抽象類可以去實(shí)現(xiàn)成員屬性或者是方法拧略,然后子類再去覆寫成員屬性或者方法芦岂。
abstract class Person {
abstract talk(): void;
walk(): void {
console.log('...')
}
}
和其他高級(jí)語言的面向?qū)ο蟪橄箢愐粯樱绻粋€(gè)方法被定義為 abstract(抽象方法)垫蛆,則這個(gè)方法可以不在基類中實(shí)現(xiàn)禽最,但是它的子類必須去實(shí)現(xiàn)這個(gè)方法。抽象方法必須在抽象類里面袱饭。
abstract class Person {
abstract talk(): void;
walk(): void {
console.log('...')
}
}
new Person();//不允許川无,直接報(bào)錯(cuò)
class Male extends Person{
talk(){}//必須有這個(gè)
}
1.5 TypeScript 類的其他知識(shí)
去參考:http://es6.ruanyifeng.com/#docs/class
二、函數(shù)
TypeScript 的函數(shù)主要增加了強(qiáng)類型判斷和一個(gè)函數(shù)重載知識(shí)點(diǎn)虑乖。
2.1 函數(shù)類型
下面兩個(gè)函數(shù)聲明了參數(shù)的類型以及函數(shù)返回值的類型,上面是具名函數(shù)懦趋,下面是匿名函數(shù)
function add(x: number , y: number ): number {
return x + y;
}
let myAdd = function(x: number, y: number) :number {
return x + y;
}
雖然上面已經(jīng)定義了函數(shù)的類型,但是匿名函數(shù)并沒有定義完整的函數(shù)類型决左,完整的函數(shù)類型:
let myAdd: (x: number, y: number) => number = function (x: number, y: number) : number {
return x + y;
}
完整的函數(shù)類型包括:參數(shù)類型 和 返回值類型
首先聲明了參數(shù)類型 :(x: number, y: number)
愕够,然后聲明了返回值類型 => number =
。
而參數(shù)的名字實(shí)際上可以不一致佛猛,上面示例中雖然寫了相同的名字惑芭,但是可以寫成兩個(gè)不同的參數(shù)名稱:
let myAdd: (x1: number, y1: number) => number = function (x: number, y: number) : number {
return x + y;
}
返回值類型通過 =>
進(jìn)行表示,如果一個(gè)函數(shù)返回值是 void 继找,也必須聲明是 void遂跟,不能省略因?yàn)橥暾暮瘮?shù)類型包含參數(shù)類型和返回值類型兩個(gè)部分,一個(gè)也不能省略婴渡。
let myAdd: (x1: number, y1: number) => void = function (x: number, y: number) : number {
return x + y;
}
函數(shù)的類型只是由參數(shù)類型和返回值組成的幻锁。
函數(shù)中使用的捕獲變量不會(huì)體現(xiàn)在類型里。 實(shí)際上边臼,這些變量是函數(shù)的隱藏狀態(tài)并不是組成API的一部分哄尔。
所謂的捕獲變量,與函數(shù)本身是無關(guān)的柠并,因?yàn)檫@個(gè)是由函數(shù)作用域外(父級(jí)作用域或更高級(jí)作用域)聲明的變量岭接,只是在函數(shù)中使用了而已。
const a:number = 1;
let add:(x: number) => number = function(x: number) :number {
return a + x;
}
推斷類型
完整的函數(shù)類型中臼予,需要我們明確標(biāo)示參數(shù)的類型是什么鸣戴,但是如果兩邊的參數(shù)中只有一邊的參數(shù)聲明了類型,typescript 會(huì)嘗試去識(shí)別類型:
let myAdd: (baseValue: number, increment: number) => number =
function(x, y) { return x + y; };
上面代碼中粘拾,因?yàn)?baseValue
和 increment
已經(jīng)聲明了類型是 number窄锅,所以 x 和 y 會(huì)推斷出類型是 number。
2.2 可選參數(shù)和默認(rèn)參數(shù)
TypeScript 里的每個(gè)函數(shù)參數(shù)都是必須的缰雇,這不是指不能傳遞 null
或 undefined
作為參數(shù)入偷,而是說編譯器檢查用戶是否為每個(gè)參數(shù)都傳入了值。
編譯器還會(huì)假設(shè)只有這些參數(shù)會(huì)被傳遞進(jìn)函數(shù)械哟。
簡(jiǎn)短地說盯串,傳遞給一個(gè)函數(shù)的參數(shù)個(gè)數(shù)必須與函數(shù)期望的參數(shù)個(gè)數(shù)一致。
function add(x: number, y: number) :number {
return x + y;
}
add(1,2);
add(1); // 錯(cuò)誤
add(1,2,3); // 錯(cuò)誤
上面 add 方法的使用中戒良,后面兩種都是錯(cuò)誤的体捏,因?yàn)閭魅氲膮?shù)個(gè)數(shù)與期望的個(gè)數(shù)不相符合
如果需要聲明某個(gè)參數(shù)是可選參數(shù),則可以通過 ?
來標(biāo)示:
function add(x:number, y:number, z?: number): number {
z = z || 0;
return x + y + z;
}
上面代碼中通過 z?
聲明 z 可選參數(shù)糯崎,因此傳入 2 個(gè)或者3個(gè)參數(shù)都是可以的几缭。
然而上面代碼為了兼容 z 沒有值得時(shí)候,寫了個(gè)默認(rèn)值處理 z = z || 0
沃呢,而 ES6 有了函數(shù)參數(shù)默認(rèn)值特性之后年栓,這種寫法基本都是被廢棄的,在 TypeScript 中薄霜,可以使用同樣的方式聲明函數(shù)參數(shù)的默認(rèn)值:
function add(x:number, y:number, z:number = 0): number {
return x + y + z;
}
add(1, 3);
add(1,2,3);
add(1,2,3,4)
而一旦聲明了默認(rèn)值之后某抓,就表示這個(gè)值可有可無纸兔,類似于已經(jīng)加了 z?
聲明。
需要注意的是否副,可選參數(shù)必須接在必需參數(shù)后面汉矿。換句話說,可選參數(shù)后面不允許再出現(xiàn)必需參數(shù)了
2.3 剩余參數(shù)
ES6 同樣擁有 rest 參數(shù)备禀,也就是剩余參數(shù)洲拇。它可以表示任意剩下的參數(shù),本質(zhì)是一個(gè)數(shù)組曲尸。
typescript 中同樣通過省略號(hào)來表示剩下的多個(gè)參數(shù):
function add(x: number, ...otherNum:number[]): number {
otherNum && otherNum.forEach((item) => {
x += item;
});
return x;
}
add(1, 3, 4, 54, 5, 6);
2.4 重載
重載:允許一個(gè)函數(shù)接受不同數(shù)量或類型的參數(shù)時(shí)赋续,作出不同的處理。比如另患,我們需要實(shí)現(xiàn)一個(gè)函數(shù) reverse纽乱,輸入數(shù)字 123 的時(shí)候,輸出反轉(zhuǎn)的數(shù)字 321昆箕,輸入字符串 'hello' 的時(shí)候迫淹,輸出反轉(zhuǎn)的字符串 'olleh'。
利用聯(lián)合類型为严,我們可以這么實(shí)現(xiàn):
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
然而這樣有一個(gè)缺點(diǎn)敛熬,就是不能夠精確的表達(dá),輸入為數(shù)字的時(shí)候第股,輸出也應(yīng)該為數(shù)字应民,輸入為字符串的時(shí)候,輸出也應(yīng)該為字符串夕吻。
這時(shí)诲锹,我們可以使用重載定義多個(gè) reverse 的函數(shù)類型:
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
上例中,我們重復(fù)定義了多次函數(shù) reverse涉馅,前幾次都是函數(shù)定義归园,最后一次是函數(shù)實(shí)現(xiàn)。在編輯器的代碼提示中稚矿,可以正確的看到前兩個(gè)提示庸诱。
但是需要注意的是重載函數(shù)只是兩個(gè)函數(shù)聲明,并沒有函數(shù)體晤揣。所以上面雖然有三個(gè)函數(shù)但是只有兩個(gè)重載函數(shù)桥爽。
TypeScript 會(huì)優(yōu)先從最前面的函數(shù)定義開始匹配,所以多個(gè)函數(shù)定義如果有包含關(guān)系昧识,需要優(yōu)先把精確的定義寫在前面钠四。