2017.08.24

new

内存的生命周期

  1. 内存分配场景

// 1. 简单数据类型 (5种)
var name = 'stone';
var age = 30;
var sexy = true;
var hobbies = null; // null 是程序级的空缺,正常或意料之中的值空缺
var education;    // undefined 是系统级的值的空缺,出乎意料的值的空缺

// 2. 基本包装类型(3种)
var numObject = new Number(30);
var strObject = new String('carl');
var boolObject = new Boolean(true);

// 3. 对象类型(创建对象的3种方法)
var obj1 = new Object();
var obj2 = {a: 4, b: 'b'}; // 推荐写法
var obj3 = Object.create();
var obj4 = new MyObject(); // 自定义对象
var obj5 = document.getElementById('btn');  // DOM 对象

// 4. 数组类型(创建数组的2种方法)
var arr_1 = new Array();
var arr_2 = [1, 2, 3, 4, 5];  // 推荐写法

// 5. 函数类型(创建函数的2种方法)
var foo = function () { }  // 推荐写法
var bar = new Function(code)

// 6. 其他常用对象
var data = new Date();
var error = new Error();
var expression = / pattern / flags;

// 7. 闭包 :主要作用是代码隔离,模拟类,但是各浏览器实现不同,滥用会导致内存泄漏
function outer (name) {
  var x = name;
  return function inner () {
    return "Hi, " + x;
  }
}
  1. 内存的回收规则(可在IE11浏览器中查看内存的使用情况)

    • 全局变量:页面被关闭被回收
    • 局部变量:函数执行完被回收
    • 闭包:局部变量的特俗情况,当局部变量被内部函数所占用,外部函数执行完也不被回收
var getLargeObj = function(size) {
  var arr = [];
  var intSize = parseInt(size);
  // 300 * 300 i j
  for (var i = intSize - 1; i>= 0; i--) {
    for(var j = intSize - 1; j>= 0; j--) {
      arr.push('abcdefghijklmnopqrstuvwxyz'); // 26Byte * 300 * 300
    }
    return arr; // 285.64453 MB
  }
}
// 全局变量
var largeObj1 =null;
function fun1() {
  largeObj1 = getLargeObj('300MB');
}
// 局部
function fun2() {
  var largeObj2 = getLargeObj('300MB');
}
  • GC的原理及性能优化

Garbage Collection:垃圾回收。执行环境会负责管理代码的执行过程中使用的内存,垃圾回收器会按照固定的时间间隔,周期性地执行者一操作。

  1. 标记清除(mark-and-sweep)
var a = 1p; // 执行时,被标记,进去环境
var foo = function () {
  var foo_a = 0; // 函数执行时,被标记,进去环境
  var foo_b = 'b';
}
foo(); // 函数执行完毕,foo_a, foo_b ,被标记,离开环境,等待被回收
// 全局变量 a 始终处于 被标记为进入环境,一直不被回收

// **手动解除引用**
a = null;

主要缺点:某些对象被清理后,内存是不连续的,那么就算内存占用率不高,但是由于内存的空隙太对,后来的对象可能无法存储到内存之中。 解决方案:在垃圾回收后进行整理操作(标记整理),整理的过程就是将不连续的内存向一端复制,使不连续的内存连续起来。(目前主流浏览器的实现方案)

  1. 引用计数(reference-count):跟踪记录每个值被引用的次数,缺点是循环引用。(IE6/7/8早期使用的解决方案,后来被抛弃)
  1. 性能问题

JavaScript 的垃圾回收机制在回收变量时,会停止响应其他的操作,这是未了安全考虑。(100ms甚至以上)

  1. 解决方案:

  2. 分代回收(Generation GC):通过区分新生与持久对象;多回收新生对象区,少回收持久对象区,减少每次需要遍历的对象,从而减少每次GC的耗时。(因为对象越新,生存期越短;对象越老生存期越长,回收内存的一部分,速度快于回收整个内存)

  3. 增量回收(Increment GC):将完整的回收过程拆分成很多部分,每次做完一部分就停下来,执行应用逻辑,垃圾回收和应用逻辑交替完成。
  • 调试技巧

