为什么要写这篇文章呢?主要是给自己提个醒,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. 理解Maven中的SNAPSHOT版本和正式版本

    Maven中建立的依赖管理方式基本已成为Java语言依赖管理的事实标准,Maven的替代者Gradle也基本沿用了Maven的依赖管理机制.在Maven依赖管理中,唯一标识一个依赖项是由该依赖项的三个 ...

  2. Javascript面向对象类文章目录

    1.javaScript的原型继承与多态性 2.JavaScript的继承实现方式 3.JS中 call() 与apply 方法

  3. [原] KVM 虚拟化原理探究(6)— 块设备IO虚拟化

    KVM 虚拟化原理探究(6)- 块设备IO虚拟化 标签(空格分隔): KVM [toc] 块设备IO虚拟化简介 上一篇文章讲到了网络IO虚拟化,作为另外一个重要的虚拟化资源,块设备IO的虚拟化也是同样 ...

  4. [EasyUI美化换肤]更换EasyUi图标

    前言 本篇文章主要是记录一些换EasyUI皮肤的过程,备忘.也欢迎美工大神各路UI给点好意见,EasyUI我就不介绍了,自行百度吧..(So..所以别问我是不是响应式..本身EasyUI就不是响应式. ...

  5. 代码的坏味道(16)——纯稚的数据类(Data Class)

    坏味道--纯稚的数据类(Data Class) 特征 纯稚的数据类(Data Class) 指的是只包含字段和访问它们的getter和setter函数的类.这些仅仅是供其他类使用的数据容器.这些类不包 ...

  6. C#调用C++代码遇到的问题总结

    最近在开发服务后台的时候,使用c#调用了多个c++编写的dll,期间遇到了一系列的问题,经过一番努力最后都一一解决了,在此做个总结,方便以后参考,毕竟这些问题也都是很常见的,主要有以下问题: 类型对照 ...

  7. Mybatis批量删除

    <delete id="deleteByStandardIds"> delete from t_standard_catalog where standard_id i ...

  8. Three.JS 从世界坐标系转换到屏幕坐标系

    var projector = new THREE.Projector(); var world_vector = new THREE.Vector3(0,0,1); var vector = pro ...

  9. Linux LVM逻辑卷配置过程详解

    许多Linux使用者安装操作系统时都会遇到这样的困境:如何精确评估和分配各个硬盘分区的容量,如果当初评估不准确,一旦系统分区不够用时可能不得不备份.删除相关数据,甚至被迫重新规划分区并重装操作系统,以 ...

  10. 驱动01.LED

    1.写出leds_open,leds_write函数2.1告诉内核这几个函数的存在?定义一个结构体file_operations2.2把这个结构体告诉内核?用register_chrdev(major ...