函数重载
这个概念只会在一些强类型语言中才有,在 JavaScript 中依据不同参数类型或参数个数执行一些不同函数体的实现很常见,TypeScript 就会需要用到这种声明的地方。
函数重载定义:函数名相同,函数的参数列表不同(包括参数个数和参数类型),根据参数的不同去执行不同的操作。
关于函数重载,必须要把精确的定义放在前面,最后函数实现时,需要使用|操作符或者?操作符,把所有可能的输入类型全部包含进去。
JavaScript 函数重载
function overload(a){
console.log('一个参数')
}
function overload(a,b){
console.log('两个参数')
}
// 在支持重载的编程语言中,比如 java
overload(1); //一个参数
overload(1,2); //两个参数
// 在 JavaScript 中
overload(1); //两个参数
overload(1,2); //两个参数
在
JavaScript中,同一个作用域,出现两个名字一样的函数,后者会覆盖前者,所以JavaScript没有真正意义的重载。
但是有各种办法,能在 JavaScript 中模拟实现重载的效果。
我们要在 users 对象中添加一个 find 方法,当不传任何参数时,返回整个 users.values;
当传一个参数时,就把 first-name 和这个参数匹配的元素返回;
当传两个参数时,则把 first-name 和 last-name 都匹配返回。
let users = {
values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};
/**
* @param object 绑定方法的对象
* @param name 绑定的方法名称
* @param fn 需要绑定的方法
*/
function addMethod(object, name, fn) {
let old = object[name]
object[name] = function () {
if (fn.length === arguments.length) return fn.apply(this, arguments)
else if (typeof old === 'function') return old.apply(this, arguments)
}
}
function find0() {
return this.values
}
function find1(firstName) {
let ret = []
this.values.forEach(item => item.includes(firstName) && ret.push(item))
return ret
}
function find2(firstName, lastName) {
let ret = []
this.values.forEach(item => item === `${firstName} ${lastName}` && ret.push(item))
return ret
}
addMethod(users, "find", find0);
addMethod(users, "find", find1);
addMethod(users, "find", find2);
console.log(users.find()); // ["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(users.find("Dean")); // ["Dean Edwards", "Dean Tom"]
console.log(users.find("Dean", "Edwards")); // ["Dean Edwards"]
addMethod 函数是利用了闭包的特性,通过变量 old 将每个函数连接起来,让所有函数都留在内存中。
每调用一个 addMethod 函数,就会产生一个 old,形成一个闭包。我们可以通过 console.dir(users.find),把 find 方法打印到控制台看看。
TypeScript 函数重载
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x): any {
if (typeof x === "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
if (typeof x === "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [
{ suit: "diamonds", card: 2 },
{ suit: "spades", card: 10 },
{ suit: "hearts", card: 4 },
];
let pickedCard1 = myDeck[pickCard(myDeck)];
console.log("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
console.log("card: " + pickedCard2.card + " of " + pickedCard2.suit);
注意:
function pickCard(x): any并不是重载列表的一部分,因此这里只有两个重载:一个是接收对象另一个接受数字。以其它参数调用pickCard会产生错误。
重载好处
重载其实是把多个功能相近的函数合并为一个函数,重复利用了函数名。假如jQuery中的css( )方法不使用 重载,那么就要有5个不同的函数,来完成功能,那我们就需要记住5个不同的函数名,和各个函数相对应的参数的个数和类型,显然就麻烦多了。虽然,重载能为我们带来许多的便利,但是也不能滥用,不要把一些根本不相关的函数合为一个函数,那样并没有什么意义。
如果函数的返回值类型相同,那么就不需要使用函数重载。
function func (a: number): number
function func (a: number, b: number): number
// 参数个数的区别,使用可选参数来代替函数重载的定义
function func (a: number, b?: number): number
function func (a: number): number
function func (a: string): number
// 使用联合类型来代替函数重载
function func (a: number | string): number
Mobx + 函数重载
创建 counterStore
import { observable } from "mobx";
export interface CounterStore {
counter: number;
increment: () => void;
decrement: () => void;
incrementAsync: () => void;
}
const counterStore: CounterStore = observable({
counter: 0,
increment() {
this.counter++;
},
decrement() {
this.counter--;
},
incrementAsync() {
setTimeout(() => {
this.counter++;
}, 1000);
},
});
export default counterStore;
Store 类型定义
import counterStore from "./counter";
const _store = {
counterStore,
};
export type StoreType = typeof _store;
export default _store;
创建 useStores 自定义Hook
import { useContext } from "react";
import { MobXProviderContext } from "mobx-react";
import { StoreType } from "src/stores";
interface ContextType {
stores: StoreType;
}
function useStores(): StoreType;
function useStores<T extends keyof StoreType>(storeName: T): StoreType[T];
/**
* 获取根 store 或者指定 store 名称数据
* @param storeName 指定子 store 名称
* @returns typeof StoreType[storeName]
*/
function useStores<T extends keyof StoreType>(storeName?: T) {
const rootStore = useContext(MobXProviderContext);
const { stores } = rootStore as ContextType;
return storeName ? stores[storeName] : stores;
}
export default useStores;
使用
import { observer } from "mobx-react";
import useStores from "src/hooks/useStores";
const Home = () => {
const counterStore = useStores("counterStore");
return (
<>
<div>current counter:{counterStore.counter}</div>
<div onClick={() => counterStore.increment()}>增加</div>
<div onClick={() => counterStore.decrement()}>减少</div>
</>
);
};
export default observer(Home);
参考文章:TypeScript学习(二)函数重载