# WTF Solidity极简入门:3. 函数类型

By [0xAA](https://paragraph.com/@wtfacademy) · 2022-04-04

---

我最近在重新学solidity，巩固一下细节，也写一个“WTF Solidity极简入门”，供小白们使用（编程大佬可以另找教程），每周更新1-3讲。

**推特**：[@0xAA\_Science](https://twitter.com/0xAA_Science)

**WTF Academy社群：** [官网 wtf.academy](https://wtf.academy) | [discord](https://discord.wtf.academy) | [微信群申请](https://docs.google.com/forms/d/e/1FAIpQLSe4KGT8Sh6sJ7hedQRuIYirOoZK_85miz3dw7vA1-YjodgJ-A/viewform?usp=sf_link)

所有代码和教程开源在github: [github.com/AmazingAng/WTFSolidity](https://github.com/AmazingAng/WTFSolidity)

Solidity中的函数
------------

solidity官方文档里把函数归到数值类型，但我觉得差别很大，所以单独分一类。我们先看一下solidity中函数的形式：

    function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]
    

看着些复杂，咱们从前往后一个一个看（方括号中的是可写可不写的关键字）：

**1\. function**：声明函数时的固定用法，想写函数，就要以function关键字开头。

**2\. ()：圆括号里写函数的参数，也就是要输入到函数的变量类型和名字。** **3\. {internal|external|public|private}** ：函数可见性说明符，一共4种。没标明函数类型的，默认internal。 `public`: 内部外部均可见，并且自动给stoage变量生成 [getter 函数](https://docs.soliditylang.org/en/v0.8.13/contracts.html#getter-functions) 。 `private`: 只能从本合约内部访问，继承的合约也不能用。 `external`: 只能从合约外部访问（但是可以用this.f()来调用，f是函数名） `internal`: 只能从合约内部访问，继承的合约可以用。 **4.** **\[pure|view|payable\]**：决定函数权限/功能的关键字。payable很好理解，带着它的函数，运行的时候可以给合约转入ETH。pure和view的介绍见下一节。 **5\. \[returns ()\]：函数返回的变量类型和名称。** 到底什么是Pure和View？ 我刚开始学solidity的时候，一直不理解pure跟view关键字，因为别的语言没有类似的关键字。solidity加入这两个关键字，我认为是因为gas fee。合约的状态变量存储在链上，gas fee很贵，如果不改写这些变量，就不用付gas。调用pure跟view的函数是不需要付gas的。 我画了一个马里奥插画，帮助大家理解。在插画里，我把合约中的状态变量（存储在链上）比作碧池公主，三种不同的角色代表不同的关键字。 WTH is pure and view in solidity? **pure**，中文意思是“纯”，在solidity里理解为“纯纯牛马”。包含pure关键字的函数，不能读取也不能写入存储在链上的状态变量。就像小怪一样，看不到也摸不到碧池公主。 **view**，“看”，在solidity里理解为“看客”。包含view关键字的函数，能读取但也不能写入状态变量。类似马里奥，能看到碧池，但终究是看客，不能入洞房。 **不写pure也不写view**，函数既可以读取也可以写入状态变量。类似马里奥里的boss，可以对碧池公主为所欲为🐶。 代码 1. pure v.s. view 我们在合约里定义一个状态变量 number = 5。 // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract FunctionTypes{ uint256 public number = 5; 定义一个add() function，每次调用，输出 number + 1。 // 默认 function add() external{ number = number + 1; } 如果add()包含了pure关键字，例如 function add() pure external，就会报错。因为pure（纯纯牛马）是不配读取合约里的状态变量的，更不配改写。那pure函数能做些什么？举个例子，你可以给函数传递一个参数 \_number，然后让他返回 \_number+1。 // pure: 纯纯牛马 function addPure(uint256 \_number) external pure returns(uint256 new\_number){ new\_number = \_number+1; } 如果add()包含view，比如function add() view external，也会报错。因为view能读取，但不能够改写状态变量。可以稍微改写下方程，让他不改写 \_number，而是返回一个新的变量。 // view: 看客 function addView() external view returns(uint256 new\_number) { new\_number = number + 1; } 2. Internal v.s. External // internal: 内部 function minus() internal { number = number - 1; } // 合约内的函数可以调用内部函数 function minusCall() external { minus(); } 我们定义一个internal的minus()函数，每次调用使得number变量减1。由于是internal，只能由合约内部调用。我们再定义一个external的minusCall()函数，调用minus（）。这样，人们就能通过调用minusCall()来间接调用internal的minus()。 3. Payable // payable: 递钱，能给合约支付eth的函数 function minusPayable() external payable returns(uint256 balance) { minus(); balance = address(this).balance; } 我们定义一个external payable的minusPayable()函数，间接的调用minus()，并且返回合约里的ETH余额（this关键字可以让我们引用合约地址)。我们调用minusPayable()时，往合约里转入1个ETH。 我们可以在返回的信息中看到，合约的余额是1 ETH。 **注意：如果想在部署合约的时候往合约里转账，则需要给合约的构造函数声明**`payable`**。** constructor() payable {} 总结 在第三讲，我们介绍了solidity中的函数类型，比较难理解的是pure和view，在其他语言中没出现过。solidity拥有pure和view两种关键字主要是为了节省gas fee和控制函数权限，这两种方程都是不消耗gas的。下一讲我们会介绍引用和映射两种类型，并介绍更复杂的函数。

---

*Originally published on [0xAA](https://paragraph.com/@wtfacademy/wtf-solidity-3)*
