typescript上手-函数(2)

函数

和js一样,ts函数可以创建有名字的函数和匿名函数。
在es5中定义一个function有这么两种方式

	// 命名函数
	function fun(ags) {
	    return ags;
	}
	// 匿名函数
	let fun = function(ags) { 
		return ags
	};

函数类型

在ts中变量都是有固定的类型的,函数同样需要指定类型,包括参数和返回值

	function toStr(x: number): string {
	    return x + '';
	}
	let myAdd = function(x: number, y: number): number { 
		return x + y;
	};
	//没有返回值的函数
	function noRtn(x:string):void{
		console.log('没有返回值的函数')
	}

ts能够根据返回语句自动推断出返回值类型,换句话说返回值的类型可以省略。

	function toStr(x: number){
	    return x + '';
	}
	let myAdd = function(x: number, y: number){ 
		return x + y;
	};
	function noRtn(x:string){
		console.log('没有返回值的函数')
	}

至于es6的箭头函数这里需要注意一下,在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

	//ts函数完整写法
	let _Add: (x: number, y: number) => number =
    function(x: number, y: number): number { return x + y; };
	//箭头函数
	let fun = (a:string)=>{console.log(1)}

可选参数

ts里的每个函数参数都是必须的。 编译器检查用户是否为每个参数都传入了值,就是传递给一个函数的参数个数必须与函数定义的参数个数一致。

	let _add=(a:number,b:number)=>{return a+b;};
	_add(1,2)		//fine
	_add(1)			//error
	_add(1,2,3)		//error

如果是入参数量不确定则在定义参数后加上‘?’将他变成可选参数,并且可选参数必须跟在必须参数后面;

	function _add(a: string, b?: string) {
	    if (b)
	        return a + b;
	    else
	        return a;
	}
	let res1 = _add(1);  	// fine
	let res2 = _add(1,2);  	// fine
	let res3 = _add(1,2,3);  // error

默认参数

对应js中的默认参数,ts也可以在函数声明时在同样位置定义默认参数,当没有值的时候取默认值,传递参数的时候替换默认值。

	function _add(a: number, b=1) {
        return a + b;
	}
	
	let res1 = _add(1);                 // 2
	let res2 = _add(1, undefined);      // 2
	let res3 = _add(1, 2);				// 3

看res1也可以看出带默认值的参数也是可选参数;之后来试试看默认参数能不能放在常规参数的前边

	function _add(a=1, b: number) {
        return a + b;
	}
	
	let res1 = _add(2);                 // error
	let res2 = _add(undefined,2);     	// 3
	let res3 = _add(2, 1);				// 3

可以看出放在其他普通参数之前是可以的,并且传入参数数量仍需一致;

剩余参数

剩余参数也就是现在js中常用的rest参数,通常(…rest)一丢就完了,或者麻烦一点使用arguments进行遍历;
在ts里也可以用…rest收集参数

	function _add(a: number, ...rest: number[]) {
	    return a+rest.reduce(function (x, y) { return x + y })
	}
	console.log(_add(1,2,3,4,5))	//15	

当然rest这个是可以随意命名的,rest只是个习惯。

重载

重载是指多个函数接受不同的参数而不同处理返回的情况;JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。但是es中如果定义多个重名function则代码后面的会将之前的同名函数覆盖。ts参考了java\c#等面向对象语言的习惯,也搞了个重载

	function fun(arg: string): string
	function fun(arg: number): number
	function fun(arg:any):any{
		if(typeof arg === 'string'){
			return '字符串';
		}else if(typeof arg === 'number'){
			return ++arg
		}
	}

所示情况是传入参数类型不一致,另一种情况就是参数数量不一致的

	function fun(arg: string): string
	function fun(arg: string,sec:number): number
	function fun(arg:any,sec?:any):any{
		if(sec){
			return arg+sec;
		}else{
			return arg;
		}
	}

this

