solidity 函数分为四种访问权限:
private : 私有函数。内部正常访问,外部无法访问,子类无法继承。
internal : 内部函数。内部正常访问,外部无法访问,子类可继承。
public : 公共函数。内部正常访问,外部正常访问,子类可继承。
external : 外部函数。内部不能访问,外部正常访问,子类可继承。
只有当函数有返回值的情况下,才需要使用 pure 、 view 、 constant
pure : 当函数返回值为自变量而非变量时,使用 pure
view : 当函数返回值为全局变量或属性时,使用 view
constant : 可以理解为 view 的旧版本,与 view 是等价的
如果一个函数有返回值,函数中正常来讲需要有 pure 、 view 或 constant 关键字,如果没有,在调用函数的过程中,需要主动去调用底层的call方法。
注: 如果一个函数中带了关键字 view 或 constant ,就不能修改状态变量的值。但凡是是带了这两个关键字,区块链就默认只是向区块链读取数据,读取数据不需要花gas,但是不花gas就不可能修改状态变量的值。写入数据或者是修改状态变量的值都需要花费gas。
public
public类型的状态变量和函数的权限最大,可供外部、子合约、合约内部访问
internal
internal类型的状态变量可供外部和子合约调用。
internal类型的函数和private类型的函数一样,智能合约自己内部调用,它和其他语言中的protected不完全一样
private
我们在person合约中尝试调用_name状态变量,你会发现,编译没法通过。
重写
子合约可以将父合约的public类型的函数,只能继承public类型的函数,只能继承public类型的函数,只能继承public类型的函数,我们可以直接调用继承过来的函数,当然,我们还可以对继承过来的函数进行重写。
每个合约都允许存在构造器,构造器是与合约同名的一个特殊函数,有且只能有一个,不允许重载。构造器将在合约创建时,执行一次,用于初始化一些配置。
pragma solidity ^0.4.0;
contract ContractConstructor{
uint public counter;
function ContractConstructor(){
counter++;
}
}
上述合约在创建成功后,counter的值将为1。说明合约在创建时,被调用了一次。构造器内,由于合约尚未初始化完成,故不能使用this来以external的方式来调用当前合约中的函数,
构造器还允许接收ether,只需要在构造器函数上增加payable关键字,下面是一个简单的例子:
pragma solidity ^0.4.0;
contract ConstructorEther{
function ConstructorEther() payable {}
function getBalance() constant returns (uint){
return this.balance;
}
}
在上面的例子中,ConstructorEther可以在创建时接收指定数量的ether。
细心的读者可能发现在deposit函数上有一个payable关键字,如果一个函数需要进行货币操作,必须要带上payable关键字,这样才能正常接收msg.value。
事件中,我们对_sender标记为了indexed。原因是因为为了提高查找事件的性能
contract CryptoExchange {
event Deposit(uint256 indexed _market, address indexed _sender, uint256 _amount, uint256 _time);
function deposit(uint256 _amount, uint256 _market) returns (int256) {
// perform deposit, update user’s balance, etc
Deposit(_market, msg.sender, _amount, now);
}
修改器(Modifiers)可以用来轻易的改变一个函数的行为。比如用于在函数执行前检查某种前置条件。
修改器是一种合约属性,可被继承,同时还可被派生的合约重写(override)
下面我们来看一段示例代码:
pragma solidity ^0.4.0;
contract owned {
function owned() { owner = msg.sender; }
address owner;
// This contract only defines a modifier but does not use
// it - it will be used in derived contracts.
// The function body is inserted where the special symbol
// "_;" in the definition of a modifier appears.
// This means that if the owner calls this function, the
// function is executed and otherwise, an exception is
// thrown.
modifier onlyOwner {
if (msg.sender != owner)
throw;
_;
}
}
contract mortal is owned {
// This contract inherits the "onlyOwner"-modifier from
// "owned" and applies it to the "close"-function, which
// causes that calls to "close" only have an effect if
// they are made by the stored owner.
function close() onlyOwner {
selfdestruct(owner);
}
}
contract priced {
// Modifiers can receive arguments:
modifier costs(uint price) {
if (msg.value >= price) {
_;
}
}
}
contract Register is priced, owned {
mapping (address => bool) registeredAddresses;
uint price;
function Register(uint initialPrice) { price = initialPrice; }
// It is important to also provide the
// "payable" keyword here, otherwise the function will
// automatically reject all Ether sent to it.
function register() payable costs(price) {
registeredAddresses[msg.sender] = true;
}
function changePrice(uint _price) onlyOwner {
price = _price;
}
}
修改器可以被继承,使用将modifier置于参数后,返回值前即可。
特殊_表示使用修改符的函数体的替换位置。
从合约Register可以看出全约可以多继承,通过,号分隔两个被继承的对象。
修改器也是可以接收参数的,如priced的costs。
使用修改器实现的一个防重复进入的例子。
pragma solidity ^0.4.0;
contract Mutex {
bool locked;
modifier noReentrancy() {
if (locked) throw;
locked = true;
_;
locked = false;
}
/// This function is protected by a mutex, which means that
/// reentrant calls from within msg.sender.call cannot call f again.
/// The `return 7` statement assigns 7 to the return value but still
/// executes the statement `locked = false` in the modifier.
function f() noReentrancy returns (uint) {
if (!msg.sender.call()) throw;
return 7;
}
}
默认情况下,当事件发生时,我们才开始监听。但当UI首次加载时,由于没有事件发生,没有对应的存款记录需要展示。所以为了展示用户的历史存款记录,我们需要从0号区块开始搜索用户的事件,这可以通过增加一个fromBlock参数来实现。
var depositEventAll = cryptoExContract.Deposit({_sender: userAddress}, {fromBlock: 0, toBlock: 'latest'});
depositEventAll.watch(function(err, result) {
if (err) {
console.log(err)
return;
}
// append details of result.args to UI
})
UI加载完成后,切记调用depositEventAll.stopWatching()来去掉监听。
最多可以有三个参数是索引的。举例来说,基本的代币行为有event Transfer(address indexed _from, address indexed _to, uint256 _value)。所以为了有效的监听转帐行为,你可以:
监听发送帐号tokenContract.Transfer({_from: senderAddress})。
监听收款帐号tokenContract.Transfer({_to: receiverAddress})。
同时监听发送,收款帐号tokenContract.Transfer({_from: senderAddress, _to: receiverAddress})。
block.coinbase (address): 当前块的矿工的地址
block.difficulty (uint):当前块的难度系数
block.gaslimit (uint):当前块汽油限量
block.number (uint):当前块编号
block.blockhash (function(uint) returns (bytes32)):指定块的哈希值——最新的256个块的哈希值
block.timestamp (uint):当前块的时间戳
msg.data (bytes):完整的calldata
msg.gas (uint):剩余的汽油
msg.sender (address):消息的发送方(当前调用)
msg.sig (bytes4):calldata的前四个字节(即函数标识符)
msg.value (uint):所发送的消息中wei的数量
now (uint):当前块时间戳(block.timestamp的别名)
tx.gasprice (uint):交易的汽油价格
tx.origin (address):交易发送方(完整的调用链)