IE Memory (11): window.CollectGarbage() 、堆快照

Chrome:Timeline(监控内存的使用情况、事件耗时、垃圾回收的内存大小和耗时), Memory(新建堆快照查看各对象占用内存情况、对比多个堆快照的区别)

Firefox:性能、内存(堆快照)选项卡

var getLargeMemory = function (size) {
  var arr = [];
  size = parseInt(size);
  for (var i = size.length - 1; i >= 0; i--) {
    for (var j = size.length - 1; j >= 0; j--) {
      arr.push('abcdefghijklmnopqrstuvwxyz');
    }
  }
};
var clearLargeMemory = function () {
  if (window.CollectGarbage) {
    CollectGarbage
  }
};
var testGlobal = function () {
  getLargeMemory();
};
var testFunc = function () {
  clearLargeMemory();
};

作用域

作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。

  • 三种拥有全局变量的情形:最外层函数本身以及外层函数定义的变量、所有未定义直接赋值的变量、所有window 对象的属性
  • var name;window.name 是有区别的:var 关键字申明的变量不能 delete

函数作用域(局部作用域)

作用域链

  • 在运行期上下文的作用域链中,标志符所在的位置越深,读写速度就会越慢。 解决方案:如果一个跨作用域的对象被引用一次以上,则会把它先存储到局部变量里再使用。
    function opDom() {
    document.getElementById('btn').onclick = function() {
      document.getElementById('node').style.color = '...';
    }
    }
    // 改写
    function opDom () {
    var doc = document;
    doc.getElementById('btn').onclick = function() {
      doc.getElementById('...')...;
    }
    }
    
  • 使用 with 关键字,会改变作用域链,从而产生性能问题
function initUI () {
  with (document) {
    debugger; // document 中的所有属性被推入栈顶,访问原本函数生成的作用域需要遍历完document 对象后才能访问到,所以成本提高了
    var db = body;
      links = getElementsByTagName('a');
      i = 0,
      len = links.length;
      while(i < len) {
        update
      }
  }
}

全局作用域、局部作用域、函数作用域、声明提前、作用域链、匿名函数

解题提示:

1.以下挑战主要涉及知识点有:全局作用域、局部作用域、函数作用域、声明提前、作用域链、匿名函数,请综合多个知识点,深入思考后给出答案。

2.部分挑战第一次执行的结果,会影响到第二次的执行结果。


// 挑战一

var scope = 'global';

function checkScope(flag) {

  if (flag) {

    var scope = 'local';

  }

  return scope;

}

console.log(checkScope(true));  // ???

console.log(checkScope(false)); // ???

// 挑战二

var scope = 'global';

function checkScope(flag) {

  if (flag) {

    scope = 'local';

  }

  return scope;

}

console.log(checkScope(true));  // ???

console.log(checkScope(false)); // ???

// 挑战三

var scope = 'global';

function checkScope(flag) {

  if (flag) {

    scope = 'local';

  }

  var scope;

  return scope;

}

console.log(checkScope(true));  // ???

console.log(checkScope(false)); // ???

// 挑战四

var scope = 'global';

function checkScope(flag) {

  if (flag) {

    (function(){

      var scope = 'local';

    })();

  }

  return scope;

}

console.log(checkScope(true));  // ???

console.log(checkScope(false)); // ???

// 挑战五

var scope = 'global';

function checkScope(flag) {

  if (flag) {

    (function(){

      scope = 'local';

    })();

  }

  return scope;

}

console.log(checkScope(true));  // ???

console.log(checkScope(false)); // ???

// 挑战六

var scope = 'global';

function checkScope(flag) {

  if (flag) {

    (function(){

      scope = 'local';

    })();

  }

  var scope;

  return scope;

}

console.log(checkScope(true));  // ???

console.log(checkScope(false)); // ???

results matching ""

    No results matching ""