变量、作用域与内存
1.1、原始值与引用值
Javascript不允许直接访问内存位置,不能直接操作对象所在的内存空间,这样操作的对象一般就是引用,而非对象本身。
1.2、动态属性
非null对象都可以动态添加属性。
let o = new Object();
let s = new String();
o.name = "xxx";
s.age = 19;
console.log(o.name);// xxx
console.log(s.age);// 19
1.3、复制值
原始值赋值到另一个变量,则原始值被复制到新变量的位置。两变量独立的,互不干扰。
引用值赋值给另一个变量,则复制的值只是一个指针,指向存储在堆内存中的对象,两变量实际上指向的都是同一个对象。
这跟java一样,也是存储在堆内存。
1.4、参数传递
ECMAScript中所有函数的参数都是按值传递,跟java不同。
function setName(obj){
obj.name = "NiNa"
obj = new Object();
obj.name = "rora";
}
let person = new Object();
setName(person);
console.log(person.name);
以上这个例子你说按引用传递也没有问题,因为obj = new Object();就是一个局部对象。
1.5、分不清的typeof 和instanceof
typeof主要用来判断一个变量是否为原始类型,因为typeof对于null或者对象都是object。
instanceof 主要用来判断具体是什么类型的对象。
function setName(obj) {}
console.log(typeof setName); // function
console.log(setName instanceof Function);// true
二 执行上下文与作用域
使用var定义的全局变量和函数都会成为window对象的属性和方法。
function setName(obj) {
console.log("test")
}
window.setName(null); // test
var a = "xxxx";
console.log(window.a);// xxxx
作用域链增强:比如with语句和try/catch语句的catch块,kotlin的with估计就是抄袭这里的with吧,语法糖是一样的。
with语句中声明的var 变量会成为函数上下文的一部分,可以作为函数值被返回。
function buildUrl(){
let a = "xx";
with(a){
var b = a + "yy"
}
return b;
}
console.log(buildUrl()); // xxyy
var 可以声明多次,let、const不允许声明多次。
如果一个变量未经声明就被初始化了则会自动添加到全局上下文。小心这种很容易导致内存泄露!!!
function test() {
let a = "xx";
b = a+"yy";
return b;
}
console.log(test());// xxyy
console.log(b);// xxyy
console.log(window.b);// xxyy
常量const 用于Object的话,想让Object的成员也不支持修改,可以使用Object.freeze({});
const o3 = Object.freeze({
"name":"aaa"
});
o3.name = 'bb';
console.log(o3.name); // aa
三 垃圾回收
javascript使用垃圾回收的语言,是由执行环境在负责代码执行时管理内存。跟java类似。
判断一个对象是否是垃圾:可达性分析和引用计数(循环引用hold不住)。
标记清除:最常用, 内存碎片太多就用标记整理。(怎么判断一个对象是垃圾,就跟java里面一样用GCroot 链条,对象不可达就是垃圾了,可以被清除。)
标记整理、复制算法、分代收集:(新生代 --复制算法,老年代--标记清除和标记整理)
并发标记-清除算法:并发垃圾回收算法,用于减少垃圾回收对应用程序的影响。它通过在应用程序运行时并发执行标记和清除阶段来实现。
3.1、内存优化细节
3.1.1、通过const和let声明,块作用域,可能更早的让垃圾回收程序介入。
3.1.2、V8 javascript引擎的隐藏类和删除操作
隐藏类就是V8在将解释后的javascript代码编译为实际的机器码时会利用“隐藏类”,如果多个实例对象共享一个隐藏类,则算优化,不共享则会创建多个隐藏类。
不共享的场景:
a、动态添加属性(其实可以直接在构造函数就预添加属性)
b、动态删除属性。(其实直接置null该属性,不要调用delete obj.name即可);
3.1.3 内存泄露
-
意外的全局声明,就是没有用var、let、const等修饰的变量,就自动搞成window的属性了。
-
变量捕捉 (kt也有这种)
function outerFunction() {
let outerVar = 10;
function innerFunction() {
console.log(outerVar); // 内部函数捕捉了外部函数的 outerVar
}
return innerFunction;
}
const capturedFunction = outerFunction();
capturedFunction(); // 输出 10
你能想到capturedFunction只要存在,这个outerVar如果是超大字符串的话,那么内存就一直占用着。
3.1.4 静态分配与对象池
静态分配 就是指定size的数组保存一定数量的对象。但是javascript中的数组size是可以动态改变的,这个跟java不一样呢。
对象池复用对象,如果对象占用内存大的话,频繁创建是要避免的,此时可以用对象池。