为什么要写这篇文章呢?主要是给自己提个醒,js的水很深,需要小心点儿才能趟过去,更何况自己不是专业人士,那就得更加小心了。

看下面的js代码:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>javascript中变量的作用域</title>
</head>
<body> <script>
for (var i = 0; i < 3; i++) {
console.log(i);
}
console.log('第一个for循环结束');
console.log(i); console.log('第二个for循环开始'); for (; i > 0; i--) {
console.log(i);
}
console.log('第二个for循环结束');
console.log(i);
</script>
</body>
</html>

你猜它的输出是什么?

为何循环结束后,i的值仍然存在呢?js循环代码块内部变量的作用域怎么跑到外面来了?作为js小白的我,竟然会问这么傻的问题。答案是,js没有块级作用域,像for、if、switch等控制结构形成的代码块内部声明的变量其实都是全局变量。对于像c这种有块级作用域的语言,在for语句执行完毕后,循环变量会被销毁,下面是c语言的for循环:

 #include "stdio.h"

 int main() {
for (int i = ; i < ; i++) {
printf("%d\n", i);
} printf("%d\n", i); // 这里编译时会报错 error: use of undeclared identifier 'i'
}

编译时报错如下:

而对于javascript这种没有块级作用域的语言,即使在for循环结束之后,循环变量i也是会存在于循环外部的“全局”当中,因为这个循环变量其实就是“全局变量”。这种描述不够准确,javascript当中一个重要的概念是执行环境(execution context),简称环境。每个环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。全局执行环境是最外围的一个执行环境,比如在浏览器中,全局执行环境就是window对象,所以,所有的全局变量都是作为window对象的属性和方法创建的。比如上面的循环变量i,应该可以访问window.i:

下面只写出js代码:

   for (var i = 0; i < 3; i++) {
console.log(i);
}
console.log('第一个for循环结束');
console.log(i);
console.log('访问window.i');
console.log(window.i); console.log('第二个for循环开始'); for (; i > 0; i--) {
console.log(i);
}
console.log('第二个for循环结束');
console.log(i); console.log('访问window.i');
console.log(window.i);

结果如下:

那么,javascript中的局部变量该怎么定义呢?(或者说如何定义一个变量,这个变量不会被影响到全局呢),答案是函数。js中除了全局执行环境外,还有一个执行环境:函数执行环境,也可以把它叫做局部执行环境。比如下面这段代码:

   var j = 0;
function test() {
var j = 1;
return j;
} console.log(j);

输出结果为0,证明函数内部的j没有污染到外部的j,函数内部的j只在函数的执行环境内被访问。

我又想到php跟js一样都是脚本语言,那么php是不是也没有块级作用域呢?看下面的代码:

 <?php

 if (true) {
$i = 7;
}
echo $i , PHP_EOL; for ($i = 0; $i < 3; $i++) {
echo $i , PHP_EOL;
}
echo '第一个for循环结束' , PHP_EOL;
echo $i , PHP_EOL; echo '第二个for循环开始' , PHP_EOL;
for (; $i > 0; $i--) {
echo $i , PHP_EOL;
}
echo '第二个for循环结束' , PHP_EOL; echo $i , PHP_EOL;

输出结果如下:

嗯,没错,它也没有块级作用域。其实php手册中很“含蓄”的表达了这一点:“变量的范围即它定义的上下文背景(也就是它的生效范围)。大部分的 PHP 变量只有一个单独的范围。这个单独的范围跨度同样包含了 include 和 require 引入的文件。......在用户自定义函数中,一个局部函数范围将被引入。任何用于函数内部的变量按缺省情况将被限制在局部函数范围内。”

我又想到了php的for循环,我记得php手册中对于foreach循环的描述中有这么一段话“Warning:数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。”,这个恐怕也是因为它没有块级作用域的原因,但手册中for循环中没有提到这一点,这有些奇怪。看来,php的水也很深。

其实,javascript的ES6规范中已经引入了let(还有const)变量标识符,利用let可以让js的变量拥有块级作用域,看下面的代码:

   for (let i = 0; i < 3; i++) {
console.log(i);
}
console.log('第一个for循环结束');
console.log(i);

输出结果如下:

看来js正在变得越来越规范。

ps:js的执行环境、php的局部变量和全局变量,以后还要仔细学习下。

参考:

1,javascript高级程序设计

2,php手册

