在写这篇文章之前,再次提醒一下
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 =