技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> PHP --> 有趣的变量作用域-PHP中global和Javascript中的var关键字

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

浏览:2866次  出处信息

    昨天在网上看到几道有意思的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. javascript的词法作用域    (阅读:3157)
  2. 闭包与作用域    (阅读:2820)
  3. mysql 查看服务器端配置记得加global    (阅读:2557)
  4. 认识javascript中的作用域和上下文    (阅读:2509)
  5. javascript作用域和作用域链    (阅读:2406)
  6. 样式的作用域──页面重构中的模块化设计(一)    (阅读:2257)
  7. JavaScript 函数、作用域和继承    (阅读:2259)
  8. Javascript作用域原理    (阅读:2093)
  9. 从另外两道题说起    (阅读:1838)
  10. JavaScript的作用域和提升机制    (阅读:1784)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1