javascript的变量作用域--对比js、php和c的for循环的更多相关文章

  1. [转]深入理解JavaScript的变量作用域

    1.JavaScript的作用域链 2.函数体内部,局部变量的优先级比同名的全局变量高. 3.JavaScript没有块级作用域. 4.函数中声明的变量在整个函数中都有定义. 5.未使用var关键字定 ...

  2. 深入理解JavaScript的变量作用域(转载Rain Man之作)

    在学习JavaScript的变量作用域之前,我们应当明确几点: JavaScript的变量作用域是基于其特有的作用域链的. JavaScript没有块级作用域. 函数中声明的变量在整个函数中都有定义. ...

  3. 深入理解JavaScript的变量作用域

    在学习JavaScript的变量作用域之前,我们应当明确几点: JavaScript的变量作用域是基于其特有的作用域链的. JavaScript没有块级作用域. 函数中声明的变量在整个函数中都有定义. ...

  4. JavaScript函数变量作用域

    变量作用域 在JavaScript中,用var申明的变量实际上是有作用域的. 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量. 如果两个不同的函数各自申明了同一变 ...

  5. Javascript之变量作用域

    分析: 无论是强类型语言c#.c++.java等语言,还是弱类型语言如Javascript,所有变量可以抽象为两种类型,即局部变量和全局变量. 全局变量:整个作用域可见. 局部变量:局部可见,退出作用 ...

  6. JavaScript的变量作用域

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. 浅谈javascript中变量作用域和内存(1)

    先理解两个概念:基本类型和引用类型的值 1.基本类型和引用类型的值 (1)定义: 基本类型:指简单的数据段,比如按值访问的js五种基本数据类型undefined.null.boolean.number ...

  8. 浅谈javascript中变量作用域和内存(2)

    1.无块级作用域 javascript没有块级作用域,这会让其他程序员在理解js代码上很痛苦.在其他很多语言,比如C,大括号括起来的代码块都有自己的作用域 举个例子 if(true) { var na ...

  9. JavaScript 关于变量作用域的一道面试题

    ShineJaie 原创,转载请注明出处. 昨晚在一个交流群里看到有位网友提了一个他的面试题求助答疑.刚好我也有看到,就对这个问题思考了一下,觉得这道题对理解 JavaScript 作用域还是很有帮助 ...

随机推荐

  1. 游戏编程系列[1]--游戏编程中RPC协议的使用[3]--体验

    运行环境,客户端一般编译为.Net 3.5 Unity兼容,服务端因为用了一些库,所以一般为4.0 或往上.同一份代码,建立拥有2个项目.客户端引用: WindNet.Client服务端引用: OpL ...

  2. HTML5 语义元素(二)文本内容

    上一篇介绍的是关于页面结构方面的语义元素,本篇介绍文本内容方面,包含:<bdi>.<details>.<summary>.<mark>.<outp ...

  3. HDU1671——前缀树的一点感触

    题目http://acm.hdu.edu.cn/showproblem.php?pid=1671 题目本身不难,一棵前缀树OK,但是前两次提交都没有成功. 第一次Memory Limit Exceed ...

  4. JS判断鼠标进入容器方向的方法和分析window.open新窗口被拦截的问题

    1.鼠标进入容器方向的判定 判断鼠标从哪个方向进入元素容器是一个经常碰到的问题,如何来判断呢?首先想到的是:获取鼠标的位置,然后经过一大堆的if..else逻辑来确定.这样的做法比较繁琐,下面介绍两种 ...

  5. Ajax实现原理,代码封装

    都知道实现页面的异步操作需要使用Ajax,那么Ajax到是怎么实现异步操作的呢? 首先需要认识一个对象 --> XMLHttpRequest 对象 --> Ajax的核心.它有许多的属性和 ...

  6. VS2015在创建项目时的一些注意事项

    一.下面是在创建一个新的项目是我最常用的,现在对他们一一做一个详细的介绍: 1.Win32控制台应用程序我平时编写小的C/C++程序都用它,它应该是用的最多的. 2.名称和解决方案名称的区别:名称是项 ...

  7. input type='file'上传控件假样式

    采用bootstrap框架样式 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> &l ...

  8. Maven(一)linux下安装

    1.检查是否安装JDK,并且设置了环境变量(JAVA_HOME): echo $JAVA_HOME java -version 运行结果: 显示jdk的安装路径,和java的版本,如: #jdk路径 ...

  9. 一条Sql语句分组排序并且限制显示的数据条数

    如果我想得到这样一个结果集:分组排序,并且每组限定记录集的数量,用一条SQL语句能办到吗? 比如说,我想找出学生期末考试中,每科的前3名,并按成绩排序,只用一条SQL语句,该怎么写? 表[TScore ...

  10. centos 6.5 升级php

    1>追加CentOS 6.5的epel及remi源. # rpm -Uvh http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/x86_64/epel-re ...