函数介绍
- 完成某一功能的程序代码集合,称为函数
- 把一段常用的功能代码,封装到一起,做成一个函数,可以提高代码的复用性和可维护性
- get_defined_functions() 返回所有已定义函数的数组
函数定义
- 函数的参数列表可以是多个,并且数据类型可以是任意类型
- 函数内定义的变量是局部变量,只能在函数内使用,与函数外同名变量不冲突
- 函数中要使用全局变量,使用global关键字
function 函数名(形参1,形参2,n){ // 参数可以不写
执行语句;
return; // 返回值写不写看需求,如不写默认返回null
}
函数运行原理
代码示例
$num1 = 10;
$num2 = 10;
//此处传参相当于 $n1=$num1; $n2=$num2 传值赋值
function getSum($n1, $n2){
$res = $n1 + $n2;
return $res;
}
$val = getSum($num1, $num2);
echo $val;
内存示例
变量作用域
函数中定义的变量是局部的,函数外不生效
代码示例
function test(){
$a = 100;
echo $a;
}
test();
echo $a; // Notice: Undefined variable: a
内存示例
global使用全局变量
函数中要使用全局变量,使用global关键字
global $a
本质上等价于$a = &$GLOBAL['a']
【重要】global $a
表示希望使用全局区的$a
- 如果没有全局变量
$a
,则会先创建全局变量$a=null
var_dump($GLOBALS)
查看所有的全局变量
代码示例
$a = 100;
function test(){
// global $a 表示希望使用全局区的$a
// 如果没有全局变量$a,则会先创建全局变量 $a=null
// global $a 本质上等价于 $a = &$GLOBAL['a'] 引用赋值
global $a;
$a = 10;
echo $a;
}
test(); // 10
echo $a; // 10
function test(){
// 如果没有全局变量$a,则会先创建全局变量 $a=null
global $a;
var_dump($a);
}
test(); // null
var_dump($a); // null
内存示例
流程分析
1.主栈$a
指向100
2.test()
,调用函数,在栈区新开辟一个新的空间新栈1,入新栈1
3.入栈后执行global $a
,即函数内希望使用一个全局变量$a
- 如果主栈没有
$a
则会先创建全局变量$a
值为null
,然后全局和局部(主栈和新栈)的变量名$a
指向全局区的null
- 如果有则在新栈1创建
$a变量名
指向全局区100
4.$a = 10
,即新栈1中$a
将全局区的100改为10
5.函数没写返回值,默认返回null,函数结束,出新栈1,新栈1销毁,新栈1中的数据和与新栈1相关的箭头也都没有了
7.echo $a
,主栈中的$a
仍指向10,输出10
函数中的静态变量
- 静态变量的特点,只会被初始化一次
- 静态区数据在函数执行完后,不销毁,即使不再有变量指向(这是一个特例),直到整个进程结束才销毁
代码示例
function test(){
$b = 10;
static $a = 0;
$a++;
echo $a;
}
test();
test();
test();
内存示例
- 第一次调用
test()
,$a
初始化指向静态区0,$a++
,静态数据变为1,调用结束,新栈1销毁,静态区并不销毁 - 第二次调用
test()
, a 不 再 初 始 化 为 0 , 直 接 找 到 静 态 数 据 1 , ‘ a不再初始化为0,直接找到静态数据1,` a不再初始化为0,直接找到静态数据1,‘a++`,静态数据变为2,调用结束,新栈2销毁,静态区并不销毁 - 第三次调用
test()
, a 不 再 初 始 化 为 0 , 直 接 找 到 静 态 数 据 2 , ‘ a不再初始化为0,直接找到静态数据2,` a不再初始化为0,直接找到静态数据2,‘a++`,静态数据变为3,调用结束,新栈2销毁,静态区仍不销毁,直到整个进程结束才销毁
函数传参
函数默认参数的值
// $b的默认值为20,如果调用时不填写,以默认值为准,如果填写,以最新的为准
function test($a, $b=20){
echo $a + $b,'<br>';
}
test(10); // 30
test(10,1); // 11
传值赋值
函数参数默认通过值传递
// 函数传参默认值传递 $a = $num
function test($a){
echo ++$a,'<br>';
}
$num = 10;
test($num); // 10
echo $num; // 11
引用赋值
可以在形参前加&修改为引用传递
// 函数传参方式修改为引用传递 $a = &$num
function test(&$a){
echo ++$a,'<br>';
}
$num = 10;
test($num); // 11
echo $num; // 11
内存示例
函数传参 – 不定长参数
- func_num_args — 返回传递给函数的参数数量
- func_get_arg — 返回参数列表的某一项
- func_get_args — 返回一个包含函数参数列表的数组
代码示例
函数的参数可以是基本数据类型任意
function fun(){
return 'hello';
}
function test(){
$res1 = func_num_args();
$res2 = func_get_arg(1);
$res3 = func_get_args();
var_dump($res1,$res2,$res3);
}
test(1,5,'hi',array(1,2),fun());
练习题
// 传入参数个数不确定,求和
function getSum(){
$arr = func_get_args();
$sum = 0;
foreach ($arr as $value){
$sum += $value;
}
return $sum;
}
echo getSum(1,2,5);
echo getSum(1,-2,5,6);
内部函数
- 函数和类声明是全局有效的,不管是在多深的函数下声明的类或函数
- 函数调用时才会会执行函数内部代码 (调用时才入栈执行)
函数内部可以定义函数,供函数内部使用
function test(){
function fun(){
echo 'hello world';
}
// 内部直接调用内部函数
fun();
}
test(); // 'hello world'
函数调用时才会会执行函数内部代码
function test(){
function fun(){
echo 'hello world';
}
}
test(); // 这里调用之后 fun()才被定义
fun(); // 'hello world'
可变函数
- PHP 支持可变函数的概念
- 如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它
- 可变函数可以用来实现包括回调函数,函数表在内的一些用途
function test(){
echo 'hello world';
}
$a = 'test';
$a(); // 先解析$a, test(),若有则执行,若没有则报错
函数面试题
写一个函数,接受两个参数$a $b
和一个函数$fun
,要求通过$fun求$a $b的和
// 新浪面试题:写一个函数,接受两个参数$a $b和一个函数$fun,要求通过$fun求$a $b的和
// 方案一:最容易理解
function test($n1,$n2,$n3){
echo $n3;
}
function getSum($n1,$n2){
return $n1 + $n2;
}
$a = 1; $b = 2;
test($a, $b, getSum($a, $b));
==============================
// 方案二:使用可变函数知识点
function test($n1,$n2,$n3){
echo $n3($n1,$n2);
}
function getSum($m1,$m2){
return $m1 + $m2;
}
$a = 1; $b = 2; $funName = 'getSum';
test($a,$b,$funName);
=============================
// 方案三:方案二的改进使用
function test($n1,$n2,$n3){
function getSum($n1,$n2){
return $n1 + $n2;
}
return $n3($n1,$n2);
}
$a = 1; $b = 2; $funName = 'getSum';
echo test($a, $b, $funName);
匿名函数
- 匿名函数,也叫闭包函数,允许临时创建一个没有指定名称的函数
- 最经常用作回调函数参数的值
- 闭包函数也可以作为变量的值来使用,最后要加上分号:
- 匿名函数其实是一个对象数据类型
代码示例
$fun1 = function ($n1, $n2){
return $n1 + $n2;
}; // 这里是加;的
// 调用匿名函数
echo $fun1(1,3);
var_dump($fun1); // object
回调函数
调用某个函数A,而这个函数内又会调用你实现的另一个函数B,那么函数B就是所谓的回调函数
function test($n1,$n2,$n3){
echo $n3($n1,$n2);
}
function getSum($m1,$m2){
return $m1 + $m2;
}
$a = 1; $b = 2; $funName = 'getSum';
test($a,$b,$funName);
匿名函数用作回调函数参数的值(开发中用的挺多)
function test($n1, $n2, $n3){
$sum = $n1 + $n2;
$res = $n3($sum); // 回调
return $res;
}
echo test(1, 3, function ($v){return $v*2;}); // 第三个参数为匿名函数
递归函数
- 递归函数即自调用函数,在函数体内部直接或间接的自己调用自己
- 函数体中会附加一个条件判断,以判断是否需要执行递归调用,并且在特定的条件下终止函数的递归
代码示例
function abc($n){ // 此处是传值赋值
if ($n>2){
abc(--$n); // $n = $n-1; abc($n)
}
echo $n,'<br>';
}
abc(4); // 2 2 3
// 思考: 如果是 $n-- 会怎么? 无限递归
内存示例
递归练习1 :1 ~ n的和
function sum($n){
if ($n == 1){
return 1;
}
return $n+sum($n-1);
}
echo sum(100);
递归练习2 :斐波那契数列
// 斐波那契数列 1 1 2 3 5 8 13
function test($n){
if ($n == 1 || $n == 2){
return 1;
}else{
return test($n-1) + test($n-2);
}
}
$res = test(5);
echo $res;