变量的声明
- 变量声明使用关键字var
如果未在var声明时给定初始值,则未undefined
动态语言与静态语言
动态语言:运行期间才做数据类型的检查
静态语言:数据类型在运行前检查强烈建议声明语句
尽管在js非严格模式下仍有侥幸的识别可能,但会导致bug的产生
变量的作用域
作用域:程序源码中定义的变量的区域
全局变量拥有全局作用域,局部变量只在函数内有意义
在函数体内局部变量的优先级高于同名的全局变量
var scope="global";//scope:范围
function checkscope(){
var scope="local";//局部变量
return scope;
}
checkscope();//=>local
全局变量声明时可以省略声明关键词
scope="global";
function checkscope2(){
scope="local";//糟糕!修改了全局变量
myscope="local";//显示声明新的全局变量
return[scope,myscope];//
}
checkscope2()//=>["local","local"]
scope//=>"loacl"
myscope//=>"loacl"
var scope="global scope";//global variable
functin checkscope(){
var scope="local scope";//local variable
function nested(){
var scope="nested scope";//nested scope of local variable
//nested:嵌套
return scope;//返回当前作用域内的值
}
return nested();
}
checkscope()//=>"嵌套作用域"
函数作用域和声明提前
块级作用域(block scope):c语言中函数的每段代码有各自的作用域,且变量在声明他们的代码段外不可见(invisible)
函数作用域(function scope):javascript中使用,variable在声明他们的functon body and nested scope 内有定义
function test(o){
var i=0;//i 在functin body内有意义
if(typeof o=="object"){
var j=0;j在函数体内有意义,不仅仅是在这个代码段内
for(var k=0;k<10;k++)//k在函数体内有意义不仅仅在循环内
{
console.log(k);//输出:0~9
}
console.log(k);//k=10
}
console.log(j);//j定义,但可能未初始化
}
即意味着所有函数体内的变量成员均可访问到
函数作用域指的是在函数内声明的所有变量在函数体内始终可见。意味着函数在变量声明前已经可用,被称为声明提前
var scope="global";
function f(){
console.log(scope);//输出"undefined",undefined:不同于null,属于更深层次的空值,表明变量没有初始化,undefined是预定义的全局变量,值为未定义
var scope="local";//变量赋初始值,但变量本身在函数体内的任何地方均有定义
console.log(scope);//=>"local"
如上函数等价于,将函数声明提前至顶部,同时变量初始化留在原位置
function f()
{
var scope;//var locla variable;
console.log(scope);//=>"undefined"未初始化;
scope="local";//初始化(init)赋值
console.log(scope);//
javascript中没有块级作用域(block scope),因此一般将声明变量放在函数体顶部,便于查看维护
作为属性的变量
全局对象的属性是全局定义的符号
js解释器在工作前定义一个全局对象(global object)
声明一个全局变量时,实际是定义了全局对象(global object)的属性
当使用var声明一个变量时,创建的这个属性是不可配置的,该变量无法通过delect运算符删除
如果没有使用严格模式并给一个未声明的变量赋值,javascript会自动创建一个局部变量,可删除
var truevar=1;
fakevar=2;
this.fakevar=3;
delect truevar//error;=>false
delect fakevar//=>true
delect this.fakevar//=>true
javascript全局变量是全局对象的属性,允许使用this引用全局对象,却没有方法引用局部变量存放的对象
作用域链
javascript基于词法作用域的语言、
词法作用域
当定义了一个函数后,当前的作用域就会被保存下来,并且成为函数内部状态的一部分
var x = "globol value";
var getValue = function(){
alert(x); //弹出"undefined"
var x = "local value";
alert(x); //弹出"local value";
}
getValue();
当getValue函数被定义的时候,他的作用域被保存起来,还有被加到作用域链上,他的上端就是全局执行环境。当调用getValue方法的时候,js解释器首先会把作用域设置为定义函数的时候的那个作用域(即之前保存那个),接下来,他在作用域的前加上调用对象即getValue这个函数,再在他的上端加上全局对象。
如果将局部变量看作是自定义实现的对象的属性的话,那么可以换个角度来解读变量作用域。
每段代码都有一个与之关联的作用域链,作用域链是一个对象列表,或链表。
作用域链决定了哪些数据能被函数访问。当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象填充。
过程
js要解析一个变量时(查找变量),会从链中的第一个对象开始查找,如果有则解析,没有继续上一个链查找。如未查找到结果,则抛出异常。
在js顶层代码中,作用域链又一个全局对象组成。
在不包含嵌套的函数体内,作用域链由局部变量对象和全局变量对象构成。
理解对象链的创建规则:定义一个函数时,实际上保存了一个作用域链,当调用一个函数时,创建一个新对象来存储他的局部变量,并添加至保存的作用域链上,同时创建一个新的更长的表示函数调用作用域的链,
对于嵌套函数来讲,每次调用外部函数时,内部函数又会重新定义一次,因为每次调用外部函数时,作用域链不同,内部函数在每次重新定义的时候都会有微妙的差别–每次调用外部函数时,内部函数的代码相同的,而且关联的作用域链不同
原理
Javascript 则是在运行时编译,边编译边执行,这个机制涉及到三样东西:
- 引擎
- 编译器
- 作用域
编译器首先对代码进行编译,然后将生成的代码提供给引擎执行。引擎和编译器工作的时候,都会用到作用域。编译器编译时,会在作用域中查找标示符,如果没有找到的话则会在对应的作用域创建一个标示符。引擎在执行时不断的在作用域中查找标示符,如果一直到全局对象(Global object)都没有找到的话则抛出一个 Reference Error
并停止代码的执行。//reference 引用