javascript的作用域、闭包

javascript的作用域和闭包

作用域

作用域:作用域是一个规则,用于确定在何处以及如何查找变量。如果查找的目的是对变量进行赋值,就使用LHS查询;如果目的是获取变量的值,就使用RHS查找。赋值操作符产生的是LHS查找
(程序代码中定义这个变量的区域。全局变量拥有全局作用域,代码中任何地方都是有定义的,函数内声明的变量只在函数体内有定义,作用域是局部性的)
LHS:赋值操作的目标 是谁

RHS:谁是赋值操作的源头(得到某某的值)

寻找不成功的RHS 会抛出ReferenceError异常,不成功的LHS会在非严格模式下自动隐式的创建一个全局变量,严格模式下抛出ReferenceError异常

demo:

function foo(a){
   console.log(a);
}
foo(2);

在上面的代码中可以看到,LHS查询有:a=2;;RHS查询有:console.log(a);和foo(2);

作用域链

作用:保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,子对象会一级一级的向上寻找所有父对象的变量,变量访问到window对象即将终止,作用域链向下访问变量是不被允许的

词法作用域

作用域查找时会在找到的第一个匹配的标识符处停止,所以在多层嵌套作用域中可以定义同名的标识符(内部标识符遮蔽了外部标识符),叫做遮蔽效应

欺骗词法机制(修改词法作用域)
  • eval

让你写的代码中用程序生成代码并运行,就好像代码是写在那个位置

demo:

function foo(str,b){
   eval(str);//欺骗
   console.log(a,b);
}

var b = 2;

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

从这段代码中eval(..)调用”var b = 3”,代码会被当做本身就在那里一样执行,让b=3假装存在于foo()函数中,即在foo()内部创建了一个b变量,并遮蔽了外部作用域的同名变量。所以console.log会输出1,3

eval()函数通常被用来执行动态创建的代码。

  • with(不推荐使用)

通常被当做重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。

对比with和eval,eval()函数如果接受了包含一个或多个声明的代码,就会修改其所处的词法作用域,而with声明实际上是根据你传递给他的对象凭空创建了一个全新的词法作用域。

隐藏内部实现

把变量和函数包含在一个函数的作用域中,用这个作用于来隐藏他们

demo:

function doSomething(a){
   b = a + doSomethingElse( a * 2);

   console.log( b * 3);
}

function doSomethingElse(a){
   return a - 1;
}

var b;

doSomething( 2 );

将部分隐藏在doSomething(…)内部

demo :

function doSomething(a){
   function doSomethingElse(a){
      return a - 1;
  }
   var b;
   b = a + doSomethingElse( a * 2);
   console.log( b * 3);
}

doSomething( 2 );

此时b和doSomethingElse(…)都无法从外部被访问,只能被doSomething()控制。

“隐藏”的好处:

  • 可以最小限度的暴露必要内容,将他的访问权限变为私有
  • 可以避免同名标识符之间的冲突,防止变量的值被意外覆盖

    规避冲突的方法
    
    * 隐藏内部实现
    * 全局命名空间
    
         将一个对象用作库的命名空间,所有需要暴露给外界的功能都会成为这个对象的属性,
           而不是将自己的标识符暴露顶级的词法作用域
    

demo:

var MyReallyCoolLibtary = {
  awesome:"stuff",
  dosomething: function(){
   //....
 },
 doAnotherThing: function(){
   //
}
};
  • 模块管理

选用一种模块管理器工具,利用作用域中的规则强制所有标识符都不能注入到共享作用域中,而且保持私有、无冲突的作用域中,可以有效规避所有的意外冲突

函数作用域

含义:属于这个函数的全部变量都可以在整个函数范围内使用或者复用

匿名函数

setTimeout(function(){
   console.log("I waited 1 secound")
},1000)

虽然书写简单快捷,但是匿名函数在栈追踪时不会显示出有意义的函数名,调试困难;当需要引用自身的时候不方便;所以给函数表达式命名是一个最好的方式

setTimeout(function timeoutHandler(){
   console.log("I waited 1 secound")
},1000)
 立即执行函数表达式(IIFE)

表达式: (function foo(){…})()或者 (function(){…}())

 使用方法:
  • 使用一个匿名函数表达式
var a = 2;
(function IIFE(){
   var a = 3;
   console.log(a);//3
 console.log(a);//2
  • 当作函数调用并传递参数进去
var a = 2;
(function IIFE(global){
   var a = 3;
   console.log(a);//3
   console.log(global.a);//2
})(window);
console.log(a);//2
把windowd对象的引用传递进去,将参数命名为global,
  • 解决undefined标识符的默认值被错误覆盖导致的异常
undefined = true;
(function IIFE(undefined){
   var a;
   if(a===undefined){
     console.log("Undefined is safe here");
}
})();
  • 倒置代码的运行顺序(将需要执行的代码写在第二位,在IIFE执行后当作参数传递进去,在UMD<Universal Module Definition >模式中被使用)
var a = 2;
(function IIFE(def){
   def(window);
})(function def(global){
   var a = 3;
   console.log(a);
   console.log(global.a);
});
def定义在片段的第二部分,当做参数被传入IIFE函数定义的第一部分,最后def被调用,并将window传入当作global参数的值。

块作用域

理解:将代码从函数中隐藏信息扩展为在快中隐藏信息

demo:

for(var i = 0;i<10;i++){
   console.log(i);
}

变量i的作用域只在for循环内部使用

用于创建块作用域的函数
  • with

with从对象中创建出的作用域仅在with声明中而非外部作用域中有效
* try/catch

javascript的ES3规范中try/catch的catch分句会创建一个块作用域,其中声明的变量只在catch中有效

  • let
    可以将变量绑定到所在的任意作用域中,

  • const

    同样由ES6引入,用来创建块作用域变量,其值是固定的(常量)

var foo = true;
if(foo){
   var a = 2;
   const b = 3;
a = 3;
b = 4;
}
console.log(a);
console.log(b); 

闭包

当函数记住并可以访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这是就产生了闭包

特点:

  • 当function里面嵌套function时,内部的function可以访问外部的funciton里的变量、参数、函数
function foo(){
  var a = 2;
  function bar(){
     console.log(a);
 }
 return bar;
}
var bar = foo();
baz();//2

函数bar()的词法作用域能够访问foo()中的内部作用域,然后将bar作为一个值类型进行传递,bar()在自己定义的词法作用域之外执行,他拥有涵盖foo()内部所有作用域的闭包

  • 参数和变量不会被垃圾回收机制回收
  • 优点:避免全局变量污染
  • 缺点:闭包常驻内存,会增大内存使用量
    解决方法:在退出函数之前,要将不使用的局部变量全部删除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值