---
title: "Solidity 中的 modifier 是什么？"
description: "Solidity Modifier 的作用、原理及使用场景"
---

# Solidity 中的 modifier 是什么？

[Solidity](https://www.alchemy.com/overviews/solidity) 是一种面向对象的高级语言，用于实现智能合约。在[学习 Solidity](https://ethereum.stackexchange.com/questions/55048/modify-struct-on-upgradeable-smart-contracts?rq=1) 的过程中，你会遇到修饰器（modifier），这是一种用于修改其他函数行为的特殊函数。

本文将解释 Solidity 修饰器的作用，介绍修饰器的类型，并展示如何使用它们。读完本文后，你将能够识别 Solidity 修饰器并理解如何使用它们。

## **Solidity 修饰器的作用是什么？**

**修饰器是一种特殊的[Solidity 函数类型](https://www.alchemy.com/overviews/solidity-functions)，用于修改其他函数的行为。** 例如，开发者可以使用修饰器在允许函数执行之前检查某个条件是否满足。

修饰器与函数类似，可以接受参数并具有返回类型。修饰器也可以链式组合，也就是说一个函数上可以有多个修饰器。

不过，修饰器只能修改合约逻辑，[不能修改合约的存储](https://ethereum.stackexchange.com/questions/55048/modify-struct-on-upgradeable-smart-contracts?rq=1)，这也包括结构体（struct）。修饰器减少了开发者需要编写的样板代码，可以让你的 Solidity 代码更具可读性。

### **一个 Solidity 函数上可以有多个修饰器吗？**

**可以，开发者可以在一个 Solidity 函数上使用多个修饰器。** 多个修饰器之间用逗号分隔。关于使用多个修饰器，有一点需要注意：修饰器的顺序很重要。列表中第一个修饰器会最先执行，第二个修饰器第二个应用，依此类推。

例如，如果你有一个修饰器用于检查用户是否已通过身份验证，另一个修饰器用于检查用户是否有权限查看某个资源，那么这些修饰器的应用顺序将决定用户是否能够查看该资源。

## **Solidity 修饰器有哪些不同类型？**

**Solidity 修饰器大致可以分为四类：门禁检查（gate check）、前提条件（prerequisite）、过滤器（filter）以及重入攻击防护（reentrancy attack prevention）。**

### **1. 门禁检查（Gate checks）**

“门禁检查”是一种在允许函数执行之前，检查某个条件是否为真的修饰器。

例如，你可能有一个函数允许用户从账户中提取资金，但在函数执行之前，开发者可能希望检查用户账户中是否有足够的资金进行提现。这种检查就被视为门禁检查修饰器。

门禁检查的另一个例子是：在允许用户查看某个资源之前，检查用户是否已通过身份验证的函数。

### **2. 前提条件（Prerequisites）**

“前提条件”是一种为函数执行搭建环境的修饰器，而不是检查某个条件是否为真。

例如，Solidity 开发者可能会使用一个函数，要求在调用时必须随之发送一定数量的以太币才能执行。在这种情况下，前提条件就是设置以太币余额的函数。

### **3. 过滤器（Filters）**

“过滤器”是一种检查某个条件是否为真的修饰器，如果为真，则允许函数执行。如果条件不为真，则函数不会执行。

与门禁检查不同的是，门禁检查即使条件为真，也不会自动允许函数执行，而过滤器在条件为真时会允许函数执行。

### **4. 重入攻击防护（Reentrancy attack prevention）**

**[重入攻击](https://www.alchemy.com/overviews/reentrancy-attack-solidity)是一种攻击方式，恶意行为者试图通过递归调用多次执行某个函数，以此进行攻击利用。**

例如，假设你有一个函数允许用户从账户中提取资金。发起重入攻击的攻击者可能会尝试多次调用该函数，以提取超过其账户实际余额的资金。

为了防止重入攻击，你可以使用一个修饰器来检查该函数是否正在被递归调用。如果是，则函数不会执行。

## **require 与 Solidity 修饰器之间是什么关系？**

**require 常常与修饰器互换使用，因为二者都可以让你在允许函数执行之前**[**检查某个条件是否为真**](https://www.alchemy.com/university/courses/solidity?a=313ac76819)。如果指定的条件不为真，编译器就会抛出错误。

例如，以下语句使用了 require 关键字，使得只有所有者才能与该函数进行交互：

require 和 Solidity 修饰器之间存在一些重要区别：

1. 修饰器可以用于为函数执行搭建环境（如前提条件的情况）
1. _require_ 只能用于检查某个条件是否为真
1. 修饰器可以被重写
1. _require_ 不能被重写

## **Solidity 处理修饰器的方式与 Vyper 相比有何不同？**

Vyper 是一种用于 Ethereum 智能合约开发的 Python 风格语言，它做出了某些权衡以提高安全性，包括[不使用修饰器](https://www.alchemy.com/overviews/solidity-vs-vyper)。相反，开发者应该在函数内使用内联检查和 assert，如果要修改智能合约，也应该再次将改动作为函数的一部分显式写出。

Vyper 移除修饰器功能的选择提高了智能合约的可审计性，因为读者不必在脑海中把修饰器套用到函数上，才能看出函数会做什么。

### **什么是修饰器重写？**

关键字 “virtual” 可用于表明某个修饰器可以在派生合约中被重写。例如，假设你有一个合约 “Base Contract”，其中有一个修饰器 “myModifier”。你还有一个合约 “Derived Contract”，它继承自 “Base Contract”。

如果 “Base Contract” 被标记为 “virtual”，那么它就可以在派生合约中被重写。这种做法常用于库的场景中，即某个合约允许被自定义。

### **修饰器如何与继承一起工作？**

**继承让你可以扩展合约的属性，在修饰器的语境下，继承使你可以添加新的修饰器，或重写已有的修饰器。** 这可以通过上一节提到的关键字 _virtual_ 来实现。

下面这个简单的实现展示了继承和修饰器是如何协同工作的：

在这个例子中，合约 B 继承自合约 A。两个合约都有一个名为 “X” 的修饰器。不过，在合约 B 中，该修饰器被标记为 “override”，表示它重写了合约 A 中的修饰器。

## **如何在 Solidity 中使用修饰器**

要使用修饰器，你首先需要在合约中定义修饰器函数。修饰器使用一个特殊符号 “\_;”，只有在满足修饰器条件时，才会插入函数体。

**下面的合约展示了如何使用修饰器：**

在上面的例子中，该合约有两个修饰器：“onlyOwner” 和 “costs”。

第一个修饰器检查 _msg.sender_ 是否为合约的所有者，第二个修饰器检查 _msg.value_ 是否大于或等于某个价格。

这两个修饰器都可以用在合约中的任何函数上。

## **继续学习 Solidity 修饰器**

修饰器是用于修改其他函数行为的函数。通过使用修饰器，你可以减少需要编写的样板代码，让你的 Solidity 代码更具可读性。

要[继续学习 Solidity 修饰器](https://www.alchemy.com/overviews/learn-solidity)并了解如何成为 Solidity 开发者，请报名参加 Alchemy University 为期 7 周的 Ethereum 开发者训练营。这门免费的[Solidity 速成课程](https://www.alchemy.com/university/courses/solidity?a=313ac76819)最初由 ChainShot 团队创建，是学习 Solidity 的最佳途径。如果开发者对开发完全是新手，Alchemy University 的**为期 3 周的 JavaScript 速成课程**是开始 Ethereum 训练营之前的绝佳前置课程。
