IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

有趣的变量作用域-PHP中global和Javascript中的var关键字

Zen Space [睿] 2011-03-02 23:03:38 累计浏览 3,955 次
本机暂存

    昨天在网上看到几道有意思的PHP题, 下面这道题让我想起了对应的Javascript版本.

1

    2

    3

    4

    5

    6

    7

     function multiply($b) {

          $a = 100;    

          global $a;

          return $a * $b;

     }

     echo multiply(100);

    这段代码运行结果是什么呢? 别急着执行这段代码,先想想你的结果.然后再对比一下吧.

    我们看先看看global的定义 http://www.php.net/manual/en/language.variables.scope.php 这里也没有太为规范的解释.只是说可以通过global关键字来访问全局变量. 这里还涉及到一个类型转换的问题.

    大家都知道PHP脚本是编译为opcode逐语句执行的. 那么现在要一句语句解释就很容易了.

1

    2

    3

    4

    5

    6

    7

     function multiply($b) {

          $a = 100;     // 定义局部变量$a

          global $a;    // 访问全局变量$a, $a变量现在的是全局变量了

          return $a * $b;  // 返回$a和$b的乘积

     }

     echo multiply(100);

    这里可能比较困惑的的是现在变量$a到底是局部变量还是全局变量了.因为global在定义局部变量之后.所以$a变为了全局变量,而在最后输出结果的时候$a并没有值.所以最后在相乘的时候是 NULL * 100; 也就是0了;可能会有人有疑问, 后面只是把$a变为了全局变量, 他的值应该不变的啊. 让我通过下面的例子来看把:

1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

     function func($d) {

          $a = 100;

          global $a;

          var_dump(get_defined_vars());  // get_defined_vars()返回当前作用域的所有变量信息

     }

     func();

     array(2) {

       ["d"]=>

       int(0)

       ["a"]=>

       &NULL

     }

    变量a是NULL的一个引用,因为全局作用域内没有a这个变量. 所以即使在函数前面定义了一个a变量,但是它的值已经指向了全局作用域了.

     实际上 global关键字首先从全局符号表中查找变量名叫做a的变量,并把这个变量值设置为当前作用域的符号表中的a变量(更新了当前变量的值). 如果全局作用域内没有这个变量则会在全局作用域内增加这个变量, 实现代码见: $PHP_SRC/Zend/zend_vm_execute.h

1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST(int type, ZEND_OPCODE_HANDLER_ARGS) {

             // ...

             if (zend_hash_find(target_symbol_table, varname->value.str.val, varname->value.str.len+1, (void **) &retval) == FAILURE) {

                 switch (type) {

                     case BP_VAR_R:

                     case BP_VAR_UNSET:

                         zend_error(E_NOTICE,"Undefined variable: %s", Z_STRVAL_P(varname));

                         /* break missing intentionally */

                     case BP_VAR_IS:

                         retval = &EG(uninitialized_zval_ptr);

                         break;

                     case BP_VAR_RW:

                         zend_error(E_NOTICE,"Undefined variable: %s", Z_STRVAL_P(varname));

                         /* break missing intentionally */

                     case BP_VAR_W: {

                             zval *new_zval = &EG(uninitialized_zval);

                             Z_ADDREF_P(new_zval);

                             zend_hash_update(target_symbol_table, varname->value.str.val, varname->value.str.len+1, &new_zval, sizeof(zval *), (void **) &retval);

                         }    

                         break;

                     EMPTY_SWITCH_DEFAULT_CASE()

                 }    

             }    

          //...

     }

    看了这个解释大家可能觉得理所当然.一句一句执行的嘛. 看完了PHP中全局作用域的例子,咱们再看看类似的Javascript中的局部变量的版本吧

1

    2

    3

    4

    5

    6

    7

    8

    9

    10

var a = 1;

     function multiply(b)

     {

          a = 100;

          var a;

          return a * b;

     }

     alert(a);

     alert(multiply(100));

    那这段代码的输出将会是多少呢?

     如果还是同样的思路,结果可能是你的期望完全不一样的结果. 这里的var定义变量和php中global不是一样的东西, php中的global是会在运行时执行的.而Javascript中的var在运行之前就已经”处理”好了.在运行之前的”语法分析”(没有看过Javascript引擎的实现.姑且这么分把)过程中,multiply函数中出现了var a;则把变量a加到函数体内的”局部变量表”中了.在运行过程中并不会执行var a;这一句. 这也是Javascript”怪异”的地方.定义变量的位置并没有关系.所以在函数内定义局部变量最好放在函数体的前面.

    所以第一个alert输出的1, 函数的执行并没有改版全局范围内的a变量; 第二就没有什么问题了, 是10000;

同分类推荐文章

  1. 科技爱好者周刊(第 401 期):如何赚到10亿美元 (2026-06-26 08:05:38)
  2. 如何做决策 - 从 Go 的一个 issue 说起 (2026-06-26 08:00:00)
  3. Seven Player:Windows上播放115网盘视频的增强工具 (2026-06-09 00:06:47)

查看更多 开发者 文章 →

建议继续学习

  1. JQuery实现Excel表格呈现 (累计阅读 48,350)
  2. 使用gettext来支持PHP的多语言 (累计阅读 39,270)
  3. WordPress插件开发 -- 在插件使用数据库存储数据 (累计阅读 29,164)
  4. Paypal接口详细代码(PHP版,非API接口) (累计阅读 19,408)
  5. 深入理解Javascript之执行上下文(Execution Context) (累计阅读 18,404)
  6. 从输入 URL 到页面加载完成的过程中都发生了什么事情? (累计阅读 15,933)
  7. 图片动态局部毛玻璃模糊效果的实现 (累计阅读 14,849)
  8. 天朝第二代身份证号码的验证机制 (累计阅读 14,762)
  9. HTML 5 的data-* 自定义属性 (累计阅读 14,349)
  10. 分享一个JQUERY颜色选择插件 (累计阅读 14,223)