Solidity 中的函数
1、solidity 函数定义
function 函数名(参数列表) 修饰符 returns (返回类型){}
function eatHamburgers(string _name, uint _amount) {
// 这是一个名为 eatHamburgers 的函数,它接受两个参数:一个 string类型的 和 一个 uint类型的。现在函数内部还是空的
// 习惯上函数里的变量都是以 _ 开头 (但不是硬性规定) 以区别全局变量。
}
2、solidity 函数的可见性定义符(4个)
Solidity 定义的函数的属性默认为公共(public)。 这就意味着任何一方 (或其它合约) 都可以调用你合约里的函数。
pragma solidity ^0.4.14;
contract HelloWorld{
function getNum(uint num) {
}
// 此函数等价于
function getNum(uint num) public {
}
}
显然,不是什么时候都需要这样,而且这样的合约易于受到攻击。 所以将自己的函数定义为私有是一个好的编程习惯,只有当你需要外部世界调用它时才将它设置为公共。
如何定义一个私有的函数呢?
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
//这意味着只有我们合约中的其它函数才能够调用这个函数,给 numbers 数组添加新成员。
可以看到,在函数名字后面使用关键字 private 即可。和函数的参数类似,私有函数的名字用(_)起始。
函数的可见性定义符还有internal 和 external
- public:在外部和内部均可见(创建存储/状态变量的访问者函数)
- private:仅在当前合约中可见
- external: 只有外部可见(仅对函数)- 仅仅在消息调用中(通过this.fun)
- internal: 只有内部可见
3、solidity 函数的返回值
要想函数返回一个数值,按如下定义:
string greeting = "What's up dog";
function sayHello() public returns (string) {
return greeting;
}
Solidity 里,函数的定义里可包含返回值的数据类型(如本例中 string)。
-----------------------------------------------------------
// 第二种返回写法
string greeting = "What's up dog";
function sayHello() public returns (string aa) {
aa = greeting;
}
// 不写return 关键字那么就得在返回列表中制定返回名
4、solidity 函数的修饰符(3个)
- view 修饰符
上面的函数实际上没有改变 Solidity 里的状态,即,它没有改变任何值或者写任何东西。这种情况下我们可以把函数定义为 view, 意味着它只能读取数据不能更改数据,老版本为 constant ,但是现在不能写了:
function sayHello() public view returns (string) { }
因此用 view 标记一个函数,意味着告诉 web3.js,运行这个函数只需要查询你的本地以太坊节点,而不需要在区块链上创建一个事务(事务需要运行在每个节点上,因此花费 gas)。
- pure 修饰符
Solidity 还支持 pure 函数, 表明这个函数甚至都不访问应用里的数据,
function _multiply(uint a, uint b) private pure returns (uint) {
return a * b;
}
这个函数甚至都不读取应用里的状态 — 它的返回值完全取决于它的输入参数,在这种情况下我们把函数定义为 pure.
可能很难记住何时把函数标记为 pure/view。 幸运的是, Solidity 编辑器会给出提示,提醒你使用这些修饰符。这两种在被从合约外部调用的时候都不花费任何gas
- payable 修饰符 --- 可支付
允许函数在调用同时接收Ether,payable 修饰符是让 Solidity 和以太坊变得如此酷的一部分 —— 它们是一种可以接收以太的特殊函数。
当你在调用一个普通网站服务器上的API函数的时候,你无法用你的函数传送美元——你也不能传送比特币
但是在以太坊中, 因为钱 (以太), 数据 (事务负载), 以及合约代码本身都存在于以太坊。你可以在同时调用函数 并付钱给另外一个合约。
这就允许出现很多有趣的逻辑, 比如向一个合约要求支付一定的钱来运行一个函数
contract OnlineStore {
function buySomething() external payable {
// 检查以确定0.001以太发送出去来运行函数:
// msg.value 是一种可以查看向合约发送了多少以太的方法,另外 ether 是一个內建单位。
require(msg.value == 0.001 ether);
// 如果为真,一些用来向函数调用者发送数字内容的逻辑
transferThing(msg.sender);
}
}
如果一个函数没标记为payable, 而你尝试利用上面的方法发送以太,函数将拒绝你的事务。
5、solidity 函数的自定义修饰符
我们先来写个onlyOwner自定义的函数修饰符
/**
* @dev 调用者不是‘主人’,就会抛出异常
*/
pragma solidity ^0.4.14;
contract Ownable {
address public owner;
// 构造函数,当合约部署的时候就会自动执行这个函数,并且只执行一次
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
//注意! `onlyOwner`上场 :
function hello() public onlyOwner returns(bool){
return ture;
}
}
注意 hello 函数上的 onlyOwner 修饰符。 当你调用 hello 时,首先执行 onlyOwner 中的代码, 执行到 onlyOwner 中的 _; 语句时,程序再返回并执行 hello 中的代码。
我们再写个onlyOwner可以传参的自定义的函数修饰符
/**
* @dev 调用者不是‘主人’,就会抛出异常
*/
pragma solidity ^0.4.14;
contract Ownable {
address public owner;
// 构造函数,当合约部署的时候就会自动执行这个函数,并且只执行一次
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner(uint i) {
require(msg.value== i);
_;
}
// 注意! `onlyOwner`上场 :
function hello(uint i) public onlyOwner(i) returns(bool){
return ture;
}
}
当我们调用hello 的时候需要传递 参数 i 进去, 传进去后先调用 修饰符 onlyOwner函数,若require()内的结果不是true,则抛出异常,为true 才会继续执行下去
目前我们可以给函数添加多种修饰符了,这些修饰符可以同时作用于一个函数定义上:
function test() external view onlyOwner anotherModifier { /* ... */ }