在写这篇文章之前,再次提醒一下

JavaScript 是大小写敏感的语言

// 'test', 'Test', 'TeSt' , 'TEST' 是4个不同的变量名

JavaScript中的变量,最重要的就是它的作用域, JS中变量的作用域其实就是函数作用域

比如我们的浏览器,在JavaScript中,它就是一个被实例化的window对象, 如果我们在window下面定义一个name字段, 那么name字段就具有window这个函数的作用域, 也就是说在整个window下面是可以访问这个name字段来获取它的值的

那现在我们在window下面定义一个函数Function MyFunc, 然后在这个函数里面定义了一个变量myName, 那么这个新定义的变量myName,它的作用域就是在函数MyFunc里面,也就是说只能在MyFunc函数中才可以访问myName变量

上面是在Chrome=》F12的Console中的一个例子. 刚写的时候一直报错,后来发现是我把function写成了Function,第一个字母大写了,就报错

现在我们换一种方式来写,看看会出现什么情况

这里,我们做了一下更改,在函数MyFunc中,在定义name变量之前,就使用console.log(name)语句来输出name, 如果没有在Chrome的console中执行的话,我可能会误以为它应该输出windowName. 因为这个时候,在函数MyFunc中还没定义name, 而全局变量(window对象下的变量)中已经定义了一个name="windowName", 所以我觉得它应该获取全局变量的值。但实际呢,我们可以看到,在实际中它输出的是undefined, 这是怎么个情况呢?

原来JavaScript的解析器在执行函数MyFunc的时候,第一件事情就是寻找这个函数中的所有局部变量,然后再执行后续语句, 所以它会先找到在函数MyFunc中定义了局部变量name, 但既然找到定义了局部变量name,为什么没有取到它的值funcName呢。 而是输出undefined呢。 这是因为解析器虽然先会寻找函数中的所有局部变量,但是在执行语句时,还是一条一条来执行的,所以解析器先找到函数MyFunc中已经定义了局部变量name,然后执行时,一条一条语句来执行

执行第一条语句 console.log(name);  => 此时,发现MyFunc函数中已经定义了局部变量name,但是此时还没有给它赋值,所以得到的就是undefined.

这一点也可以如下解释 JavaScript在解析代码的时候,都会搜索一下var声明的变量,然后将其声明提前。 javascript这个特性被非正式地称为 声明提升(hoisting)

所以,如下语句

alert(name);  // 这里弹出undefined
var name = "Luke";

它实际的解析过程如下:

var name;
alert(name); // 这里弹出undefined
name = "Luke";

那么,我们在换一种写法,来看看

在这里,我们首先定义了一个全局变量name="windowName", 然后在函数中,有一行语句

name="funcName"

注意,这里不是var name = "funcName", 也就是说它并不是在函数MyFunc中定义一个新的变量var name, 而是重新给全局变量name赋值,把它的值由windowName改为了funcName

所以JavaScript解析器在执行MyFunc函数这段代码时,先会去找这个函数内部的局部变量,发现没有。所以执行console.log(name);语句时,获取的就只能是全局变量name的值,这里就是windowName

接下来name="funcName", 将这个全局变量name的值由windowName改为了(重新赋值)funcName, 所以在MyFunc()之后执行console.log(name),输出的是全局变量name中的新值 funcName

我们再来看两个相似的例子

var testList = [,,];
function myShow(){ if(typeof testList === 'undefined')
{
testList = [];
} alert(testList.length); }; myShow();//结果 3

这里结果将输出3,因为在这个例子中,在函数myShow中没有定义testList(var testList), 所以JavaScript编译器找的就是全局变量testList,显然,它的length就是3

再看下面

var testList = [,,];
function myShow(){ if(typeof testList === 'undefined')
{
var testList = [];
} alert(testList.length); }; myShow(); //结果 0

注意到在函数myShow中,var testList是在myShow函数内部的if语句块中定义的。 这里要注意一下

JavaScript中没有块级作用域(由{}限定的作用域), 函数中声明的变量,无论在函数内部的哪一个地方声明,在这整个的函数中都是有定义的,都是一样的效果。

所以,这里,实际的执行过程是这样的:

