预解释/ 变量提升
JS中的数据类型:
基本数据类型: number/ string/ boolean/ null/ undefined 栈内存
引用数据类型: 堆内存
object: {} [ ] /^$/ Date
function
本质区别:基本数据类型是按照值来操作,引用数据类型是按照引用地址来操作。
1. 当浏览器加载HTML页面的时候,首先会提供一个共全局JS代码执行 的环境,即全局作用域(global/ window)。
2. 预解释:在当前的作用域中,JS代码执行之前,浏览器首先会默认的把所有的带var和function的进行提前的声明或者定义
a. 声明(declare)未赋值 默认值是undefined
定义(defined)即赋值
b. var 在预解释的时候只是提前的声明
function ……提前声明并定义
c. 预解释只发生在当前的作用域下。例如:window初始化时只对全局的变量和函数预解释,函数执行的时候对函数内部的变量和子函数进行预解释。
3. JS中内存的分类
栈内存: 用来提供一个供JS代码执行的环境。 —— 作用域(全局作用域/ 私有作用域)
堆内存: 用来存储引用数据类型的值 —— 对象储存的的是属性名和属性值。函数储存的是代码字符串……
作用域链
1. 全局变量和私有变量的区分
a. 在全局作用域下声明(预解释的时候)的变量,是全局变量。
b. 在“私有作用域中声明的变量”和“函数的形参”都是私有的变量。
c. 在私有作用域中,代码执行遇到一个变量,首先判断这个变量它是否为私有变量。是,则和外部作用域没有任何关系。若不是私有变量,则往当前作用域的上级作用域进行查找,若上级作用域也没有此变量,则继续查找,一直到window为止(作用域链)。
若window下也没有,(1)若变量是获取值(如console.log( )输出,报错;(2)设置值,相当于给window添加属性并赋值。正常执行。
2. 当函数执行的时候(直接目的:让函数体中的代码执行),首先会形成一个新的私有作用域,然后按照以下步骤执行:
a. 若有形参,先给形参赋值
b. 进行此私有作用域预解释
c. 私有作用域代码从上往下执行
全局变量的细节问题
1. 在全局作用域中,带var和不带var的关系
区别: 带var的可以进行预解释,所以中赋值的前面执行不会报错;不带var的是不能进行预解释的,在赋值前面执行会报错。
关系: 不带var的变量赋值相当于给window增加了一个和它名字一样的属性名和属性值。带var的变量不仅如此,还说给window全局作用域增加了一个全局变量。
预解释中的变态机制
1. 在if判断中,预解释的时候不管条件是否成立,都要把带var的进行提前声明。
2. 预解释的时候只预解释“=”左边的部分,“=”右边的是值,不参与预解释。
匿名函数之函数表达式:把函数定义的部分当作一个值赋值给我们的变量/元素的某一个事件。
//window下的预解释:var fn;
fn();// Error: fn is not a function;
var fn = function(){
console.log(”ok”);
};
//window预解释
//声明+定义 fn = 1;
//声明 var fn;(不重复声明),不定义.
//声明(不重复声明)+ 定义 fn = 2;
fn();//2
function fn() {
console.log(1);
}
fn();//2
var fn = 10;//Error: fn is not a function
function fn() {
console.log(2)
}
fn();
3. 自执行函数定义的function在全局作用域下不进行预解释,当代码执行到自执行函数的位置时定义与执行一起完成。
4. 函数体中return以下的代码虽然不再执行,但是return语句之后的代码还需要进行预解释;return之内的代码都是返回值,不进行预解释。
5. JS中若变量名字和函数名字重复了,也算冲突。之后声明的赋值会覆盖之前声明的赋值。
如何查找上级作用域
看当前函数是在哪个作用域下定义的,那么它的上级作用域就是谁。和函数在哪执行没有任何关系。
内存释放和作用域销毁/ 垃圾回收机制
1. 堆内存
a. 对象数据类型object或者函数数据类型function在定义的时候首先都会开辟一个堆内存,堆内存有一个引用的地址,如果外面有变量等等指向了这个地址,就说这个内存被占用了,就不能销毁了。
b. 若想要堆内存释放,则只需把所有引用它的变量赋值为null即可。或者在当前堆内存没有任何东西被占用了,那么浏览器会在空闲的时候把它销毁……
2. 栈内存
a. 全局作用域
只有当页面关闭时全局作用域才会销毁。
b. 私有作用域(只有函数执行时才会产生私有作用域)
(1)一般情况下,函数执行会形成一个新的私有作用域,当私有作用域中的代码执行完成后,当前作用域都会主动的进行释放和销毁。
(2)特殊情况下:
当前私有作用域中的部分内存被作用域以外的东西占用了,那么当前的这个作用域就不能销毁了。
a. 函数执行返回了一个引用类型的值,并且在函数外面被一个其他东西给接受了,这种情况下一般形成的私有作用域都不会销毁。
eg:
function fn(){
var num = 100;
return function (){}
}
var f = fn();//fn执行形成的作用域就不能被再销毁了
b. 自执行函数执行形成私有作用域也不能销毁
在一个私有作用域中给DOM元素绑定的方法,一般情况下此私有作用域都不销毁。
eg:
var btn = document.getElementById(“btn”);
(function(){
btn.onclick = function (){
}
})()
c. 返回的函数没有被其他东西占有,可还需要再执行一次,所以暂时不销毁,当返回的值执行完成后,浏览器会在空闲的时候把它销毁。 —— 不立即销毁
eg:
fn()();