饥人谷技术博客

JS-执行环境-作用域(链)-垃圾收集

2019-01-18  本文已影响16人  学的会的前端

执行环境

作用域链(scope chain)

var color = 'red';
function changeColr(){
    var another = 'blue';
    function swapColpr() {
        var temp = 'yellow';
        console.log(color); //red 可以访问祖先执行环境
        console.log(another); //blue  可以访问父执行环境
        console.log(temp); //yellow 可以访问自身执行环境
    }
    swapColpr();
    console.log('------------------');
    console.log(color); //red 可以访问父执行环境
    console.log(another); //blue 可以访问自身执行环境
    console.log(temp); // error not defined 不能访问子执行环境
}
console.log('------------------');
console.log(color); //red 可以访问自身执行环境
console.log(another); //error not defined 不能访问子执行环境
console.log(temp); //error not defined 不能访问子执行环境
changeColr();

延长作用域链

  1. try-catch语句的catch块。会创建一个新的变量,其中包含抛出错误对象的声明。
  2. with语句。会将指定对象添加到作用域链中。
function bulidUrl(){
    var qs = "debug = true";
    with(location){
        var url = href + qs;
    }
    return url
}
var result = bulidUrl();
console.log(result);
TIM图片20190118103029.png

以上代码假设with不能延长作用域链的话,那么url就是with函数内的局部变量,他的父执行环境bulidUrl是无法访问的,那么return url就会报错,但是with可以延长作用域链,他接收到的是location对象,因此其变量对象中就包含了location对象所有的属性和方法,并且把这个变量对象添加到作用域链的前端,当在with语句中引用变量href时(实际上引用的是location.href),可以在当前执行环境的变量对象中找到,当变量引用qs时,引用的是在bulidUrl()中定义的那个变量,至于with语句内部,则定义了一个名为url的变量,因此url就成了函数执行环境的一部分,可以作为函数值被返回。

没有块级作用域

if(true){
    var color = "blue";
}
console.log(color)
TIM图片20190118103912.png
在其他强类型语言,例如C语言中,color是定义在if语句内的变量,if是具有块级作用域的,当if执行完,color就会被销毁,所以最后输出undefined,但是在JavaScript语言中,if语句中的变量声明会将变量添加到当前的执行环境(此时当前的执行环境就是全局执行环境)中。for语句也是这样的道理

垃圾收集

JavaScript具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存,所需内存的分配以及无用内存的回收完全实现了自动化管理。
原理:垃圾回收器按照固定的时间间隔找出那些不在继续使用的变量,然后释放其占用的内存。

  1. 标记清除
    先给即将使用的变量标记---清除标记---标记使用后的变量,便于删除
  2. 引用计数:跟踪标记记录每个值被引用的次数。
    当声明了一个变量a并且将一个引用类型的值Q赋值给a,Q的引用就+1,如果Q又被b引用,Q的引用在+1,此时为2。当a不引用Q而引用T的时候,Q的引用就-1,此时Q的引用为1,当Q的引用为0时,就将Q占用的内存空间收回来。
  3. 引用计数的缺点:循环引用
function fn(){
    var obj1 = new Object();
    var obj2 = new Object();
    obj1.someOtherObject = obj2;
    obj2.anotherObject = obj1;
}

例如以上代码,obj1通过someOtherObject引用obj2,obj2通过anotherObject引用obj1,那么obj1和obj2的引用都是2,永远不会变成0,所以使用引用计数永远无法回收,假设这个函数被重复多调用,那么就会导致大量的内存得不到回收。
在IE浏览器中,为了避免DOM对象和JavaScript原生对象之间循环引用问题,最好在不使用它们的时候,手动断开它们之间的联系。

myObject.element = null;
element.somObject = null;

设置为null就断开了它们之间的联系,垃圾回收机制就会对他们进行内存回收。
原代码:

/*原生对象与DOM对象是循环使用的*/
var element = document.getElementById('some_element'); //DOM对象
var myObject = new Objrct(); //JavaScript原生对象
myObject.element = element;  // 原声对象与DOM对象的联系
element.someObject = myObject; // DOM对象与原声对象的联系

为了解决上诉问题,IE9把DOM和BOM对象都转成了真正的JavaScript对象。

管理内存

分配给web浏览器的可用内存数量通常要比分配给桌面应用程序的少,目的:防止运行JavaScript的网页耗尽全部系统内存造成系统崩溃。内存限制的问题不仅会影响给变量分配内存,同时还会影响调用栈以及一个在线程中能够同时执行的语句数量。

function fnname){
    var person = new Object();
    person.name = name;
    return person; //person为局部变量,当函数执行完毕后,自动离开执行环境,不需要设置null。
}
var resut = fn('liqi');
resut = null //手动解决resut的引用

以上内容小结:

JavaScript变量可以用来保存两种类型的值:基本类型和引用类型。基本类型的值包含5种:undefined,null。number,Boolean,string。基本类型与引用类型具有以下特点:

代码解析示例1

var x = 10
bar()
function foo() {
  console.log(x)
}
function bar(){
  var x = 30
  foo() //  输出什么
}

当声明的时候,会出现下面的情况

globalContext = {
  AO: {
    x: 10
    foo: function
    bar: function
  },
  Scope: null
}

//声明 foo 时 得到下面
foo.[[scope]] = globalContext.AO
//声明 bar 时 得到下面
bar.[[scope]] = globalContext.AO

注意: 在当前的执行上下文内声明的函数,这个函数的[[scope]]就执行当前执行上下文的 AO
当调用 bar() 时, 进入 bar 的执行上下文

barContext = {
  AO: {
    x: 30
  },
  Scope: bar.[[scope]] //globalContext.AO
}

当调用 foo() 时,先从 bar 执行上下文中的 AO里找,找不到再从 bar 的 [[scope]]里找,找到后即调用
当调用 foo() 时,进入 foo 的执行上下文

fooContext = {
  AO: {},
  Scope: foo.[[scope]] // globalContext.AO
}

所以 console.log(x)是 10

代码解析示例2

var x = 10;
bar() //  输出什么
function bar(){
  var x = 30;
  function foo(){
    console.log(x)
  }
  foo();
}

进行声明的时候,会出现

globalContext = {
  AO: {
    x: 10
    bar: function
  },
  Scope: null
}

//声明 bar 时 得到下面
bar.[[scope]] = globalContext.AO

注意: 在当前的执行上下文内声明的函数,这个函数的[[scope]]就执行当前执行上下文的 AO
当调用 bar() 时, 进入 bar 的执行上下文

barContext = {
  AO: {
    x: 30,
    foo: function
  },
  Scope: bar.[[scope]] //globalContext.AO
}
//在 bar 的执行上下文里声明 foo 时 得到下面
foo.[[scope]] = barContext.AO

当调用 foo() 时,先从 bar 执行上下文中的 AO里找,找到后即调用
当调用 foo() 时,进入 foo 的执行上下文

fooContext = {
  AO: {},
  Scope: foo.[[scope]] // barContext.AO
}

所以 console.log(x)是 30

相关概念

笔试题

var a = 1;

function fn(){
  console.log(a);   //undefined
  var a = 5;
  console.log(a);    // 5
  a++; //a = 6
  var a; // a= 6
  fn3(); 
  fn2(); 
  console.log(a); // 20

  function fn2(){
    console.log(a);  //6
    a = 20;
  }
}

function fn3(){
  console.log(a) //a = 1
  a = 200;
}

fn();
console.log(a);  //200

补充说明

上一篇 下一篇

猜你喜欢

热点阅读