MyException - 我的异常网
当前位置:我的异常网» JavaScript » 您不知道的javascript系列学习(作用域)

您不知道的javascript系列学习(作用域)

www.MyException.Cn  网友分享于:2013-08-11  浏览:0次
你不知道的javascript系列学习(作用域)

一、javascript作用域的理解

 

  • 1、什么是作用域

   (1)、作用域是根据名称查找变量的一套规则。如果查找的目的是对变量进行赋值,那么会使用LHS查询;如果目的是获取变量的值,就会使用RHS查询。

   (2)、在当前作用域中无法找到某个变量时,会在外层嵌套的作用域中继续查找,直到找到或者到最外层的作用域(全局作用域)为止。 

如:

function foo(a){

console.log(a+b);//b在函数作用域内无法找到,可以在上一级作用域找到b=2;

}

var b=2;//外层作用域

foo(2);//4

   (3)、不成功的RHS引用会导致抛出ReferenceError异常;不成功的LHS引用会导致自动隐式的创建一个全局变量(非严格模式下),该变量使用LHS引用的目标作为标识符,或者抛出ReferenceError异常(严格模式下)

 

  • 2、词法作用域

作用域分为词法作用域和动态作用域

   (1)、词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里决定的。

如:

  function foo(a){

var b=a*2;

function bar(){

console.log(a,b,c);

}

bar(b*3);

          }

 

foo(2);//最终输出2,4,12

       说明:

最外层包含整个全局作用域,其中只有一个标识符:foo。

 

foo层包含所创建的作用域,有三个标识符 a,bar,b。

bar层包含所创建的作用域,只有一个标识符 c。

    (2)、欺骗词法  在运行时“修改”词法的作用域,有两种机制来实现整个目的,一、用eval()函数,二、用with语句。但不推荐使用这两种方法,欺骗词法作用域会导致性能下降。

如:

function(str,a){

eval(str);//欺骗,将屏蔽了外层var b=2;将b定义为3

console.log(a,b);//1,3

}

         var b=2;

foo("var b=3;",1);

with 本质上是通过将一个对象的引用当作作用域来处理,将对象的属性当做作用域的标识符来处理,从而创建了 一个新的词法作用域(在运行时)。

这两个机制的副作用是引擎无法再编译时对作用域查找进行优化,将导致代码运行变慢。不要使用它们。

 

 

  • 3、函数和块作用域

(1)、函数作用域是指属于这个函数的全部变量都可以在整个函数的范围内使用及复用。

任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐藏”起来,外部作用域无法访问包装函数内部的任何内容。

如:

var a=2;

function foo(){

var a=3;

console.log(a);//3 访问的是foo函数内部变量a.如果foo函数内部没有定义a,则访问外部var a=2.

}

foo();

console.log(a);//2 访问的是全局变量a,不能访问foo函数里面的变量 var a=3

(2)、函数表达式 

(function foo(){

var a=3;

})();

作为函数表达式,意味着foo只能在所代表的位置中访问,外部作用域则不行。函数被包含在一对()括号内部,因此成为一个表示式,通过末尾加上另外一个()可以立即执行这个函数。

(3)、块作用域

块作用域是只变量在一个代码块中有效。

最熟悉的块作用域:

for(var i=0;i<10;i++){

console.log(i);//0,1,2,3,4,5,6,7,8,9

}

console.log(i);//10

可以看出i并不是在for块中有效,在外层作用域依然可以访问到。像if块里面的定义的变量也是一样,那么怎么样才是块作用域?

 

a、从with对象中创建的作用域

b、try/catch  的catch分句会创建一个块作用域,其中声明的变量仅在catch内部有效。

如:

try{

undefined();//执行一个非法操作来强制抛出异常

}catch(err){

console.log(err);//正常打印出错信息

}

console.log(err);//ReferenceError:err not found

c、let 关键字

 let关键字可以将变量绑定到所在的任意作用域中。通常{}内部。

 重新看回for循环的代码块:将var 变为let定义i

for(let i=0;i<10;i++){

console.log(i);//正常输出

}

console.log( i);//

ReferenceError: i is not defined  无法再外层引用,i只在for 代码块有效。

d、const 同样可以用来创建块作用域变量,但其值是固定的(常量)

 

  • 4、作用域闭包

(1)、什么是闭包?当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。(能够读取其他函数内部变量的函数)

 

如:

function foo(){

var a=2;

function baz(){

console.log(a);//2

}

bar(baz);

}

 

function bar(fn){

fn();//妈妈快看啊!这就是闭包

};

 

只要使用回调函数,实际上就是在使用闭包。

(2)、闭包与循环

for循环是最常见的例子:

for(var i=1;i<=5;i++){

setTimeout(function(){

console.log(i);// 输出5次6

},i*1000);

}

预想情况,是分别输出1~5,每秒一次,每次一个。

实际情况,每秒一次输出6.

因为,试图假设循环中每个迭代在运行时都给自己“捕获”一个i的副本,但是根据作用域的工作原理,实际情况是尽管循环中的五个函数是在各个迭代中分别定义的,

但是他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。

 

要想按预期输出需要有自己的变量,用来在每个迭代中存储i的值:

for(var i=1;i<=5;i++){

(function(){

var j =i;

setTimeout(function(){

console.log(j);

},j*1000);

})();

}

 

或者 可以用let 块作用域

 

for(let i=1;i<=5;i++){

setTimeout(function(){

console.log(i);

},i*1000);

}

 

(3)、模块

模块模式需要具备两个必要条件:

1、必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例);

2、封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。

如:

var foo = (function CoolModule(id){

function change(){

publicAPI.identify = identify2;

}

 

function identify1(){

console.log(id);

}

function identify2(){

console.log(id.toUpperCase());

}

 

var publicAPI ={

change:change,

identify:identify1

}

return publicAPI;

})("foo module");

foo.identify();//foo module

foo.change();

foo.identify();//FOO MODULE

 

文章评论

程序员应该关注的一些事儿
程序员应该关注的一些事儿
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
我的丈夫是个程序员
我的丈夫是个程序员
一个程序员的时间管理
一个程序员的时间管理
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
代码女神横空出世
代码女神横空出世
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
中美印日四国程序员比较
中美印日四国程序员比较
Java程序员必看电影
Java程序员必看电影
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员都该阅读的书
程序员都该阅读的书
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员的鄙视链
程序员的鄙视链
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
程序员和编码员之间的区别
程序员和编码员之间的区别
我是如何打败拖延症的
我是如何打败拖延症的
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
10个调试和排错的小建议
10个调试和排错的小建议
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
那些争议最大的编程观点
那些争议最大的编程观点
每天工作4小时的程序员
每天工作4小时的程序员
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
总结2014中国互联网十大段子
总结2014中国互联网十大段子
漫画:程序员的工作
漫画:程序员的工作
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
鲜为人知的编程真相
鲜为人知的编程真相
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
老程序员的下场
老程序员的下场
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
如何成为一名黑客
如何成为一名黑客
 程序员的样子
程序员的样子
旅行,写作,编程
旅行,写作,编程
编程语言是女人
编程语言是女人
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有