预解释和作用域

177 阅读5分钟

预解释/ 变量提升

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()();