这一篇博客,主要针对波老师的前端基础进阶前6篇的内容,结合自己看的一些书,对JavaScript中的内存空间、执行上下文、变量对象、作用域链、闭包、this的一个自我总结。如有意见,欢迎交流。
1.内存空间
内存的生命周期:分配内存,使用内存,释放内存
大致分为 栈、堆、队列(相应的数据结构,可以去看相关书籍)
JavaScript中采用的垃圾清理机制:标记清除
垃圾收集器会在运行的时候给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量以及被环境中的变量应用的标记,在此之后再把加上标记的变量都将被视为准备删除的变量。最后,垃圾收集器完成内存的清除工作,销毁那些带标记的值并收回它们所占用的内存空间
JavaScript中5种基本数据类型:undefined、null、String、Number、Boolean,保存在栈内存中
引用类型:Array、Object,保存在堆内存中
基本数据类型的使用,是对值的复制,等于在栈内存中重新开辟一个内存空间
引用类型,是一个引用(指针)指向堆内存中的内存空间地址
2.执行上下文
执行上下文可以理解为函数执行的环境,每一个函数执行时(注意,是在执行时候),都会给对应的函数创建这样一个执行环境
JavaScript中运行环境大致分为三种:
1.全局环境:JavaScript代码运行起来会首先进入该环境
2.函数环境:当函数被调用执行时,会进入当前函数中执行代码
3.eval:(不建议使用,忽略)
因此在一个JavaScript程序中,必定会产生多个执行上下文,JavaScript引擎会以栈的方式来处理它们,这个栈,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。
执行上下文是在函数执行时创建,那么执行上下文的周期又是什么呢?
分为两个阶段:
创建阶段:
在这个阶段中,执行上下文会分别
- 创建变量对象
- 建立作用域链
- 确定this的指向
代码执行阶段:
创建完成之后,就会开始执行代码,这个时候,会完成
- 变量赋值
- 函数引用
- 执行其他代码
执行完成后,出栈,等待垃圾回收机制处理回收
总结:执行上下文概念
- 单线程
- 同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
- 全局上下文只有唯一的一个,它在浏览器关闭时出栈
- 函数的执行上下文的个数没有限制
- 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
3.变量对象(活动对象)
在创建变量对象时,首先创建arguments对象,再检查function函数声明,最后检查var(let、const)变量声明创建属性
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}
}
//有调用,才会进入call-stack
test();
// 创建过程
//执行上下文
testEC = {
// 变量对象
VO: {},
scopeChain: {}
}
// VO 为 Variable Object的缩写,即变量对象
VO = {
arguments: {...}, //注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理
foo: <foo reference> // 表示foo的地址引用
a: undefined
}
作者:这波能反杀
链接:https://www.jianshu.com/p/330b1505e41d
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
未进入执行阶段之前,变量对象中的属性都不能访问!
但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。
4.作用域链
什么是作用域?作用域,可以理解为一套规则,从功能的角度来看,作用域确定了哪些变量可以被函数访问以及确定了this
的赋值;从性能的角度上看,作用域关系到标示符解析的性能
作用域链,就是由当前函数的变量对象及其父作用域的变量对象,形成的链式结构,只能由嵌套子作用域访问父作用域
在ES6出来之前,JavaScript中只有全局作用域与函数作用域,并没有块作用域。
块级作用域类似函数作用域。
5.闭包
闭包是指有权访问另一个函数a作用域中的变量的函数b(在chrome中,a来指代是闭包)
闭包是一种特殊的对象,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量
6.this
this的指向,是在函数被调用的时候确定的。也就是执行上下文被创建时确定的。
如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象。如果函数独立调用,那么该函数内部的this,则指向undefined。但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。
通过call、apply可以显式的指定this
function fn(num1, num2) {
console.log(this.a + num1 + num2);
}
var obj = {
a: 20
}
fn.call(obj, 100, 10); // 130
fn.apply(obj, [20, 10]); // 50
作者:这波能反杀
链接:https://www.jianshu.com/p/d647aa6d1ae6
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
通过new操作符调用构造函数,会经历以下4个阶段。
- 创建一个新的对象;
- 将构造函数的this指向这个新对象;
- 指向构造函数的代码,为这个对象添加属性,方法等;
- 返回新对象。
因此,当new操作符调用构造函数时,this其实指向的是这个新创建的对象
// 定义父级的构造函数
var Person = function(name, age) {
this.name = name;
this.age = age;
this.gender = ['man', 'woman'];
}
// 定义子类的构造函数
var Student = function(name, age, high) {
// use call
Person.call(this, name, age);
this.high = high;
}
Student.prototype.message = function() {
console.log('name:'+this.name+', age:'+this.age+', high:'+this.high+', gender:'+this.gender[0]+';');
}
new Student('xiaom', 12, '150cm').message();
作者:这波能反杀
链接:https://www.jianshu.com/p/d647aa6d1ae6
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。