JavaScript作用域链和原型链

JavaScript作用域链和原型链

  1. 作用域链
  • 作用域

    ​ 作用域,是指变量的生命周期(一个变量在哪个范围内保持一定的值),在JavaScript中,作用域分为以下几种:全局作用域函数作用域块级作用域动态作用域

    • 全局作用域

      全局变量存在于整个程序中,程序中的任何函数、任何方法均可访问全局变量,但全局变量不能滥用,所以操作或使用未生命的变量,JS会默认生命此变量为全局变量。

    • 函数作用域

      函数作用域内,对外是封闭的,外层无法直接访问内部变量,内部可访问外层的变量

    • 块级作用域(ES6)

      使用功能let关键字声明,可形成块级作用域

    • 动态作用域

      this引用会存在动态作用域

    • 执行上下文

      ​ 执行上下文即当前代码的执行环境,可以描述成是一个包含变量对象、活动对象、作用域链和this的数据结构。

    • 作用域链

      ​ JS所有函数都有它的执行环境(执行上下文),当它执行的时候会被压入环境栈,如果执行完毕,会将当前环境弹出,执行环境中有函数作用域中定义的变量和其他信息,作用域链最顶端是当前执行环境,下一个是上层环境,一直到Windows对象。举个例子:

      function Outer(){
          var out = 123;
          Inner();
          function Inner(){
              var inner = 456;
              console.log(out,inner);   //123,456
          }
      }
      Outer();
      

      Inner内部既可以访问Inner的作用域,也可以访问Outer的作用域。因此,当多个作用域互相嵌套时,就形成了作用域。词法作用域在查找标识符的时候会就近查找,从当前作用域向外逐层查找,当抵达最外层作用域依然没有找到,则停止查找。如果是RHS则会抛出错误,如果是LHS则会在全局范围内声明该变量。

    • 闭包

      定义:函数在定义的作用域以外的地方被调用,闭包使得函数可以继续访问定义时的作用域。举个例子:

      function fn(){
          var temp = 123;
          function func(){
              console.log(temp);
          }
          return func;
      }
      var func = fn();
      func();      //123
      

      func则是有一个闭包函数,使得全局范围可以访问fn函数作用域内部的变量temp,且函数执行完后,fn对象的活动变量不会被销毁,因为fn返回的函数func的作用域链还保持着fn的活动变量,因此JavaScript的垃圾回收机制不会回收fn活动变量。综上所述,闭包会引起内存泄漏,尽量不要使用闭包。

    • 垃圾回收机制

      JS的垃圾回收机制分为两种,分别为标记清除和引用计数

      • 标记清除:垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后它会去掉环境中的变量以及环境中的变量引用的变量的标记,如果此时还存在有标记的变量就视为准备删除的变量,因为环境中的变量无法访问到这些变量,最后垃圾收集器完成内存清理,销毁这些变量并回收内存。
      • 引用计数:记录每一个值被引用的次数,当声明一个变量并将一个引用类型值赋给该变量时,该变量的引用次数加一,如果一个值又被赋给另外一个变量,则该值的引用次数减一,当这个值的引用次数为0时,就说明没有办法再访问这个值了,就可以回收该变量的空间,当垃圾收集器下次运行时,它会释放这个值占用的内存。
  1. 原型链
  • 原型

    ​ 所有的函数都有一个特殊的属性:prototype,prototype属性是一个指针,指向原型对象,原型对象中的方法和属性都可以被函数的实例所共享。通过构造函数创建的对象实例都可以共享构造函数的原型的方法。举个例子:

    var Person = function(name){
        this.name = name;
    }
    Person.prototype.sayName = function(){
        console.log(name);
    }
    var person = new Person('aa');
    person.sayName();  //aa
    

    上述例子中对象personPerson构造函数创建的实例,通过new操作符调用构造函数时主要执行以下几个步骤:

    • 创建新的对象,并将函数的this指向新创建的对象
    • 执行函数
    • 返回新创建的对象

    通过构造函数创建的对象,内部有一个_proto_指针指向构造函数的原型对象

    构造函数的原型对象有一个constructor属性,指向构造函数

    • 原型链

      当执行以下代码时:

      person.toString();
      

      首先会在person实例中查找toString属性,发现不存在该属性;会转到Person原型对象去找toString属性,仍然不存在;接着到Person原型对象的_proto_指向的Object原型对象中找,终于找到,然后执行toString方法。这样整个串起来就是一条原型链,原型链的顶端为null

    prototype:构造函数具有的属性,指向调用构造函数而创建的实例的原型

    _proto_:指向对象的原型

    constructor:原型对象的属性,指向原型对象对应的构造函数

    在这里插入图片描述

  1. 作用域链和原型链的比较
  • 作用域链是主要的作用是查找标识符,从当前作用域沿着作用域链依次查找,如找到会停止查找,否则会继续查找,直到尾部;尾部依然没有找到的情况分为两种,如果RHS,会抛出未找到变量的错误,如果是LHS,会在查找的最外层声明变量。
  • 原型链是用于查找引用类型的属性,沿着作用域链依次向上查找,如果找到该属性会停止查找并作出相应的操作,否则会沿着原型链依次查找到结尾,尾部依然没有找到的情况分为两种,如果是RHS,会返回undefined,如果是LHS,会在该对象实例中声明变量,不会去查找原型链。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值