---
title: "Solidity 中的函数可见性是什么？"
description: "了解 Public、External、Internal 和 Private 函数可见性之间的区别"
---

# Solidity 中的函数可见性是什么？

在[Solidity](https://www.alchemy.com/overviews/solidity)智能合约编程中，有一项规定允许开发者根据指定的限制条件，决定谁或哪些其他智能合约可以调用自己的函数。这种定义[智能合约函数](https://www.alchemy.com/overviews/solidity-functions)如何被交互的能力，被称为**函数可见性**。

## **什么是函数可见性？**

Solidity 智能合约中的函数可见性功能用于确保函数在被指定时，其可访问级别（包括 public、external、internal 和 private）能够按照开发者的预期得到维持。

如果开发者没有使用正确的函数可见性类型，或者代码中没有指定任何可见性模型，合约默认的 public 可见性就会暴露出潜在的可利用安全漏洞。除了未指定函数可见性带来的安全隐患外，由于函数在缺少正确指示的情况下运行，智能合约也可能无法按预期工作。

### **谁可以调用智能合约函数？**

**有三种合约可以调用某个函数：主合约本身、继承自主合约的合约（DerivedContract），以及第三方合约（OutsideContract）。**

[调用智能合约](https://www.alchemy.com/overviews/solidity-call)是指访问智能合约中的内部代码及其包含的数据。这是一种从合约中获取数据的方式。

#### **1. 主合约（MainContract）**

这是 MainContract 调用其自身函数的语法：

#### **2. 继承自主合约的合约（DerivedContract）**

这是 DerivedContract 使用来自 MainContract 的派生函数的语法：

#### **3. 第三方合约（OutsideContract）**

这是 OutsideContract 从外部函数调用主合约的语法。

## **函数可见性修饰符与继承是如何协同工作的？**

**继承是指使用 “is” 关键字将一个合约的内容复制到另一个合约（即“派生合约”）中，而函数修饰符的可见性则根据合约被赋予的可见性类型，决定其对派生子合约的作用方式。**

函数可见性修饰符与继承之间的关系说明如下：

- 如果 _ParentContract_ 函数的修饰符为 **public**，_ChildContract_ 可以访问它。
- 如果 _ParentContract_ 函数的修饰符为 **internal**，_ChildContract_ 可以访问它。
- 如果 _ParentContract_ 函数的修饰符为 **private**，继承的 _ChildContract_ 无法访问它。
- 如果 _ParentContract_ 函数的修饰符为 **external**，继承的 _ChildContract_ 无法访问它。

## **函数可见性修饰符如何帮助优化 gas？**

由于 external 函数可见性修饰符的参数不会被保存到内存中，而是直接从 calldata 中读取，因此[智能合约消耗的 gas 更少](https://www.alchemy.com/overviews/solidity-gas-optimization)。相比之下，public 函数的输入参数会被保存到内存中，这会增加部署智能合约的 gas 成本。

## **函数可见性修饰符有哪些？**

函数可见性修饰符共有四种类型，按可访问程度从高到低排列为：public、external、internal 和 private。开发者应根据希望哪些对象能够查看和调用该函数，用正确的可见性来[修饰函数](https://www.alchemy.com/overviews/solidity-modifier)。

### **1. Public**

**public 函数可以被三种调用合约中的任意一种访问：主合约、派生合约以及第三方合约。函数默认即为 public。**

如上方代码示例所示，代码库中的任何合约都可以访问“`functionPublic`”函数，因为它的可见性为“public”。

### **2. External**

**external 函数是指只能被第三方调用的函数。** 在 external 函数可见性下，能够调用该函数的合约必须独立于主合约，不能是派生合约。

上方是一个正在执行某项任务的随机合约（`RandomContract`）。下面的代码片段是一个外部合约，正在调用 `RandomContract` 上的 external 函数。

### **3. Internal**

**internal 函数可以被主合约及其任何派生合约调用。** internal 函数可以从最初声明它们的主合约中访问，也可以被通过继承扩展自该主合约的合约访问。

### **4. Private**

**private 函数只能被指定它的主合约调用。** 按照通常的做法，private 函数最初会被采用，但如果作用范围比这个修饰符类型更广，就应该使用其他更合适的修饰符。

### **Solidity 中默认的函数可见性是什么？**

如果函数的可见性修饰符没有在代码中显式声明，该函数默认设置为 public 可见性。将函数可见性留空、依赖 Solidity 编译器使用默认可见性的做法并不推荐；有意地设置函数可见性可以使代码在审查时更清晰、更易于理解。

## **函数可见性修饰符与状态变量可见性修饰符有什么区别？**

**函数可见性修饰符与状态变量可见性修饰符的区别在于，状态变量没有 external 可见性修饰符这一选项。**

状态变量是指其值被永久存储在合约存储中的变量，合约存储在多次函数调用之间持久保存数据。与函数可见性修饰符类似，状态变量也有可见性修饰符，分别是 Public、Private 和 Internal。

### **状态变量可见性修饰符**

状态变量在程序的合约部分中声明。先指定变量的数据类型，然后再为该变量赋予可见性修饰符。

#### **1. Public**

具有 _public_ 修饰符的状态变量可以被应用程序中的任何合约访问。如果一个变量被声明为 _public_，那么存储在其中的数据可以被主合约、派生合约或外部合约读取。

#### **2. Internal**

用 _internal_ 修饰符声明的状态变量只能在定义它们的合约内部以及派生合约中被访问。第三方或其他外部合约无法访问被定义为 _internal_ 变量所存储的数据。

#### **3. Private**

如果一个状态变量是 _private_ 的，只有声明它的主合约才能调用它。_private_ 可见性修饰符限制了除主合约以外其他各方的访问，从而保护其所持有的数据。

### **Solidity 中默认的变量可见性是什么？**

当变量可见性未被定义时，默认值为 internal。最佳实践是将变量声明为 private，直到其作用范围需要扩大为止。

## **继续学习 Solidity**

本文向你介绍了 Solidity 函数可见性，并为你的下一个项目提供了相关说明和资源。

如果你刚接触 Solidity 语言，并期待构建你的第一个智能合约，欢迎在 Alchemy University 免费的[在线 Solidity 编程课程](https://alchemy.com/university/courses/solidity?a=d7f2788ae5)中占据一席之地。
