当一段代码被执行时,js
引擎会先对其编译,并创立执行上下文。
- 当执行全局代码时,会编译全局代码并创立全局执行上下文,整个页面生存周期内,全局上下文只有一份。
- 调用到函数时,函数体内代码编译并创立函数执行上下文,函数完结后,下下文会被销毁。
栈恪守后进先出的准则,比方一条死胡同,进去的多人,只能最初进来的人先出。
var a = 2;
function add(b,c){return b+c}
function addAll(b,c){
var d = 10,
result = add(b,c);
return a+result+d
}
addAll(3,6)
- 创立全局上下文,放最底下:先进。
a = undefined, add = function(){...}, addAll = function(){...}
- 执行全局代码,
a = 2
赋值,上下文变量的a = 2
- 调入
addAll()
,js
编译addAll
函数,并创立其函数执行上下文,压入栈中。参数列表,d = undefined, result = undefined
- 执行
d = 10
,把上下文赋值 - 执行到
add(b,c)
时,创立函数执行上下文,压入栈中,参数列表。 add
函数返回时,add
函数上下文从栈顶弹出,将result
设为add
函数的返回值,add
上下文销毁。addAll
执行相加操作后,返回addAll
执行上下文从栈顶弹出,销毁。只剩全局上下文,完结。
call stack
调用栈,console.trace()
打印函数调用关系。
当栈的执行上下文超过肯定数据会栈溢出。let
const
有块级作用域,当执行到块级时,会在执行上下文的词法环境,词法环境也是一个栈的构造,最外层在栈底一个块级执行结束,该作用域的信息就会从栈顶弹出,销毁。
function foo(){
var myName='思否'
let test1=1;
const test2=2;
var innerBar={getName:function(){console.log(test1)
return myName
},
seeName:function(newName){myName=newName;}
}
return innerBar;
}
var bar=foo();
bar.seeName('思否')
bar.getName()
console.log(bar.getName())
innerBar
是个对象,蕴含 seeName
和getName
这 2 个办法,这 2 个办法是在 foo
函数外部定义的,用了 foo
的myName
和 test1
2 个变量。
词法作用域规定,外部是函数总是能够拜访它们内部函数的变量,尽管 foo
曾经完结,其执行上下文也曾经弹出去销毁了,但因为其变量 myName
和test1
被应用,所以这 2 个变量仍然保留在内存中,是专属于外面的闭包的。
闭包:应用内部函数变量,闭包函数销毁后,下次 js 引擎回收时判断不再用会回收这块内存。
var myObj={
name:' 思否‘,
showThis:function(){
this.name='思否 1'
console.log(this)
}
}
var foo=myObj.showThis;// 相当于赋值指向同一块内存地址,然而调用对象变了,//foo 在全局执行上下文指向 window,所以 foo 函数 this 指向 window
foo()//foo 在 window 全局里
js
的 8
种类型:Boolean,Null,Undefined,Number,BigInt,String,Symbol,Object
。7
种原始类型是存在调用栈中。Object
援用类型实在存在堆中,栈中存的是堆的援用地址。
调用栈的执行上下文调完了,会弹出调用栈,所占用的内存也会被其它占用。而调用栈中援用地址指向的堆空间垃圾回收分为 2 类。V8
把堆分成新生代和老生代,新生代容量1-8M
,调用少的。其它则在老生代。次要是标识对象是否在应用标记齐全回收清理非流动对象,因为非流动对象散布在不同的内存地址,大小也不同,回收之后会有很多内存碎片。新生代有对象区域和闲暇区域,退出的放到对象区域,对象区域满垃圾回收。回收剩下的对象放到闲暇区域,闲暇区域变成了对象区域。老行生代的标记革除,会循环调用栈找对象地址的援用,找不到就是要回收,回收之后让所有剩下对象移向一端汇合在一起内存碎片就变成大空间。js 和回收穿插进行。