var testList = [,,];
function myShow()
{
var testList; if(typeof testList === 'undefined')
{
testList = [];
} alert(testList.length); }; myShow(); //结果 0

所以在执行函数myShow时,发现定义了和全局变量testList同名的局部变量,此时还没给它赋值,所以确实是undefined, 所以执行if条件中的语句, testList = [], 它的length就是0

接下来,我们来看看函数定义式函数表达式之间的区别

函数定义式  function show(){} 采用这种函数定义式的声明方法,函数的定义会提前

函数表达式 var show = function(){}  函数的表达式,和普通的JavaScript变量的处理是一样的,也就是说函数声明会提前,但函数定义的位置不变

我们来看下面两个例子

alert(typeof myFunc);  //输出结果: function
function myFunc(){}

JavaScript在解析代码时, 像function myFunc()这种函数定义,和var声明定义变量一样,都会被提到前面。不同点在于:

var声明定义变量时,只会把var声明提到最前面,而定义还会停留在原处

而函数定义function myFunc(),函数声明和定义是一起的,都会同时被提到前面。所以上面的语句和下面执行是一样的

function myFunc(){}
alert(typeof myFunc); //输出结果: function

那我们再来看函数表达式的例子

alert(typeof myFunc); //输出: undefined
var myFunc = function(){};

这个执行起来就是下面这样的

var myFunc;
alert(typeof myFunc); //输出: undefined
myFunc = function(){};

我们再来看两个更容易出错更让人抓狂的例子

var myData = {name:'testName'};
function myData()
{
alert('testName function');
}
myData(); //输出 TypeError: object is not a function

这个例子非常抓狂吧,这里很容易以为是输出"testName function". 这个确实是很难辨别的一个例子

我们前面说过,var声明定义的变量会把声明提前,通过function myFunc(){}这种函数定义的函数会把函数的声明和定义同时提前. 这里就是这种情况,两个都会提前,那到底谁更前呢? 注意

var声明定义变量和函数定义同名时,函数的声明定义会在更前面,所以上面的JavaScript执行时会变成如下:

function myData()
{
alert('testName function');
}
var myData;
myData = {name:'testName'}; myData(); //输出 TypeError: object is not a function

可以看出,myData此时不再是函数,所以你不能采用这种方法myData(), 所以报错

那我们再看下面这个例子

var myData = {name:'testName'};
var myData = function()
{
alert('testName function');
}
myData(); //输出结果: testName function

在这里函数是通过函数表达式(而不是函数定义式)的var形式来声明定义的,而前面的变量var myData = {name:'testName'}也是var形式,两个优先级是一样的,所以就按代码顺序来,实际执行过程如下

var myData;
myData = {name:'testName'};
myData = function()
{
alert('testName function');
}
myData(); //输出结果: testName function

上面的例子就是这样,JavaScript的变量是一个非常绕的事情, 我们再来看一个例子

alert(name);  //报错: 对象未定义, 使用一个压根就不存在的变量,所以出错

age = ; //这里有错吗,这里不会报错,为什么呢?
// JavaScript中给一个未定义的变量赋值,就会自动创建一个全局变量, 这就
相当于 var age =

