typescript[4] - function

// -------- Functions(函数) --------

// 函数是js程序的基本构件.
// 函数包括如何建立抽象层,模拟类,信息屏蔽和模块
// 在typescript中,一旦涉及到类和模块,函数肯定在其中扮演重要角色
// typescript也加了一些新的功能到标准js函数中,来使得它更加方便使用

// -------- Functions(函数) --------

// 作为开始,如同在javascript中一样,typescript也可以建立有名字的函数和匿名函数
// 这使得你可以为你的程序选择合适的方式,不论你在创建一个api的函数列表,还是创建一次性的函数

// 来快速回顾下两种方式在javascript中的实现

/*
	//Named function
	function add(x, y) {
	    return x+y;
	}

	//Anonymous function
	var myAdd = function(x, y) { return x+y; };
*/

// 如同在javascript中,函数在return中可以使用函数体外的变量
// 当他们这么做,他们被称为,他们捕获到了这些变量
/*
	var z = 100;

	function addToZ(x, y) {
	    return x+y+z;
	}
*/

//  -------- Function Types(函数类型) -------- 
// 让我们从早些的一个简单例子来增加类型

function add(x: number, y: number): number {
	return x + y;
}

var myAdd = function(x: number, y: number): number { return x + y; };

// 我们给函数的每个参数加了类型,也给函数的返回值加了类型
// typescript可以从return代码段中识别出return的类型,所以我们可以在很多情况下省略它

//	Writing the function type
// 现在我们已经给函数确定了类型,让我们通过观察函数各个部分的类型,来写出函数的完整类型

var myAdd2: (x: number, y: number) => number =
	function(x: number, y: number): number {
		return x + y;
	}

// 函数类型分为2个部分,参数的类型和返回值的类型那个
// 当写完整的函数类型,这2个部分都是需要的
// 我们写出参数类型就像参数列表,给与每个参数名字和类型
// 参数名字只是为了让我们更好的阅读,我们可以替换它们如下

/*
	var myAdd: (baseValue:number, increment:number)=>number = 
    function(x: number, y: number): number { return x+y; };
*/

// 只要你排列出参数类型,它就被认为是函数的合法类型,不要在意你给出的参数的名字

// 第二个部分是返回值类型.我们可以在参数列表和返回值类型之间使用"=>",这样返回类型会很清楚
// 如前面提到的,这个是函数类型的一个必要部分
// 所以如果函数没有返回值,那么你应该用"void",而不是留空

// 提醒,只有参数和返回类型定义了函数的类型
// 捕获的变量是不影响函数类型的
// 在影响上,这些捕获变量是函数的"隐藏部分",虽然不是api的组成部分


// -------- Inferring the types(类型推断) --------

// 在例子里,你可能意识到了,typescript编译器可以识别出类型,即便你在"="的两边,一边写了类型,而一边没有

// myAdd has the full function type
var myAdd2 = function(x: number, y: number): number { return x + y; };

// The parameters 'x' and 'y' have the type number
var myAdd3: (baseValue: number, increment: number) => number =
    function(x, y) { return x + y; };

// 这个被称为"上下文类型",一种类型推断的形式
// 这有效降低你的代码量,并且保证了你程序的类型安全

//  -------- Optional and Default Parameters(可选参数和默认参数) -------- 
// 跟javascript不一样,在typescript中,每个函数中的参数都是被函数所需要的
// 这不意味没有null值,而是,当函数被编译器调用,编译器会检测用户是否给每个参数提供了值
// 编译器也会检测提供的参数是不是函数所要的参数(参数不能多)
// 简单的说,传递给函数的参数要跟函数所要求的参数个数一致

/*
	function buildName(firstName: string, lastName: string) {

	    return firstName + " " + lastName;

	}

	

	var result1 = buildName("Bob");  //error, too few parameters

	var result2 = buildName("Bob", "Adams", "Sr.");  //error, too many parameters

	var result3 = buildName("Bob", "Adams");  //ah, just right
*/

// 在javascript中,每个参数都被认为是可选的,用户觉得合适就可以把他们留空
// 当他们这么做的时候,这些被留空的参数的值就是undefined
// 在typescript中,我们如果要让参数为可选参数,我们要用"?"跟在参数后面来修饰参数
// 举例,我们将"last name"设为可选

function buildName(firstName: string, lastName?: string) {
	if (lastName)
		return firstName + " " + lastName;
	else
		return firstName;
}

var result1 = buildName("Bob");  //works correctly now
// var result2 = buildName("Bob", "Adams", "Sr.");  //error, too many parameters
var result3 = buildName("Bob", "Adams");  //ah, just right

// 可选参数必须在必须提供的参数之后
// 如果我们要让firstName可选,而不是lastName,我们需要改变参数在函数中的位置,把firstName放在列表的最后

// 在typescript中,我们也可以给与可选参数一个默认值,如果玩家没有提供值
// 这个被称为默认值
// 让我们把上面的例子中的lastName赋予一个默认值"Smith"
function buildName2(firstName: string, lastName = "Smith") {
    return firstName + " " + lastName;
}
var result12 = buildName2("Bob");  //works correctly now
// var result22 = buildName2("Bob", "Adams", "Sr.");  //error, too many parameters
var result32 = buildName2("Bob", "Adams");  //ah, just right

