javascript变量声明提升(hoisting)
来源:互联网 发布:erp软件实施顾问 编辑:程序博客网 时间:2024/05/21 08:45
javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。
先看一段代码
var v = "hello";(function(){ console.log(v); var v = "world";})();
这段代码运行的结果是什么呢?
答案是:undefined
这段代码说明了两个问题,(变量覆盖)
第一,function作用域里的变量v遮盖了上层作用域变量v。代码做少些变动
var v = "hello";if(true){ console.log(v); var v = "world";}
输出结果为”hello”,说明javascript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构。
第二,在function作用域内,变量v的声明被提升了。所以最初的代码相当于:(变量提升)
var v = "hello";(function(){ var v; //declaration hoisting console.log(v); v = "world";})();
声明、定义与初始化
声明宣称一个名字的存在,定义则为这个名字分配存储空间,而初始化则是为名字分配的存储空间赋初值。
用C++来表述这三个概念
extern int i;//这是声明,表明名字i在某处已经存在了int i;//这是声明并定义名字i,为i分配存储空间i = 0;//这是初始化名字i,为其赋初值为0
javascript中则是这样
var v;//声明变量vv = "hello";//(定义并)初始化变量v
因为javascript为动态语言,其变量并没有固定的类型,其存储空间大小会随初始化与赋值而变化,所以其变量的“定义”就不像传统的静态语言一样了,其定义显得无关紧要。
声明提升
当前作用域内的声明都会提升到作用域的最前面,包括变量和函数的声明
(function(){ var a = "1"; var f = function(){}; var b = "2"; var c = "3";})();
变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:
(function(){ var a,f,b,c; a = "1"; f = function(){}; b = "2"; c = "3";})();
请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:
(function(){ //var f1,function f2(){}; //hoisting,被隐式提升的声明 f1(); //ReferenceError: f1 is not defined f2(); //被阻塞了,不会执行到这里 var f1 = function(){}; function f2(){}})();
上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明是被提升的。
名字解析顺序**
javascript中一个名字(name)以四种方式进入作用域(scope),其优先级顺序如下:
1、语言内置:所有的作用域中都有 this 和 arguments 关键字
2、形式参数:函数的参数在函数作用域中都是有效的
3、函数声明:形如function foo() {}
4、变量声明:形如var bar;
名字声明的优先级如上所示,也就是说如果一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字(函数优先提升),无论其在代码中的顺序如何。但名字的初始化却是按其在代码中书写的顺序进行的,不受以上优先级的影响。看代码:
(function(){ var foo; console.log(typeof foo); //function function foo(){} foo = "foo"; console.log(typeof foo); //string})();
如果形式参数中有多个同名变量(形参同名,后来覆盖),那么最后一个同名参数会覆盖其他同名参数,即使最后一个同名参数并没有定义。
以上的名字解析优先级存在例外,比如可以覆盖语言内置的名字arguments。
命名函数表达式
可以像函数声明一样为函数表达式指定一个名字,但这并不会使函数表达式成为函数声明。命名函数表达式的名字不会进入名字空间,也不会被提升。
f();//TypeError: f is not a functionfoo();//ReferenceError: foo is not definedvar f = function foo(){console.log(typeof foo);}; foo()并不会提升f();//functionfoo();//ReferenceError: foo is not defined
命名函数表达式的名字只在该函数的作用域内部有效。
怎么写代码
现在你理解了作用域和变量提升,那么这对于javascript编码意味着什么?最重要的一点是,总是用var定义你的变量。而且我强烈推荐,对于一个名称,在一个作用域里面永远只有一次var声明。如果你这么做,你就不会遇到作用域和变量提升问题。
语言规范怎么说
我发现ECMAScript参考文档总是很有用。下面是我找到的关于作用域和变量提升的部分:
如果变量在函数体类声明,则它是函数作用域。否则,它是全局作用域(作为global的属性)。变量将会在执行进入作用域的时候被创建。块不会定义新的作用域,只有函数声明和程序(译者以为,就是全局性质的代码执行)才会创造新的作用域。变量在创建的时候会被初始化为undefined。如果变量声明语句里面带有赋值操作,则赋值操作只有被执行到的时候才会发生,而不是创建的时候。
- javascript变量声明提升(hoisting)
- javascript变量声明提升(hoisting)
- javascript变量声明提升(hoisting)
- javascript变量声明提升(hoisting)
- javascript变量声明提升(hoisting)
- javascript变量声明提升(hoisting)
- javascript变量声明提升(hoisting)
- javascript变量声明提升(hoisting)
- JavaScript变量提升-Hoisting
- Javascript变量提升(Hoisting)
- Javascript变量提升(Hoisting)
- JavaScript中变量提升 Hoisting
- JavaScript变量提升(Hoisting)
- JavaScript中变量提升 Hoisting
- JavaScript中变量提升 Hoisting
- JavaScript中变量提升------Hoisting
- JavaScript中变量提升 Hoisting
- JavaScript中变量提升 Hoisting
- MeasureSpec的三种模式的意思
- 【python】杂笔记
- 字符串匹配之KMP算法
- TOJ 1473.Moving Tables
- Android 使用View Animation实现动画加载界面
- javascript变量声明提升(hoisting)
- JAVAWEB Dao模式之增删改和查询通用方法
- Treap模板(支持Merge/Split)
- (二十一)、常用类:Object类
- Oracle数据库安装与配置
- git 操作中的情况
- java编程300例:8.简单用嵌套输出九九乘法表
- 5-1 顺序存储的二叉树的最近的公共祖先问题
- 5-1 顺序存储的二叉树的最近的公共祖先问题 (25分)