# 類(lèi)型別名 type
- 類(lèi)型別名就是給已有的類(lèi)型取一個(gè)新名字枪眉,并不會(huì)新建類(lèi)型
- 類(lèi)型別名:可以用于
原始值
,聯(lián)合類(lèi)型
洒缀,交叉類(lèi)型
瑰谜,元組,
其他任何需要手寫(xiě)的類(lèi)型
- 錯(cuò)誤信息树绩、鼠標(biāo)懸停時(shí)萨脑,不會(huì)使用別名,而是直接顯示為所引用的類(lèi)型
- 別名不能被extends和implements
- 給原始類(lèi)型取別名通常沒(méi)什么用
如果你無(wú)法通過(guò)接口來(lái)描述一個(gè)類(lèi)型并且需要使用聯(lián)合類(lèi)型或元組類(lèi)型饺饭,交叉類(lèi)型渤早,這時(shí)通常會(huì)使用類(lèi)型別名繁莹。
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver; // type可以用于聯(lián)合類(lèi)型
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
# 泛型-類(lèi)型別名
type Tree<T> = {
value: T; // value是T類(lèi)型
left: Tree<T>; // 在類(lèi)型別名的屬性中引用自己
right: Tree<T>;
}
類(lèi)型別名也可以是泛型尖奔,類(lèi)型參數(shù)在別名右側(cè)傳入
# 類(lèi)型別名和交叉類(lèi)型一起使用
- 與交叉類(lèi)型一起使用,我們可以創(chuàng)建出一些十分稀奇古怪的類(lèi)型绿映。
type LinkedList<T> = T & { next: LinkedList<T> };
interface Person {
name: string;
}
var people: LinkedList<Person>; 相當(dāng)于 { name: string } & { next: { name: string } & {next...}}
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;
----
type TP<P> = P & { next?: TP<P> }
interface IP {
name: string;
}
const a = (age: TP<IP>) => {
return age;
}
window.console.log(a({ name: 'wang', next: { name: ''}}))
# 交叉類(lèi)型 ( & )
交叉類(lèi)型 intersection types是將多個(gè)類(lèi)型合并成一個(gè)類(lèi)型
# 聯(lián)合類(lèi)型 ( | )
聯(lián)合類(lèi)型表示一個(gè)值可以是幾種類(lèi)型之一
- 如果一個(gè)值是聯(lián)合類(lèi)型扛芽,我們只能訪問(wèn)此聯(lián)合類(lèi)型的所有類(lèi)型里共有的成員
interface Bird {
fly()
layEggs()
}
interface Fish {
swim()
layEggs()
}
let pet = getPet() // getPet()的返回值類(lèi)型是`Bird | Fish`
pet.layEggs() // 允許
pet.swim() // 報(bào)錯(cuò)
函數(shù)的返回值類(lèi)型是 Bird | Fish
我們唯一可以確定的是骂蓖,不管是Bird還是Fish,都有l(wèi)ayEggs()方法
所以訪問(wèn)pet.layEggs()不會(huì)報(bào)錯(cuò)川尖,不是共有的成員則可能類(lèi)型不符合登下,導(dǎo)致會(huì)報(bào)錯(cuò)
# 類(lèi)型保護(hù)與區(qū)分類(lèi)型
聯(lián)合類(lèi)型可以讓一個(gè)值可以為不同的類(lèi)型,但隨之帶來(lái)的問(wèn)題就是訪問(wèn)非共同方法時(shí)會(huì)報(bào)錯(cuò)叮喳。那么該如何區(qū)分值的具體類(lèi)型被芳,以及如何訪問(wèn)共有成員?
(1) 使用類(lèi)型斷言
- 類(lèi)型斷言有兩種語(yǔ)法
<類(lèi)型>值
和值 as 類(lèi)型
let someValue: any = "this is a string"; ---------------------- 是一個(gè)any類(lèi)型
let strLength: number = (<string>someValue).length; ------------ 斷言成string類(lèi)型
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length; ---------- jsx中使用as語(yǔ)法
let pet = getSmallPet();
// 每一個(gè)成員訪問(wèn)都會(huì)報(bào)錯(cuò)
if (pet.swim) { -------------------- 報(bào)錯(cuò)馍悟,因?yàn)閜et可能沒(méi)有swim屬性
pet.swim();
}
else if (pet.fly) { ------------------- 同樣報(bào)錯(cuò)
pet.fly();
}
let pet = getSmallPet();
if ((<Fish>pet).swim) { ---------------- 斷言成Fish類(lèi)型畔濒,就肯定有 swim 屬性
(<Fish>pet).swim();
}
else {
(<Bird>pet).fly();
}
(2) 使用類(lèi)型保護(hù)
使用類(lèi)型斷言,需要多次判斷十分麻煩锣咒。所以使用類(lèi)型保護(hù)
- 什么是類(lèi)型保護(hù): 這種param is SomeType的形式侵状,就是類(lèi)型保護(hù),它用來(lái)明確一個(gè)聯(lián)合類(lèi)型變量的具體類(lèi)型
-
類(lèi)型謂詞
謂詞為parameterName is Type
這種形式宠哄,parameterName必須是來(lái)自于當(dāng)前函數(shù)簽名里的一個(gè)參數(shù)名壹将。
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
// 'swim' 和 'fly' 調(diào)用都沒(méi)有問(wèn)題了
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
# typeof類(lèi)型保護(hù)
typeof 只能用于 number
, string
, boolean
, symbol
(只有這幾種類(lèi)型會(huì)被認(rèn)為是類(lèi)型保護(hù))
# instanceof類(lèi)型保護(hù) ---- 用于類(lèi)
# 索引類(lèi)型
# 索引類(lèi)型查詢(xún)操作符 ( keyof )
# 索引訪問(wèn)操作符 ( T[K] )
對(duì)于任何類(lèi)型 T, keyof T的結(jié)果為 T上已知的公共屬性名的聯(lián)合
1)首先毛嫉,使用keyof關(guān)鍵字诽俯,它是索引類(lèi)型查詢(xún)操作符,它能夠獲得任何類(lèi)型T上已知的公共屬性名的聯(lián)合。如例子中暴区,keyof T相當(dāng)于'name' | 'age'
2)然后闯团,K extends keyof T表明K的取值限制于'name' | 'age'
3)而T[K]則代表對(duì)象里相應(yīng)key的元素的類(lèi)型
public componentDidMount() {
interface P {
name: string;
age: number;
}
const people: P = {
age: 20,
name: 'wang',
};
const fn: <P, T extends keyof P>(p: P, t: T[]) => Array<P[T]> = (p, t) => {
return t.map(item => p[item])
};
const res = fn(people, ['age']);
window.console.log(res)
}
(1) fn是一個(gè)泛型函數(shù)
(2) 傳入兩個(gè)類(lèi)型參數(shù),fn函數(shù)的第一個(gè)參數(shù)是P類(lèi)型仙粱,第二個(gè)參數(shù)是T類(lèi)型的數(shù)組
(3) 索引類(lèi)型查詢(xún) --- keyof P 是P上已知的公共屬性名的聯(lián)合類(lèi)型房交,即 age | name
(4) 索引訪問(wèn) --- 函數(shù)的返回值A(chǔ)rray<P[T]>是類(lèi)型為 P接口對(duì)應(yīng)的T屬性的類(lèi)型 組成的數(shù)組
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
let obj = {
name: 'RuphiLau',
age: 21,
male: true
}
let x1 = getProperty(obj, 'name') // 允許,x1的類(lèi)型為string
let x2 = getProperty(obj, 'age') // 允許伐割,x2的類(lèi)型為number
let x3 = getProperty(obj, 'male') // 允許候味,x3的類(lèi)型為boolean
let x4 = getProperty(obj, 'hobby') // 報(bào)錯(cuò):Argument of type '"hobby"' is not assignable to parameter of type '"name" | "age" | "male"'.
# 索引類(lèi)型和字符串索引簽名
keyof和 T[K]與字符串索引簽名進(jìn)行交互。 如果你有一個(gè)帶有字符串索引簽名的類(lèi)型隔心,那么 keyof T會(huì)是 string白群。 并且 T[string]為索引簽名的類(lèi)型:
interface Map<T> {
[key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number
# 映射類(lèi)型 - 從舊類(lèi)型中創(chuàng)建新類(lèi)型
它的語(yǔ)法與索引簽名的語(yǔ)法類(lèi)型,內(nèi)部使用了 for .. in硬霍。 具有三個(gè)部分:
- 類(lèi)型變量 K帜慢,它會(huì)依次綁定到每個(gè)屬性。
- 字符串字面量聯(lián)合的 Keys唯卖,它包含了要迭代的屬性名的集合粱玲。
- 屬性的結(jié)果類(lèi)型。
interface Person {
name: string
age: number
}
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
-------------
解析:
type Readonly<T> = {
readonly [P in keyof T]: T[P]; // 類(lèi)似 for...in循環(huán)屬性名拜轨,然后用索引訪問(wèn)操作符得到屬性的類(lèi)型
}
相當(dāng)于
type Readonly<T> = {
readonly name: string
readonly age: number
}
public componentDidMount() {
interface P {
name: string;
age: number;
}
type NewP<P> = {
[K in keyof P]?: P[K] // 映射類(lèi)型抽减,這里把P類(lèi)型的屬性,變?yōu)榱丝蛇x屬性
};
const b: NewP<P> = {
age: 20,
}
window.console.log(b);
}
我們還可以寫(xiě)出更多的通用映射類(lèi)型橄碾,如:
// 可為空類(lèi)型
type Nullable<T> {
[P in keyof T]: T[P] | null
}
// 包裝一個(gè)類(lèi)型的屬性
type Proxy<T> = {
get(): T
set(value: T): void
}
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>
}
function proxify(o: T): Proxify<T> {
// ...
}
let proxyProps = proxify(props)
https://www.ruphi.cn/archives/266/
# 泛型函數(shù)
function identity<T>(arg: T): T {
return arg;
}
調(diào)用時(shí)胯甩,可以傳入類(lèi)型參數(shù),也可以使用類(lèi)型推論
let output = identity<string>("myString"); // type of output will be 'string'
let output = identity("myString"); // type of output will be 'string'
可以把泛型變量作為類(lèi)型的一部分
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
# 泛型函數(shù)類(lèi)型
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
<T>(arg: T) => T -------------------------------- 泛型函數(shù)的函數(shù)類(lèi)型
我們也可以使用不同的泛型參數(shù)名堪嫂,只要在數(shù)量上和使用方式上能對(duì)應(yīng)上就可以。
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <U>(arg: U) => U = identity;
我們還可以使用帶有調(diào)用簽名的對(duì)象字面量來(lái)定義泛型函數(shù):(!!!)
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: {<T>(arg: T): T} = identity;
# 泛型接口
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
把泛型參數(shù)當(dāng)作整個(gè)接口的一個(gè)參數(shù)木柬。 這樣我們就能清楚的知道使用的具體是哪個(gè)泛型類(lèi)型
這樣接口里的其它成員也能知道這個(gè)參數(shù)的類(lèi)型了皆串。
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
# Event對(duì)象中的 this , target 和 currentTarget
結(jié)論:
(1) this始終等于currentTarget即事件的監(jiān)聽(tīng)函數(shù)所綁定的節(jié)點(diǎn)對(duì)象
(2) target指的是最初觸發(fā)事件的監(jiān)聽(tīng)函數(shù)的節(jié)點(diǎn)對(duì)象
- target:指向 ( 最初觸發(fā)事件的節(jié)點(diǎn)對(duì)象 )。
- currentTarget: 指向 正在執(zhí)行的 ( 監(jiān)聽(tīng)函數(shù)所綁定的節(jié)點(diǎn)對(duì)象 )眉枕。
- this:指向 (綁定監(jiān)聽(tīng)函數(shù)的節(jié)點(diǎn)對(duì)象)
實(shí)例:
<div onClick={this.go}> // ------ 事件的監(jiān)聽(tīng)函數(shù)綁定的節(jié)點(diǎn)對(duì)象是 father 所在節(jié)點(diǎn)
father
<div>child</div>
</div>
當(dāng)我們點(diǎn)擊child的時(shí)候:
e.target指的就是最初點(diǎn)擊觸發(fā)事件監(jiān)聽(tīng)函數(shù)的節(jié)點(diǎn)恶复,即child 所在的節(jié)點(diǎn)
e.currentTarget = this 指的是事件監(jiān)聽(tīng)函數(shù)所綁定的節(jié)點(diǎn),即 father 所在的節(jié)點(diǎn)
# Event對(duì)象實(shí)例方法中的 preventDefault
- e.preventDefault() 取消瀏覽器對(duì)當(dāng)前事件的默認(rèn)行為
- 生效的前提是
cancelable
屬性為true
速挑,cancelable是只讀屬性
谤牡,表示事件是否可以取消
實(shí)例:
<input name="Fruit" type="radio" value="" onClick={this.goRadio}/>
public goRadio = (e: React.MouseEvent) => {
window.console.log(e.cancelable); // 只讀屬性,查看事件否可以取消
if ( e.cancelable ) { // 如果事件可以取消
e.preventDefault(); // 阻止事件的默認(rèn)行為姥宝,注意生效的前提一定是 cancelable為 true
}
}
單選框的默認(rèn)行為是點(diǎn)擊選中翅萤,但是如果在cancelable為true的情況下,
使用 e.preventDefault()則會(huì)阻止默認(rèn)行為, 使得單選框不能被選中
# Event對(duì)象中得 stopPropagation 和 stopImmediatePropagation
- e.stopPropagation() 阻止事件在DOM中繼續(xù)傳播腊满,防止再觸發(fā)定義在別得節(jié)點(diǎn)上得監(jiān)聽(tīng)函數(shù)套么,但是不包括當(dāng)前節(jié)點(diǎn)上的其他監(jiān)聽(tīng)函數(shù)
- e.stopImmediatePropagation()阻止同一事件其他監(jiān)聽(tīng)函數(shù)被調(diào)用培己,不管監(jiān)聽(tīng)函數(shù)是在當(dāng)前節(jié)點(diǎn)還是其他節(jié)點(diǎn)
# 屬性操作的標(biāo)準(zhǔn)方法
- getAttribute()
- setAttribute()
- hasAttribute()
- removeAttribute()
getAttribute()返回當(dāng)前元素節(jié)點(diǎn)指定的屬性,如果屬性不存在胚泌,返回false
setAttribute()為當(dāng)前元素節(jié)點(diǎn)新增屬性省咨,如果同名屬性已存在,則相當(dāng)于編輯已存在的屬性
# dataset屬性
<div id="mydiv" data-foo="bar">
var n = document.getElementById('mydiv');
n.dataset.foo // bar
n.dataset.foo = 'baz'
也可以通過(guò) setAttribute('data-foo')操作該屬性
注意玷室,data-后面的屬性名有限制零蓉,
只能包含字母、數(shù)字穷缤、連詞線(-)敌蜂、點(diǎn)(.)、冒號(hào)(:)和下劃線(_)绅项。
而且紊册,屬性名不應(yīng)該使用A到Z的大寫(xiě)字母,比如不能有data-helloWorld這樣的屬性名快耿,而要寫(xiě)成data-hello-world囊陡。
# prototype
js繼承機(jī)制的思想:原型對(duì)象的所有屬性和方法,都能被實(shí)例對(duì)象共享掀亥。
- 每個(gè)函數(shù)都有一個(gè)prototype屬性撞反,指向一個(gè)對(duì)象
- 對(duì)于構(gòu)造函數(shù)來(lái)說(shuō),生成實(shí)例對(duì)象的時(shí)候搪花,prototype屬性會(huì)成為 實(shí)例對(duì)象的 原型對(duì)象遏片,原型對(duì)象的屬性不是實(shí)例對(duì)象自身的屬性,只要修改原型對(duì)象撮竿,變動(dòng)就會(huì)立馬體現(xiàn)在所有實(shí)例對(duì)象上
- 如果實(shí)例對(duì)象自身就有某個(gè)屬性和方法吮便,就不會(huì)到原型對(duì)象上查找
原型對(duì)象的作用,就是定義所有實(shí)例對(duì)象所共享的屬性和方法
- 所有對(duì)象都有自己的原型對(duì)象幢踏,任何對(duì)象都繼承了Object.prototype髓需,Object.prototype的原型是null,原型鏈到此終止
# constructor
prototype對(duì)象默認(rèn)有一個(gè)constructor屬性房蝉,默認(rèn)指向prototype對(duì)象所在的構(gòu)造函數(shù)
- constructor屬性定義在prototype對(duì)象上僚匆,所以constructor屬性被所有實(shí)列對(duì)象所繼承
- constructor屬性的作用是:可以得知某個(gè)實(shí)例對(duì)象,到底是由哪個(gè)構(gòu)造函數(shù)產(chǎn)生的
- 有了constructor就可以從一個(gè)實(shí)例新建另一個(gè)實(shí)例
- 修改了原型對(duì)象搭幻,一定要同時(shí)修改constructor屬性的指向
# instanceof
instanceof返回一個(gè)boolean值咧擂,表示對(duì)象是否為某個(gè)構(gòu)造函數(shù)實(shí)例
- instanceof 運(yùn)算符 ( 左邊是實(shí)例對(duì)象 ) ,( 右邊是構(gòu)造函數(shù) )
- instanceof檢查整個(gè)原型鏈檀蹋,所以同一個(gè)實(shí)例對(duì)象可能對(duì)多個(gè)構(gòu)造函數(shù)返回true
instanceof的原理松申,是檢查右邊的構(gòu)造函數(shù)的prototype屬性,是否在左邊實(shí)例對(duì)象的原型鏈上,特殊情況:(就是左邊實(shí)例對(duì)象的原型鏈上只有null對(duì)象攻臀,這時(shí)焕数,instanceof判斷就是失真)
- instanceof運(yùn)算符的一個(gè)作用,就是判斷值得類(lèi)型
instanceof 運(yùn)算符刨啸,只能用于對(duì)象堡赔,不能用于原始類(lèi)型得值
typeof 則不能判斷出具體得的對(duì)象類(lèi)型
對(duì)于null和undefined,instanceof總是返回false
# getPrototypeof()
Object.getPrototypeof() 返回參數(shù)對(duì)象的原型设联,這是獲得原型對(duì)象的標(biāo)準(zhǔn)方法
# setPrototypeof()
Object.setPrototypeof(a, b)為參數(shù)對(duì)象設(shè)置原型善已,返回參數(shù)對(duì)象
- 有兩個(gè)參數(shù): 第一個(gè)是現(xiàn)有對(duì)象,第二個(gè)是原型對(duì)象
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b); // 把a(bǔ)對(duì)象的原型設(shè)置成b
Object.getPrototypeOf(a) === b // true Object.getPrototypeof()得到參數(shù)對(duì)象的原型
a.x // 1
var F = function () {
this.foo = 'bar';
};
var f = new F();
// 等同于
var f = Object.setPrototypeOf({}, F.prototype); //Object.setPrototypeof(a,b)返回a,并把b設(shè)置成a的原型
F.call(f);
new命令實(shí)質(zhì)上是以上過(guò)程
1)把空對(duì)象的原型設(shè)置成構(gòu)造函數(shù)的prototype屬性
2)將構(gòu)造函數(shù)內(nèi)部的 this 綁定到 空對(duì)象上离例,并執(zhí)行構(gòu)造函數(shù)
# create
Object.create( ) 以參數(shù)對(duì)象為原型换团,返回實(shí)例對(duì)象
# isPrototypeOf
實(shí)例對(duì)象的 isPrototypeof 方法,用來(lái)判斷該對(duì)象是否是參數(shù)對(duì)象的原型
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3) // true ---- o2是否是o3的原型
o1.isPrototypeOf(o3) // true ---- o1是否是o3的原型
var o2 = Object.create(o1)
實(shí)際上相當(dāng)于
var o2 = object.create = function(obj) {
function X() {}; 創(chuàng)建一個(gè)空的構(gòu)造函數(shù)
X.prototype = obj; 將構(gòu)造函數(shù)的prototype指向 參數(shù)對(duì)象
return new X(); 返回實(shí)例對(duì)象賦值給o2
}