js做用域与执行环境(前端基础系列)

1、做用域(what?)前端

  官方解释是:“一段程序代码中所用到的名字并不老是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的做用域。”
  单从文字理解比较难懂,举个栗子:
        java

function outer(){
  // 声明变量
  var name = "ukerxi";
  
  // 定义内部函数
  function inner() {
    console.log(name); // 能够访问到 name 变量
  }
}
console.log(name); // 报错,undefined

  其中变量name声明在 oute r函数中,当在 outer 中定义一个 inner 函数进行输出 name,能够获得正确的值,而在 outer 外进行输出 name 出现 undefined 错误;在此能够看出 outer 函数即为 name 变量的做用域(证实过程比较粗略,但结论仍是正确的--_--);编程

 

2、做用域(why?)后端

  做用域的使用,提升了程序的逻辑的局部性,加强程序的可靠性,以及避免命名冲突;为代码的模块化开发提供便利;根据上面提到的函数做用域,name 变量被局限在了 outer 函数中,在其余的函数中也能够定义相同名字的变量,二者之间不会互相影响;浏览器

 

3、js 中的做用域app

  先说ES5版本及更低版本的,由于在 ES6 上,从新定义了几个决定做用域的关键字;模块化

  1. 没有块级做用域
    在javaScript中,不像C、java等拥有块级做用域;常见块级做用域,例如:
    // C语言实现
    for(int i= 0; i<10; i++){
        // 中括号里面就是块级做用域
    }
    
    if(ture){   
        int i = 1;
        // 这里也是块级做用域
    }
    printf("%d/n",i); // --“use an undefined variable:i”
    //这里是访问不到for语句中的i与if语句中的i变量的

    固然这些在javaScript中是没有的,通常来讲只有块级做用域;因此使用时须注意做用域的影响,例如:
    for(var i= 0; i<10; i++){
        // do something
    }
    
    console.log(i); // --10

    在程序设计过程当中可使用函数做用域,进行模拟块级做用域;例如:
    function loop(){
        for(var i= 0; i<10; i++){
            // do something
        }
    }
    loop();
    console.log(i); // --undefined

    javaScript是灵活可变的,一样上面这个例子,可使用自执行函数重写实现,这样就减小了调用这一步;
    (function (){
        for(var i= 0; i<10; i++){
            // do something
        }
    }());
    console.log(i); // --undefined
  2. ES6中的做用域
    在ES6中新增长了(let,const)关键字,进行定义变量,解决了没有块级做用域的限制;
    let:let容许你声明一个做用域被限制在块级中的变量、语句或者表达式。与var关键字不一样的是,它声明的变量只能是全局或者整个函数块的。let声明的变量只在其声明的块或子块中可用,这一点,与var类似。两者之间最主要的区别在于var声明的变量的做用域是整个封闭函数。
    具体以下:
    function range () {
      // let 和var 相同的地方,都有函数做用域
      var name = 'ukerxi';
      let nameOuter = 'outer';
    
      for (var j = 0; j < 1; j++) {
        console.log(name)
      }
      console.log("输出j变量", j); // ==> 1
      for (let i = 0; i < 1; i++) {
        console.log(nameOuter)
      }
      console.log("输出i变量", i); // 报错 undefined
    }

    能够看出,使用let 定义的i变量,在for语句外进行输出时,会进行报错,说明i不在该做用域内,i的做用域在for包裹的做用内;函数

  3. 做用域链
    每一个函数都有本身的执行环境,包含当前环境的变量访问关系,与之相关联的就是“变量对象”,若是是当前函数的变量对象,也可称为“活动对象”;此对象中包含了,当前函数可访问的变量及函数;变量对象,最开始包含的对象是参数的arguments对象,而后是在函数中定义的其余变量及方法;例如:
    function fn(name){
        var text = "test";
    }
    // 变量对象中包含:命名函数fn变量、参数name、内部变量test


    固然这个变量对象是不可访问的,只提供后台引擎编译执行使用;当定义有多个变量对象嵌套,这些变量对象就组成了做用域链;例如:oop

    var name = "global";
    function super() {
        var name = "super";
        function sub(){
             var name = "sub";
        }
    }

    做用域链:
    this

    在做用域最前端的是活动对象,而最后端是全局执行环境window(浏览器宿主中);变量访问原则是,根据做用域前端往上进行搜索,若是提早搜索到变量,则中止搜索,例如上面这个例子中,name变量的值是"sub"由于其在最前端的变量对象中已经定义了,就不会往上继续检索;

  4. 延长做用域
    有两种方法能够将做用域进行延长:
    ①、try-catch 语句的catch块
    ②、with 语句

    两个语句都是在本来的做用域最前端进行添加一个变量对象;例如:
    var name = "global";
    function test(){
        var name = "sub";
        with(window){
            console.log(name);
        }
    }
    
    test(); // -- "global"

    做用域链:


    因此检索变量时,会先在最前端的window变量对象中检索;固然,在严格模式下已经禁用了with语句,编程时,最好向后兼容,废弃使用with语句;

  5. 执行环境只与函数的声明及定义位置有关
    当一个函数定义后其执行环境与做用域链就已经肯定了,不会由于执行位置改变而改变,具体例子:
    var name = "global";
    function getName(){
        console.log(name);
    }
    
    function test (){
        var name = "inner";
        getName();
    }
    
    // 执行test
    test(); // -- global

    运行test 函数,其中test 函数执行的是 getName 进行输出 name 变量,输出的是全局变量的信息;即当 getName 定义时就已经肯定了本身的做用域及执行环境,于是不会由于执行位置的不一样而输出不一样的信息;固然有一种状况不同,那就是灵活的 this

  6. this的动态绑定
    与做用域链及执行环境不一样,this是根据执行时的接受者进行绑定的,改变this的几种方法:
    ①、new 关键字
    ②、call / apply 方法
    ③、直接调用构造函数

    具体例子以下:
    // 声明一个类
    function Person (){
        this.name = "ukerxi";
    }
    // 使用new关键字,使this执行新建对象
    // 实际上是构造函数默认返回this
    var men1 = new Person(); // this绑定到men1上
    
    // 声明一个空对象,使用call/apply 进行绑定
    var men2 = {};
    Person.call(men2); // this绑定到men2上
    
    // 直接执行构造函数
    Person(); // this绑定到window上(使用严格模式则会报错,this指向undefined)

     

【结束语】

   系列文章,包括了原创,翻译,转载等各种型的文章;一方面是为了本身总结,另外一方面页但愿能够共享知识;在技术方面有输入,也要有所输出,才能更进一步!文章基于本身的实践、阅读及理解,若有不合理及错误的地方,烦请各大佬评论指出,以便改正,感谢!

相关文章
相关标签/搜索