在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
函数重载这个概念是在一些强类型语言中才有的,在JS中依据不同参数类型或参数个数执行一些不同函数体的实现很常见,依托于TypeScript,就会有需要用到这种声明的地方。 函数重载定义:函数名相同,函数的参数列表不同(包括参数个数和参数类型),根据参数的不同去执行不同的操作。 关于函数重载,必须要把精确的定义放在前面,最后函数实现时,需要使用 js函数重载js 因为是动态类型,本身不需要支持重载,直接对参数进行类型判断即可,因此js没有真正意义上的函数重载 我们举个例子看看 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 中模拟实现重载的效果。 arguments 对象来实现arguments 对象,是函数内部的一个类数组对象,它里面保存着调用函数时,传递给函数的所有参数。 function overload () { if (arguments.length === 1) { console.log('一个参数') } if (arguments.length === 2) { console.log('两个参数') } } overload(1); //一个参数 overload(1, 2); //两个参数 这个例子非常简单,就是通过判断 arguments 对象的 length 属性来确定有几个参数,然后执行什么操作。 但是参数少的情况下,还好,如果参数多一些,if 判断就需要写好多,就麻烦了。 所以,我们再来看一个经典的例子 在看这个例子之前,我们先来看一个需求,我们有一个 users 对象,users 对象的values 属性中存着一些名字。 一个名字由两部分组成,空格左边的是 first-name ,空格右边的是 last-name,像下面这样。 var users = { values: ["Dean Edwards", "Alex Russell", "Dean Tom"] }; 我们要在 users 对象 中添加一个 find 方法, 当不传任何参数时, 返回整个 当传一个参数时,就把 first-name 跟这个参数匹配的元素返回; 当传两个参数时,则把 first-name 和 last-name 都匹配的返回。 这个需求中 find方法 需要根据参数的个数不同而执行不同的操作,下来我们通过一个 addMethod 函数,来在 users 对象中添加这个 find 方法。 function addMethod (object, name, fn) { // 先把原来的object[name] 方法,保存在old中 var old = object[name]; // 重新定义 object[name] 方法 object[name] = function () { // 如果函数需要的参数 和 实际传入的参数 的个数相同,就直接调用fn if (fn.length === arguments.length) { return fn.apply(this, arguments); // 如果不相同,判断old 是不是函数, // 如果是就调用old,也就是刚才保存的 object[name] 方法 } else if (typeof old === "function") { return old.apply(this, arguments); } } } addMethod 函数,它接收3个参数 第二个:绑定的方法名称, 第三个:需要绑定的方法 这个 addMethod 函数在判断参数个数的时候,除了用 arguments 对象,还用了函数的 length 属性。 函数的 length 属性,返回的是函数定义时形参的个数。 简单说 函数的 length 是,函数需要几个参数,而 function fn (a, b) { console.log(arguments.length) } console.log(fn.length); // 2 fn('a'); // 1 下来我们来使用这个 addMethod 函数 // 不传参数时,返回整个values数组 function find0 () { return this.values; } // 传一个参数时,返回firstName匹配的数组元素 function find1 (firstName) { var ret = []; for (var i = 0; i < this.values.length; i++) { if (this.values[i].indexOf(firstName) === 0) { ret.push(this.values[i ]); } } return ret; } // 传两个参数时,返回firstName和lastName都匹配的数组元素 function find2 (firstName, lastName) { var ret = []; for (var i = 0; i < this.values.length; i++) { if (this.values[i ] === (firstName + " " + lastName)) { ret.push(this.values[i ]); } } return ret; } // 给 users 对象添加处理 没有参数 的方法 addMethod(users, "find", find0); // 给 users 对象添加处理 一个参数 的方法 addMethod(users, "find", find1); // 给 users 对象添加处理 两个参数 的方法 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,形成一个闭包。 我们可以通过 上面这个例子是 jQuery 之父 John Resig 写的,他在他的博客和他写的书 《secrets of the JavaScript ninja》第一版中都有提到过,在书中的第4章中也有讲解 Function overloading,文中的 addMethod 函数 就是书中的例子 4.15,感兴趣的朋友可以去看看。 上面的例子,本质都是在判断参数的个数,根据不同的个数,执行不同的操作,而下来举的例子是通过判断参数的类型,来执行不同的操作。 TS函数重载为同一个函数提供多个函数类型定义来进行函数重载,目的是重载的 例子1:例如我们有一个add函数,它可以接收string类型的参数进行拼接,也可以接收number类型的参数进行相加。 // 上边是声明 function add (arg1: string, arg2: string): string function add (arg1: number, arg2: number): number // 因为我们在下边有具体函数的实现,所以这里并不需要添加 declare 关键字 // 下边是实现 function add (arg1: string | number, arg2: string | number) { // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2 if (typeof arg1 === 'string' && typeof arg2 === 'string') { return arg1 + arg2 } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { return arg1 + arg2 } } TypeScript 中的函数重载也只是多个函数的声明,具体的逻辑还需要自己去写,他并不会真的将你的多个重名 function 的函数体进行合并 考虑如下 例子2: interface User { name: string; age: number; } declare function test(para: User | number, flag?: boolean): number; 在这个 test 函数里,我们的本意可能是当传入参数 para 是 User 时,不传 flag,当传入 para 是 number 时,传入 flag。TypeScript 并不知道这些,当你传入 para 为 User 时,flag 同样允许你传入:
const user = { name: 'Jack', age: 666 } // 没有报错,但是与想法违背 const res = test(user, false); 使用函数重载能帮助我们实现: interface User { name: string; age: number; } declare function test(para: User): number; declare function test(para: number, flag: boolean): number; const user = { name: 'Jack', age: 666 }; // bingo // Error: 参数不匹配 const res = test(user, false); 实际项目中,你可能要多写几步,如在 class 中: interface User { name: string; age: number; } const user = { name: 'Jack', age: 123 }; class SomeClass { /** * 注释 1 */ public test(para: User): number; /** * 注释 2 */ public test(para: number, flag: boolean): number; public test(para: User | number, flag?: boolean): number { // 具体实现 return 11; } } const someClass = new SomeClass(); // ok someClass.test(user); someClass.test(123, false); // Error someClass.test(123); someClass.test(user, false); 官网: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 { // Check to see if we're working with an object/array // if so, they gave us the deck and we'll pick the card if (typeof x == "object") { let pickedCard = Math.floor(Math.random() * x.length); return pickedCard; } // Otherwise just let them pick the card else 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)]; alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); let pickedCard2 = pickCard(15); alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); 重载的 为了让编译器能够选择正确的检查类型,它与JavaScript里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。 注意, 重载的好处重载其实是把多个功能相近的函数合并为一个函数,重复利用了函数名。 假如jQuery中的css( )方法不使用 重载,那么就要有5个不同的函数,来完成功能,那我们就需要记住5个不同的函数名,和各个函数相对应的参数的个数和类型,显然就麻烦多了。 一些不需要函数重载的场景(并不绝对,如上例子2)函数重载的意义在于能够让你知道传入不同的参数得到不同的结果,如果传入的参数不同,但是得到的结果(类型)却相同,那么这里就不要使用函数重载(没有意义)。 如果函数的返回值类型相同,那么就不需要使用函数重载 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 总结虽然 JavaScript 并没有真正意义上的重载,但是重载的效果在JavaScript中却非常常见,比如 数组的 splice( )方法,一个参数可以删除,两个参数可以删除一部分,三个参数可以删除完了,再添加新元素。 文中提到的实现重载效果的方法,本质都是对参数进行判断,不管是判断参数个数,还是判断参数类型,都是根据参数的不同,来决定执行什么操作的。 虽然,重载能为我们带来许多的便利,但是也不能滥用,不要把一些根本不相关的函数合为一个函数,那样并没有什么意义。
|
请发表评论