// 跟可选参数一样,默认值参数也必须跟在必须提供的参数后面
/*
	function buildName(firstName: string, lastName?: string) {

	and

	function buildName(firstName: string, lastName = "Smith") {
	
*/

// 他们共享一个类型"(firstName: string, lastName?: string)=>string"
// 默认值参数没有了默认值,那么就是一个可选参数

// -------- Rest Parameters(不定参数) --------
// 必须参数,可选参数和默认参数有个共同点,他们都是某一时间一个确定的参数
// 有时候,你想把参数作为一个组来使用,或则你可能不知道最终函数会使用多少个参数
// 在javascript中,你可以使用"arguments"变量来访问所有的参数,这个在每个函数体内部都可以访问到的变量

// 在typescript中,你可以把这些参数们放在一个变量里
function buildName3(firstName: string, ...restOfName: string[]) {
	return firstName + " " + restOfName.join(" ");
}

var employeeName = buildName3("Joseph", "Samuel", "Lucas", "MacKinzie");

// 不定参数被当作一捆可选参数来对待.
// 你可以把他们留空,也可以任意数量的使用
// 编译器会建立一个参数数组,数组名字是用...前缀来修饰的参数变量,你可以在你的函数中使用这个数组

// ...也用在定义函数类型
var buildNameFun: (fname: string, ...rest: string[]) => string = buildName;

// -------- Lambdas and using 'this'(lambdas表达式中使用'this') --------
// 对于来到javascript的程序员来说,'this'在javascript的函数中是怎么工作的,是个很常见的主题
// 事实上,要灵活使用javascript,那么学好javascript中的this很重要
// 因为typescript是javascript的一个超集,所以typescript开发者有必要去学习如何使用'this'和关注当它没有被正确使用的时候
// 如何在javascript中使用'this'可以写一大篇文章,甚至更多
// 这里,我们只关注一些基本的东西

// 在javascript中,'this'变量在函数被调用的时候才被确定.这使得它非常强大和具有定制的特性.
// 但是它带来的成本是,得知道函数运行的上下文
// 这让人非常的疑惑,举例来说,当函数被用做callback(回调函数)的时候

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

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

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

// 如果我们试着去运行这个例子,我们会得到一个错误,而不是期待的提醒盒子.
// 这是因为'this'在由'createCardPicker'所生成的函数中,已经指向了window,而不是deck这个object
// 这个错就是因为调用'cardPicker()'而来的
// 这里没有动态绑定,所以就this就指向了window(注意,在严格模式下,这样的情况下,this会是undefined而不是window)

// 我们可以修正这个,通过把这个返回的函数绑定一个正确的this
// 这样,不论他以后怎么用,他都会被正确指向初始的'deck'对象

// 为了修复this,我们使用lambdas表达式(()=>{}),而不是javascript里的表达式
// 这样就可以自动在创建函数的时候就捕获'this'变量,而不是在以后使用的时候才捕获

var deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        // Notice: the line below is now a lambda, allowing us to capture 'this' earlier
        return () => {
            var pickedCard = Math.floor(Math.random() * 52);
            var pickedSuit = Math.floor(pickedCard / 13);

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

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

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


// -------- Overloads(重载) --------
// javascript天生就是非常动态的语言
// 一点都奇怪,一个javascript函数就可以通过输入的参数的不同,从而返回不同类型的对象


var suits = ["hearts", "spades", "clubs", "diamonds"];

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") {
        var pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        var pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

var myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
var pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

var pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);


// 'pickCard'函数会基于用户不同的输入而返回两个不同的值
// 如果用户输入一个deck,函数会选择一个卡片
// 如果用户选择一个卡片,我们会告诉他什么卡片被选择了
// 但是我们如何把这个描述到类型系统里呢

// 答案是为一个函数提供多个函数类型来完成一个重载列表
// 编译器会使用这个列表来完成函数调用
// 让我们来创建重载列表,来描述我们的'pickCard'函数接受什么,返回什么

var suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard2(x: {suit: string; card: number; }[]): number;
function pickCard2(x: number): {suit: string; card: number; };
function pickCard2(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") {
        var pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        var pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

var myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
var pickedCard1 = myDeck[pickCard2(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

var pickedCard3 = pickCard2(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

// 通过这些该表,重载给与我们的'pickCard'函数以类型检测通过

// 未来让编译器获取正确的类型检测,它跟随javascript
// 它遍历重载表,从第一个重载开始通过提供的参数适配
// 如果找到适配的,就选择这个重载作为正确的重载
// 由于这个原因,你需要组织重载函数从最可能的到最不可能的排列顺序

// 请注意'function pickCard2(x): any'不是重载列表的某一项,所以重载列表只有2项:
// 输入一个object 和 输入一个数字
// 调用'pickCard'函数,输入其他类型参数,都会导致一个错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值