JavaScript学习系列2一JavaScript中的变量作用域的更多相关文章

  1. JavaScript学习系列博客_3_JavaScript中的变量、常量、标识符

    常量:就是改变不了的,也是可以直接使用的. 变量:可以改变的,不确定的. var =123456; 通过 var 声明一个变量,同时赋值给它 标识符:在JS中所有的可以自主命名的内容,都可以认为是一个 ...

  2. JavaScript学习系列博客_4_JavaScript中的数据类型

    JavaScript中有6种数据类型 一.基本数据类型 - String 字符串 JS中的字符串需要使用引号引起来双引号或单引号都行 但是要注意的是某种引号嵌套使用的话,需要加上 \ 转义.比如说我们 ...

  3. JavaScript学习系列博客_5_JavaScript中的强制类型转换

    -强制类型转换为String 1.方式1 调用被转换数据的toString()方法 number类型值.布尔类型值.都可以调用toString()方法强制转换.但是null值和undefined值不行 ...

  4. JavaScript学习系列博客_19_JavaScript中方法(method)

    方法 - 听了不少调用什么什么方法,所以方法究竟是啥东西? - 如果一个函数作为一个对象的属性保存,那么我们称这个函数时这个对象的方法,调用这个函数就说调用对象的方法(method). 创建一个对象 ...

  5. JavaScript学习系列博客_17_JavaScript中的函数的参数、返回值

    数的形参(形式参数) - 定义函数时,可以在()中定义一个或多个形参,形参之间使用英文逗号隔开:定义形参就相当于在函数内声明了对应的变量但是并不赋值,形参会在调用时才赋值. 函数的实参(实际参数) - ...

  6. JavaScript学习系列博客_16_JavaScript中的函数(Function)简介

    函数(Function) - 函数也是一个对象,也具有普通对象的功能 - 函数中可以封装一些代码,在需要的时候可以去调用函数来执行这些代码:当调用函数时,函数中封装的代码会按照顺序执行. - 使用ty ...

  7. JavaScript学习系列博客_14_JavaScript中对象的基本操作

    对象的基本操作 - 创建对象 - 方式一:使用new关键字调用的函数,是构造函数(constructor),构造函数是专门用来创建对象的函数. var obj = new Object(); - 方式 ...

  8. JavaScript学习系列博客_13_JavaScript中的对象(Object)简介

    对象 对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性.除了那5种基本数据类型,就是对象. 分类:1.内建对象- 由ES标准中定义的对象,在任何的ES的实现中都可以使用- 比如:Ma ...

  9. JavaScript学习系列博客_12_JavaScript中的break、continue关键字

    break关键字 -break关键字可以用来退出switch或循环语句 -不能在if语句中使用break和continue,但不是说if语句里面不能写break关键字,break关键字一定要包含在sw ...

随机推荐

  1. MySQL存储过程入门教程

    存储过程介绍 存储过程是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中.用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它.存储过程可由应用程序通过一个调用来执行,而且 ...

  2. EntityFramework 学习 一 Entity Relationships 实体的关系

    下面,我们学习Entity Framework怎么管理实体间的关系 Entity Framework支持三种关系:一对一的关系.一对多的关系.多对多的关系 前面我们创建SchoolDB的实体数据模型, ...

  3. matlab函数之imresize()

    B = imresize(A,scale) B = imresize(A,scale) 返回图像 B,它是将 A 的长宽大小缩放 scale 倍之后的图像.输入图像 A 可以是灰度.RGB 或二值图像 ...

  4. js操作url的常用函数

    1. //替换指定传入参数的值,paramName为参数,replaceWith为新值 function replaceParamVal(oUrl,paramName, replaceWith) { ...

  5. Mysql远程链接访问权限设置

    Host 'XXX' is not allowed to connect to this MySQL server 解决方案/如何开启MySQL的远程帐号 如何开启MySQL的远程帐号-1)首先以 r ...

  6. 分享知识-快乐自己:Liunx 安装 Zookeeper

    Zookeeper可以安装在windows中也可以安装在linux中! 但是我们的服务器基本都是在linux之上 安装我们的 zookeeper 注册中心! 安装成功之后  修改 nat 模式对应的端 ...

  7. 用express实现CORS跨域(上-简单请求)

    今天遇到了一个跨域请求登录验证的问题.所以有了尝试跨域的机会. 具体情景是,有一个登录界面写在名叫cas的站点上,但是相关的登录验证的后台接口是写在名叫earth的站点. 首先的反应是使用jsonp, ...

  8. 原生js监听input值改变事件

    哈哈哈,又来了,今天闲来无事,实验了下原生js监听input value值改变事件,下面就来说道说道: 本来写监听input值便获是用jquery的,之前的随笔写了,就是这个方法,地址:http:// ...

  9. Android基于socket的群聊程序

    在网上看了好多,但是感觉不是太简单就是只能单独聊,所以就自己写了个可以群聊的,直接上代码了 一.服务器端 这里用的MyEclipse作为服务器端 MyServerScoket.java package ...

  10. FFmpeg 基本用法

    FFmpeg FFmpeg 基本用法 本课要解决的问题 1.FFmpeg的转码流程是什么? 2.常见的视频格式包含哪些内容吗? 3.如何把这些内容从视频文件中抽取出来? 4.如何从一种格式转换为另一种 ...