变量有两种类型:原始值、引用值。
原始值:Undefined、Null、Boolean、Number、String 和 Symbol。
引用值:保存在内存中的对象。(访问内存中的对象其实是访问引用值,因为JavaScript 不允许直接访问内存位置)
对于引用值而言,可以随时添加、修改和删除其属性和方法。
只有引用值可以动态添加后面可以使用的属性,原始值动态添加的属性会是undefined。
原始类型的初始化可以只使用原始字面量形式,如:

原始值:
let num1=1
let num2=num1
num1++
此时num1是2,num2是1。两个变量可以独立使用,互不干扰。
引用值:
let obj1 = new Object();
let obj2 = obj1;
obj1.name = "Nicholas";
console.log(obj2.name); // "Nicholas"
这里复制的是指针。obj1和obj2指向同一个对象。
ECMAScript 中所有函数的参数都是按值传递的。
在按值传递参数时,值会被复制到一个局部变量。在按引用传递参数时,值在内存中的位置会被保存在一个局部变量(传的是指针),这意味着对本地变量的修改会反映到函数外部。
如:函数中的num是局部变量,所以不会影响到count。
function addTen(num) {
num += 10;
return num;
}
let count = 20;
let result = addTen(count);
console.log(count); // 20,没有变化
console.log(result); // 30
又如:
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
typeof 操作符:判断一个变量是否为字符串、数值、布尔值或 undefined。如果是对象或null,返回Object。
instanceof:
result = variable instanceof constructor
执行上下文包括:全局上下文、函数上下文。
变量对象(variable object):执行上下文中定义的所有变量和函数都存在于这个对象上。
在浏览器中,全局上下文是window 对象:
上下文在其所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器)。
如果上下文是函数,则其活动对象activation object用作变量对象。
作用域链:决定了各级上下文中的代码在访问变量和函数时的顺序。
内部上下文可以通过作用域链访问外部上下文中的一切。
一个例子:
这里的上下文:

作用域链如图:

某些语句会导致在作用域链前端临时添加一个上下文,这个上下文在代码执
行后会被删除。即:
增加了 let 和 const 两个关键字。
使用 var 声明变量时,变量会被自动添加到最接近的上下文:
function add(num1, num2) {
sum = num1 + num2; //没有声明,自动添加到全局上下文
return sum;
}
let result = add(10, 20); // 30
console.log(sum); // 30
var 声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前,即提升:
console.log(name); // undefined
var name = 'Jake';
function() {
console.log(name); // undefined
var name = 'Jake';
}
相当于:
var name;
console.log(name); // undefined
name = 'Jake';
function() {
var name;
console.log(name); // undefined
name = 'Jake';
}
let:
const o1 = {};
o1 = {}; // TypeError: 给常量赋值
const o2 = {};
o2.name = 'Jake';
console.log(o2.name); // 'Jake'
垃圾回收:执行环境负责在代码执行时管理内存。
基本思路:确定哪个变量不会再使用,然后释放它占用的内存。周期性的。
两种标记策略:标记清理和引用计数。
思路:每个值都记录它被引用的次数。
遇到的问题:循环引用
objectA、objectB引用值都是2.
function problem() {
let objectA = new Object();
let objectB = new Object();
objectA.someOtherObject = objectB;
objectB.anotherObject = objectA;
}
解决方案:把变量设置为 null 会切断变量与其之前引用值之间的关系。当下次垃圾回收程序运行时,这些值就会被删除,内存也会被回收。
如果内存中分配了很多变量,则可能造成性能损失,因此垃圾回收的时间调度很重要。
IE7 发布后,JavaScript 引擎的垃圾回收程序被调优为动态改变分配变量、字面量或数组槽位等会触 发垃圾回收的阈值。IE7的起始阈值都与 IE6 的相同。如果垃圾回收程序回收的内存不到已分配的 15%,这些变量、字面量或数组槽位的阈值就会翻倍。如果有一次回收的内存达到已分配的 85%,则阈值重置为默认值。这么一个简单的修改,极大地提升了重度依赖 JavaScript 的网页在浏览器中的性能。
分配给浏览器的内存通常比分配给桌面软件的要少,这是出于安全考虑:避免运行大量 JavaScript 的网页耗尽系统内存而导致操作系统崩溃。
null,从而释放其引用。const和let都以块为作用域,可能会更早地让垃圾回收程序介入,尽早回收应该回收的内存。
如代码:V8 会在后台配置,让这两个类实例共享相同的隐藏类,因为这两个实例共享同一个构造函数和原型。
function Article() {
this.title = 'Inauguration Ceremony Features Kazoo Band';
}
let a1 = new Article();
let a2 = new Article();
如果又添加了一句:它们就不共享同一个隐藏类。
a2.author = 'Jake';
如果:这样避免了“先创建后补充”的动态属性赋值,两个实例又共享同一个隐藏类(不考虑 hasOwnProperty 的返回值)
function Article(opt_author) {
this.title = 'Inauguration Ceremony Features Kazoo Band';
this.author = opt_author;
}
let a1 = new Article();
let a2 = new Article('Jake');
如果又添加了一句:它们又不共享同一个隐藏类。
动态删除属性与动态添加属性导致的后果一样。
delete a1.author;
最好的方法:把不想要的属性设置为null,这样又可以保持隐藏类,又可以达到删除引用值供垃圾回收程序回收的效果。
JavaScript 中的内存泄漏大部分是由不合理的引用导致的。
意外声明全局变量
解释器会把变量name当作window的属性来创建。
function setName() {
name = 'Jake';
}
解决方法:在变量声明前头加上 var、let 或 const 关键字。
定时器
只要定时器还在运行,回调函数中的name就不会被回收。
let name = 'Jake';
setInterval(() => {
console.log(name);
}, 100);
闭包
调用 outer()会导致分配给 name 的内存被泄漏。
以上代码执行后创建了一个内部闭包,只要返回的函数存在就不能清理 name,因为闭包一直在引用着它。
let outer = function() {
let name = 'Jake';
return function() {
return name;
};
};
静态分配与对象池
如:调用这个函数会创建新对象,修改它,再返回给调用者。如果这个对象生命周期很短,则很快会被回收。如果频繁地调用这个函数,就可能有频繁的垃圾回收。
function addVector(a, b) {
let resultant = new Vector();
resultant.x = a.x + b.x;
resultant.y = a.y + b.y;
return resultant;
}
解决方法:不要动态创建矢量对象。
function addVector(a, b, resultant) {
resultant.x = a.x + b.x;
resultant.y = a.y + b.y;
return resultant;
}
在哪里创建矢量可以不让垃圾回收调度程序盯上呢?对象池
在初始化的某一时刻,可以创建一个对象池,用来管理一组可回收的对象。应用程序可以向这个对象池请求一个对象、设置其属性、使用它,然后在操作完成后再把它还给对象池。由于没发生对象初始化,垃圾回收探测就不会发现有对象更替,因此垃圾回收程序就不会那么频繁地运行。
注意:静态分配是优化的一种极端形式。大多数情况下不考虑。