我大致扫了一眼文档觉得和js的this没有啥冲突,所以以下是照搬文档了,已备遇到问题再翻过头来看,自信的可以跳过了。

this和箭头函数

JavaScript里,this的值在函数被调用的时候才会指定。 这是个既强大又灵活的特点,但是你需要花点时间弄清楚函数调用的上下文是什么。 但众所周知,这不是一件很简单的事,尤其是在返回一个函数或将函数当做参数传递的时候。

下面看一个例子:

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        return function() {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

可以看到createCardPicker是个函数,并且它又返回了一个函数。 如果我们尝试运行这个程序,会发现它并没有弹出对话框而是报错了。 因为 createCardPicker返回的函数里的this被设置成了window而不是deck对象。 因为我们只是独立的调用了 cardPicker()。 顶级的非方法式调用会将 this视为window。 (注意:在严格模式下, this为undefined而不是window)。

为了解决这个问题,我们可以在函数被返回时就绑好正确的this。 这样的话,无论之后怎么使用它,都会引用绑定的‘deck’对象。 我们需要改变函数表达式来使用ECMAScript 6箭头语法。 箭头函数能保存函数创建时的 this值,而不是调用时的值:

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
    // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

更好事情是,TypeScript会警告你犯了一个错误,如果你给编译器设置了–noImplicitThis标记。 它会指出 this.suits[pickedSuit]里的this的类型为any。

this参数
不幸的是,this.suits[pickedSuit]的类型依旧为any。 这是因为 this来自对象字面量里的函数表达式。 修改的方法是,提供一个显式的 this参数。 this参数是个假的参数,它出现在参数列表的最前面:

function f(this: void) {
    // make sure `this` is unusable in this standalone function
}

让我们往例子里添加一些接口,Card 和 Deck,让类型重用能够变得清晰简单些:

interface Card {
    suit: string;
    card: number;
}
interface Deck {
    suits: string[];
    cards: number[];
    createCardPicker(this: Deck): () => Card;
}
let deck: Deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    // NOTE: The function now explicitly specifies that its callee must be of type Deck
    createCardPicker: function(this: Deck) {
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

现在TypeScript知道createCardPicker期望在某个Deck对象上调用。 也就是说 this是Deck类型的,而非any,因此–noImplicitThis不会报错了。

this参数在回调函数里
你可以也看到过在回调函数里的this报错,当你将一个函数传递到某个库函数里稍后会被调用时。 因为当回调被调用的时候,它们会被当成一个普通函数调用, this将为undefined。 稍做改动,你就可以通过 this参数来避免错误。 首先,库函数的作者要指定 this的类型:

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void;
}
this: void means that addClickListener expects onclick to be a function that does not require a this type. Second, annotate your calling code with this:

class Handler {
    info: string;
    onClickBad(this: Handler, e: Event) {
        // oops, used this here. using this callback would crash at runtime
        this.info = e.message;
    }
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!

指定了this类型后,你显式声明onClickBad必须在Handler的实例上调用。 然后TypeScript会检测到 addClickListener要求函数带有this: void。 改变 this类型来修复这个错误:

class Handler {
    info: string;
    onClickGood(this: void, e: Event) {
        // can't use this here because it's of type void!
        console.log('clicked!');
    }
}
let h = new Handler();
uiElement.addClickListener(h.onClickGood);

因为onClickGood指定了this类型为void,因此传递addClickListener是合法的。 当然了,这也意味着不能使用 this.info. 如果你两者都想要,你不得不使用箭头函数了:

class Handler {
    info: string;
    onClickGood = (e: Event) => { this.info = e.message }
}

这是可行的因为箭头函数不会捕获this,所以你总是可以把它们传给期望this: void的函数。 缺点是每个 Handler对象都会创建一个箭头函数。 另一方面,方法只会被创建一次,添加到 Handler的原型链上。 它们在不同 Handler对象间是共享的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值