2017.08.24
new
内存的生命周期
- 内存分配场景
// 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;
}
}
内存的回收规则(可在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:垃圾回收。执行环境会负责管理代码的执行过程中使用的内存,垃圾回收器会按照固定的时间间隔,周期性地执行者一操作。
- 标记清除(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;
主要缺点:某些对象被清理后,内存是不连续的,那么就算内存占用率不高,但是由于内存的空隙太对,后来的对象可能无法存储到内存之中。 解决方案:在垃圾回收后进行整理操作(标记整理),整理的过程就是将不连续的内存向一端复制,使不连续的内存连续起来。(目前主流浏览器的实现方案)
- 引用计数(reference-count):跟踪记录每个值被引用的次数,缺点是循环引用。(IE6/7/8早期使用的解决方案,后来被抛弃)
- 性能问题
JavaScript 的垃圾回收机制在回收变量时,会停止响应其他的操作,这是未了安全考虑。(100ms甚至以上)
解决方案:
分代回收(Generation GC):通过区分新生与持久对象;多回收新生对象区,少回收持久对象区,减少每次需要遍历的对象,从而减少每次GC的耗时。(因为对象越新,生存期越短;对象越老生存期越长,回收内存的一部分,速度快于回收整个内存)
- 增量回收(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)); // ???