JS里面,一切对象。说到对象,就有属性。JS里面的属性分为两种:一种是可以通过代码可以访问的,一种是js内部访问的,其中,内部访问的,有一个[[scope]]值得注意,特别是是javascript的函数。
函数的作用域链():包含函数创建的作用域中的对象集合,这个集合被称作是作用域链。
执行上下文(EC):本身也是一个对象,用于定义对象的执行环境。
活动对象:活动对象包含了EC+对象的执行上下文。
全局对象总是放在作用域链的末端,这样一来,在一个函数里面,强烈建议使用局部变量,特别是针对多次使用的全局变量,在函数里面也最好使用局部变量进行索引。
下面结合一个具体的例子来说明:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Javascript测试</title>
<script type="text/javascript">
var globalNum = 10;
function modifyData(){
globalNum = 200;
return;
function globalNum(){
//Do Nothing
}
}
modifyData();
alert(globalNum);
</script>
</head>
<body>
</body>
</html>
这个页面打开弹出的数字是多少呢? 10? 200?
---------------------------------------------------------------------------------------------------------------------------
这个问题考查几个点:
1)函数声明函数的创建机制问题
函数声明的函数,其在进入上下文时已经创建,这也是为什么我们可以把一个函数声明写在调用后面,函数还能正常工作的原因;另外,我们可以通过函数声明的函数名称调用函数也是因为,函数名称其实就是对于函数对象的引用,即指向函数,如果将alert里面的内容换成 modifyData,那么弹出的对话框上面的内容就会是函数声明的内容,所以,从代码的角度来看,函数声明的作用就是定义了一个对象变量,其对象的名称是 函数名称。同时,这个跟是否执行被执行无关,可以认为在执行前就会有的一个初始化工作,同时,也可以引申一点,JS不会针对函数申明的函数在代码中是否会被执行而决定是否创建。
2) 对象的作用域链问题
前面说到任何对象都有一个作用域链,且全局对象对象集合总是在作用域链的末端,这种设计是有用意的,这样的设计对于处理同名对象就很方便。在运行时,任何一变量都需要解析,这个解析就是通过活动对象,根据活动对象中的作用域链,依次从前向后找,找到就返回,这样一来,涉及到同名的对象,总是局部变量优先。
3 变量的类型是由其值决定的
一个变量被赋值一个数字,那么它就是一个number类型,被赋值一个函数,那么它就是一个function
依据这两点,上面的答案就很明显了:
1) 首先看modifyData函数,我们看到有一个函数声明,这样一来,它内部本身就有一个globalNum的函数对象,那么在执行 globalNum=200这条语句时,就会执行查找,正好,我们找到了一个globalNum,那么就会将其改写成200,但是,记住,当前modifyData中,globlNum变成了一个number类型变量,同时这个globalNum只是针对modifyData而言的
2) 输出函数alert里面,活动对象已经变了,那么全局对象global就是之前申明的,这样一来,最终输出的还是原来的对象。
为了验证modifyData里面的变量变化,可以将代码稍稍修改如下:先调用函数,再改写,再调用函数
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Javascript测试</title>
<script type="text/javascript">
//var globalNum = 10;
function modifyData(){
globalNum();
globalNum = 200;
globalNum();
return;
function globalNum(){
alert("I am function");
realGlobal = 11;
}
}
modifyData();
alert(globalNum); //这里会报错,那个globalNum并不是所谓的 没有var修改的局部变量,而是在执行前已经创建好的变量,
alert(realGlobal); //这个能够打印出来,在函数里面没有var修饰的变量是全局的!!!!
</script>
</head>
<body>
</body>
</html>
稍稍总结一下: 1 在页面初始化时,创建一个在modifyData作用域内的全局变量 globalNum, 由于其位于函数内部,所以,我们称之为局部变量,同时这个函数是基于modifyData这个函数的,是一个局部函数,只能在函数内部执行
2 在modifyData中,所有的globalNum都是那个局部变量,那个变量先是一个function类型,可以使用一对小括号进行调用,而被赋值后,它变成了一个number类型,但是这个过程并没有创建一个新的变量,所以,其访问作用域还是在函数内部。
3 一个函数里面的变量可以在外部调用,需要满足三个条件:1) 没有同名的函数声明 2)没有var修饰 3)函数执行过
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Javascript测试</title>
<script type="text/javascript">
function modifyData(){
globalNum();
globalNum = 200;
alert(typeof globalNum);
function globalNum(){
relativeGlobal = 20;
alert("globalNum is called!");
}
}
modifyData();
alert(relativeGlobal);
//globalNum();
modifyData();
</script>
</head>
<body>
</body>
</html>