window是全局作用域:我们成为栈内存;2个作用,供全局使用,1、先预解释当前作用域下的var和function,2、储存当前var的值;

 

function预解释的时候,会开辟的内存空间,(我们叫堆内存;)并且把function内的代码当做字符串储存在堆内存内;

代码执行期间:

function执行的;会开辟一个新的内存地址,这个新的空间内存叫栈内存,这时候的的作用域是私有作用域;里面的var也会再次预解释,这时候的变量是私有变量;(这种机制叫闭包;)

下面是一个预解释面试题;

<script>
console.log(a);
var a=12;
function sum(){
console.log(a);
var a=13;
console.log(a);
}
sum();
console.log(a);
//答案是:undefined、undefined、13、12;
</script>

原理分析

  • //首先,当浏览器加载我们页面的时候,会提供一个供Js执行的环境(称之为作用域,是一个栈内存),我们把开始的这个称之为全局作用域(也称为window),大的作用域永不销毁,只有当前当前页面关掉了(一个浏览器可以开很多页面,并不是关闭浏览器才销毁的,是关闭当前网页,window作用域就销毁的),这个window作用域才会销毁;我们把在全局作用域下,声明的变量叫做全局变量;全局变量可以在页面也任何的位置使用,或者修改;
  • 在当前作用域下,JS代码执行之前,首先把所有带var和function关键字的提前的声明或者定义;;(带var的只是提前声明,只声明不赋值;默认值是undefined;带function的是提前声明+定义),我们把这种机制叫预解释(或者叫变量提升)
  • 带funtion的预解释:开辟一个新的内存地址;然后将函数中的的代码当做字符串储存;我们把存储数据的叫堆内存;这个时候预解释完成;
  • 预解释完成;代码从上到下加载;分别给变量定义赋值(带var的情况;);如果遇到function sum(){}定义的这个部分,直接跳出即可;如果遇到的是函数的执行,则进行下面的处理;
  • 开辟一个新的私有作用域(栈内存)、用来执行函数中的js代码;
  • 在执行代码前 ,当前私有作用域下也要进行预解释,把所有在当前私有作用域下带var和function的进行提前的声明或者定义;如果是在私有作用域中预解释的了我们称之为私有变量或私有函数;
  • 函数中的私有的,函数外面不能直接使用的,我们把这种机制叫做闭包;
  • 思考:闭包内不带var的变量赋值,和全局情况下不带var的变量赋值是什么流程?
  • 预解释是发生在当前作用域下的;

预解释的知识点:

  • 如果判断他的上一级作用域;
  • 看存储这个函数的堆内存在哪一个作用域(A)下,那么这个函数的执行的上一级作用域就是A;(有些时候函数A内套函数B,套函数C,而C可能是在全局作用域内拿出来运行的,这个他的作用域是谁??)
  • 如何判断是私有的还是全局的;
  • 在一个函数中只有预解释声明过的和形参是定义过的,是私有的;否则往他的上一级作用域找,如果上一级作用域也没有,则继续找;一直找到window为止;

题目修改:去掉闭包内的一个var;

<script>
console.log(a);
var a=12;
function sum(){
console.log(a);
a=13;//没有var;没有预解释过,也不是形参。所以不是私有变量的;要往上一层找,一直找到window为止;
console.log(a);
}
sum();
console.log(a);
//答案是:undefined、12、13、12;
</script>

得到的结果是完全不一样的;

再次修改:两个var都去掉;

<script>
console.log(a);//此时是没有预解释过a的,a就是没有定义的;会报错; a is not undefined
a=12;//也没有var。a is not defined;下面代码会都不执行了。前提是没有进行异常捕获处理;
function sum(){
console.log(a);
a=13;//没有var;没有预解释过,也不是形参。所以不是私有变量的;要往上一层找,一直找到window为止;
console.log(a);
}
sum();
console.log(a);
//答案是:Uncaught ReferenceError: a is not defined;;就是a is not defined;其它都没有了。
</script>

这时候就会报错了;

如果再再次修改:注释掉第一行;

//console.log(a);

答案是

<script>
//console.log(a);
a=12;//也没有var。a is not defined;下面代码会都不执行了。前提是没有进行异常捕获处理;
function sum(){
console.log(a);
a=13;//没有var;没有预解释过,也不是形参。所以不是私有变量的;要往上一层找,一直找到window为止;
console.log(a);
}
sum();
console.log(a);
//答案是:12/13/13
</script>

