变量
ECMAScript两种变量
原始值:简单数据
引用值:由多个数据组成的对象
原始值能直接访问到变量内存,而引用值只能通过引用来访问到变量内存
两种变量复制值
原始值:完全独立
let num1 = 5;
let num2 = num1;
引用值:浅拷贝就是将引用指向新的值,深拷贝也是独立的
浅拷贝:
let obj1 = new Object();
let obj2 = obj1;
obj1.name = "Nicholas";
console.log(obj2.name); // "Nicholas"
函数传递参数
引用值传递给参数(即命名参数,局部变量),不是按引用传递的。(参数只能修改对象属性)
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
确定类型
通过typeof来确定对象是什么类型的对象是不准确的,数组跟普通对象还null,都是返回Object
let s = "Nicholas";
let b = true;
let i = 22;
let u;
let n = null;
let o = new Object();
console.log(typeof s); // string
console.log(typeof i); // number
console.log(typeof b); // boolean
console.log(typeof u); // undefined
console.log(typeof n); // object
console.log(typeof o); // object
所以ECMAScript推出instanceof更准确来确定对象类型,能排除在typeof中检测原始值为object的情况,所有是Object实例化在object下都是true
let colors = []
console.log(person instanceof Object); //
console.log(colors instanceof Array); // true
console.log(colors instanceof Object) // true
console.log(pattern instanceof RegExp); //
作用域
上下文
上下文:每个变量或函数都有自己的上下文,且这个上下文都会有一个关联的变量对象,这个上下文定义的变量或函数都存在这个对象上,我们实际开发无法用代码显示这个关联对象,只有后台解析代码的时候才会有用到(上下文可以理解为身处的地方)
在浏览器中,window对象为顶级全局上下文,这是对var定义的变量,但let跟const定义变量顶级全局上下文不在window上
上下文执行的过程,上级上下文执行到下级上下文,等到下级上下文代码执行完,又会回到上级上下文执行下面代码
作用域链
定义:当我们执行上下文时会创建一个作用域链,这个作用域链决定我们访问的变量对象,就近原则访问最近一个变量对象,逐级往上找变量对象
作用域链增强
作用域链增强:是指在执行到某些语句时,会在该语句所处的作用域链位置前端再创建一个上下文,执行完语句后销毁该上下文(但是其他类型的上下文得在关闭程序时才会被销毁,例如关闭浏览器)
作用域链主要有全局上下文跟函数上下文(第三种时eval()调用内部存在的第三种上下文)
作用域链增强的两种情况
with()语句
try{}catch(){}中的catch
这两个语句都会将括号里面数据作为上下文关联的变量对象,放在作用域链的前端
with语句是指定一个对象作为上下文关联的变量对象
catch则是创建一个新的变量对象,这个变量对象里面包含错误抛出的信息
变量的声明
1.var
var声明的变量会提升,在同一作用域下可以重复声明
2.let与const
let与const声明的变量会有块级作用域,且不能重复声明,且不会提升
const 声明的变量除了引用值,原始值不能更改值,不然会报错,一般推荐用这个来声明变量,除非后期需要更改这个变量
const如果声明的变量想要不能更改值,可以使用Object.freeze来固定其值
const aiqi = Object.freeze({})
aiqi.name="aiqi"
console.log(aiqi.name) //undefined
垃圾回收
1.标记处理
这种标记就是当运行上下文时被用到的变量会被添加标记,不需要用到会被添加待删除标记,此时你访问不到这个变量,当垃圾回收机制运行时,就会释放该变量占有的内存
2.引用计数
引用计数就是当你声明一个变量并给赋予引用值,这个引用值的计数即为1,当这个变量上的引用值被其他引用值覆盖,则上面的引用值计数为0,当引用值计数为0时,等下次垃圾回收程序运行,就会释放引用计数为0的值的内存
这种计数垃圾回收机制有一个问题,循环引用, 就是对象 A 有一个指针指向对象 B,而对象 B 也引用了对象 A,这种情况下两个对象的引用数永远都不会为0,所以这样会有大量的内存得不到释放
解决方法为,当你想要的程序结尾处,将这些会出现循环引用问题的变量赋予null,当垃圾回收程序运行时会将这些值为null的变量清除
3.性能
频繁触发垃圾回收机制会出现性能问题,为此需要合理对何时触发垃圾回收机制进行规定与处理
在 IE 中,window. CollectGarbage()方法会立即触发垃圾回收。
在 Opera 7 及更高版本中,调用 window. opera.collect()也会启动垃圾回收程序。
不推荐自己主动触发垃圾回收
4.内存管理
因为浏览器分配给js的内存比起桌面或其他应用分配给js的内存较小,当然这也是为了安全考虑,所以我需要在用完变量时给其设置值为null,这种操作成为解除引用。
在内存管理方面我们应尽量使用const跟let,因为他们都有以块为作用域,且他们声明的变量在无用时会尽快让垃圾回收机制介入回收
隐藏类:用来与对象关联,以追踪对象的属性特征(谷歌采用)
function Article() {
this.title = 'Inauguration Ceremony Features Kazoo Band';
}
let a1 = new Article();
let a2 = new Article();
在上面代码中,a1与a2共用一个隐藏类,但如果在声明完后,改变他们两个其中一个,就会将将两个变量对应不同的隐藏类,如果考虑性能我们应该尽量让他们共用一个隐藏类,所以尽量避免先创建在补充这种操作
上面问题的解决方法
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也会出现同个构造函数实例的两个变量对应不同隐藏类,这种情况解决方法时将要删除的变量的值设为null即可
function Article() {
this.title = 'Inauguration Ceremony Features Kazoo Band';
this.author = 'Jake';
}
let a1 = new Article();
let a2 = new Article();
a1.author = null;
5.内存泄漏
内存泄漏:是指内存在不用的时候一直存在
1.意外设置全局变量
function aiqi(){
name = 'aiqi'
//这种全局变量只要window对象不被清理它就会一直存在
}
2.定时器引起内存泄漏
let name = 'Jake';
setInterval(() => {
console.log(name);
}, 100);
//只要定时器一直在的话,name占用的内存就不会被清理
3.闭包引起内存泄漏
let outer = function() {
let name = 'Jake';
return function() {
return name;
};
};
只要返回的函数存在就不能清理 name,因为闭包一直在引用着它。
假如 name 的内容很大(不止是一个小字符串),那可能就是个大问题了。
6.静态分配与对象池
合理分配内存,跟避免多次垃圾回收机制触发就能提高内存上的性能问题
减少垃圾回收触发:1.减少对象频繁初始化,2.数组初始化时应设置够用length的数组,这样可以避免垃圾回收多次动态分配触发(动态分配:如果不够用就删除这个数据,在创建个数组)