带var不带var的区别;

全局作用域下才有下面这种一个机制的;

  • var a=12;//在全局作用域下定义了一个变量a=12,相当于给window增加一个属性名a;属性值是12;
  • a=12//就是给window增加了一个属性名a,值是12;(严格模式下是会报错的;)
  • 区别:上面的预解释了,下面是没有预解释;

再再在次修改是:

<script>
//console.log(a);
//a=12;
function sum(){
console.log(a);
a=13;
console.log(a);
}
sum();
console.log(a);
//答案是:Uncaught ReferenceError: a is not defined
</script>

再再再次修改:

<script>
//console.log(a);
//a=12;
function sum(){
//console.log(a);
a=13;//当前这种情况是属于给window增加一个属性值;属性值是13;
console.log(a);
}
sum();
console.log(a);
//答案是:13,13、
</script>

——————–

面试题思路;

<script>
var a=12;
function fn(){
var a=13;
function ff(){
a++;
console.log(a);
}
return ff;
}
var f=fn();
f();
console.log(a);
//答案:14/12。
// ff是存在fn中的;ff里面的a是私有的。向上级找,也就是fn里面;
</script>

虽然f()在外面执行,但是他的上级作用域还是fn(),a是要在上级作用域里找的;图解0526

————

预解释是毫无节操的一种机制;

<script>
if(!("a" in window)){
var a=12;
}
console.log(a);//结果是undefined,而不是报错。
</script>
  • 1、预解释是发生在当前作用域下的;
  • 2、如果有if判断;不管条件是否成立,都要预解释;
  • 上面题目的分析;
  • 规定:var a=12;相当于给window增加一个属性名,属性值是12;
  • in:用来检测某一个属性名是否是这个对象的;”a”in window 的意思是:判断window下有咩有”a“这个属性名;属于的话返回true,不属于返回false;
  • 预设值的时候;if判断里面的a是会被预解释的,相当于给window加一个属性名;
  • 执行代码;if(!(“a” in window))。这个条件是false,所以不继续执行。
  • 这个时候console.log(a)。这时候a已经被预解释了,只是a没有定义,是undefined、所以结果是undefined;(并非报错的那种a is undefined)【分析完成】
  • 3、预解释值发生在=左边的,右边的不进行预解释;
  • <script>
    function fn(){};//这个是function声明+定义的;
    var fn=function(){}//fn预解释,只声明了fn的变量;
    //虽然两个一样的用法;但是预解释不一样的;
    </script>

第二个例子:

<script>
fn();// fn is not a function
var fn=function(){}
//fn();
</script>

如果改成下面,就不会报错;会执行fn()

<script>
//fn();// fn is not a function
var fn=function(){}
fn();
</script>

就是下面这样的;

<script>
//fn();// fn is not a function
var fn=function(){
console.log("123")
}
fn();
//控制台是显示123
</script>

接上面的继续总结;

  • 4、只有预解释发生在同一个脚本快,不能夸脚本
  • <body>
    <script>
    console.log(a);//预解释只发生在同一个脚本快,不能夸脚本;//所以此时a is not defined
    </script>
    <script>
    var a=13;
    </script>
    </body>
    <body>
    <script>
    var a=13;
    </script>
    <script>
    console.log(a);//这个时候是弹出来13、以后写代码,不同代码块的顺序一定要注意;因为第一个脚本快已经赋值了(代码块的值是可以用的。这时候已经过了预解释阶段了,是定义过的了)
    </script>
    </body>
  • 5、自执行函数不进行预解释   
  • ;(function(){})();//自执行函数不解析预解释
  • 6、函数中,return后面的代码也要进行预解释;
  • 7、在预解释的时候,如果发现名字重了,不重新的声明,但是要重新的定义;
  • function a(){
    //变量的名a和函数名a,如果重名后也是冲突的;
    }
    </script>
    下面是一个经典的思路,一个不错的面试题:
<script>
fn();
var fn=13;
fn();
function fn(){
console.log(100);
}
fn();
//答案:100、 fn is not a function(只有函数才能执行;fn()相当于13(),这个是不能执行的;)、后面的不执行了
</script>

留意:报错的fn is not a function也有浏览器弹出 number is not  function

练习做题;

<script>
console.log(fn);
function fn(){
console.log(1);
}
console.log(fn);
var fn;
console.log(fn);
function fn(){
console.log(2);
}
console.log(fn);
var fn=10;
console.log(fn);
</script>

·····