1.函数的解析
函数在两个阶段分别干了啥?
内存
栈内存:基本类型(简单数据类型)
堆内存:复杂数据类型
函数是复杂数据类型,函数名在栈内存中,函数体在堆内存中
堆内存的地址会给到函数名所在的栈内存
所以复杂数据类型又叫引用类型
函数定义阶段:
function test(形参){执行的代码}
var test=function(形参){执行的代码}
1.在堆内存中开辟一个空间,栈内存开一个新空间存函数名
2.把函数体内的代码一模一样的存储到这个空间内
3.将堆内存的地址给到函数名所在的栈内存空间
函数调用阶段:
1.函数名所在的栈内存空间,按照地址找到堆内存空间 先判断栈内存空间是否存在,再看栈内存中的堆内存地址是否存在
2.在栈里面开辟一个新的空间,叫执行空间
3.在执行空间内部对形参进行赋值
4.在执行空间内,进行函数内的预解析
5.预解析完成接下来就是在执行空间内部,把函数体内的代码挨个执行一遍
6.执行完毕后,这个执行空间就会被销毁
1.函数预解析
/* 预解析
第一行不需要
第二行需要var num 声明一个变量 但是不赋值
第三行不需要 */
/* 执行
1.console.log(num)
因为在预解析的时候我们已经声明了num变量但是没有赋值,所以这里打印undefined
2.num=100
3.将100输出到控制台
console.log(num); // undefined
var num = 100;
console.log(num);
2.声明式函数进行预解析
<script> test(); function test(){ console.log('i am a function') } test(); //第一行不解析 //第二行解析 //在浏览器声明一个变量 这个变量其实就是在内存中 //开辟一个空间 然后将一个函数赋值给这个变量,也就是把内存交给这个空间 //不解析 //代码执行 //test();因为加了() 就是当作函数来调用 //预解析的时候我们已经声明了一个test变量,并且赋值了一个函数,所以这里边正常调用 //第二步因为预解析的时候已经赋值 //直接到第三步 //跟第一步一样,当作函数来调用 </script>
3.赋值式函数进行预解析
<script> // test(); var test=function(){ console.log('i am a function') } test(); /* 预解析 第一步 不预解析 第二步,告诉浏览器,声明一个变量,起名test,不赋值 第三步,不预解析 */ /* 执行 test();因为没有把函数赋值给变量,也就是不存在test,所以这里报错test is not a function */ /* 把函数赋值给test */ /* 正常把test()当作函数调用 */ </script>
4.认识作用域
<script> //作用域分类 //1.全局作用域 // 一个html文件打开或者一个.js文件,就是一个全局作用域 //全局的这个 // 2.局部作用域 // 只有函数才有局部作用域 // if else if for while switch do while 不叫向部1用域 // 作用域提供了三个机制 // 变量的定义 // 在哪个作用域下就是哪个作用域的私有变量 // 这个变量只能自己用或者后代用父级别不能用 // 变量的赋值 // 自己作用域有先给自己的 // 自己作用域没有往上找父级父级有直接赋值停止查找 // 父级没有继续向上 // 最终到window全局作用域还是没有 // 那么这个变量会被当做全局变量进行赋值 // 变量的访问 // 自己作用域有访问自己的 // 自己作用域没有向上找父亲的如果有停止然后访问 // 如果父级没有继续向上直到找到为止 // 如果最后找到window还是没有 // 那么就会返回变量is not defined // 变量的三种行为 // 变量定义的时候我们需要加上var关键字 // 函数的名字也是标识符的一种 //函数的形参 // 变量的访问 //console.log(变量名) //变量的赋值 //= += -= *= /+ %= **= //形参和实参的交互也是一种赋值 var test=100; function fn(){ var test=200; console.log(test);//200 function fn1(){ var test=300; console.log(test);//30 } fn1(); } console.log(test);//100 fn(); fn1() </script>
5.变量的定义机制
//全局作用域 //局部作用域 //定义在哪个作用域的变量,就是哪个作用域的私有变量 //能在它的作用域及后代作用域都可以使用 //不能在他的父作用域使用 var age=18; function test(){ var name1='zhnagsan' alert(age);//test是全局的子作用域 //所以,它可以是用全局的变量 } //alert(name1);// name1属于test //只能在test内部使用 及后代作用域都可以使用 // 我们现在在它的父级别使用 所以报错 test();
6.变量的访问机制
//虽然变量名一样 但是因为作用域的问题,他们并不是一个变量 var num=100; function test1(){ var num1=200; function test2(){ var num2=300; console.log(num); } test2(); } test1(); // 现在自己的作用域里边找变量 如果有用自己的 // 如果自己没有就去父级作用域查找 父亲有 停止查找 // 父亲没有问爷爷要 爷爷有 用爷爷给的 // 最高到全局作用域 window // 如果window没有 那么久提示你 num is not defined
7.变量的赋值机制
变量的赋值机制
如果自己的作用域有,就先在自己的作用域,直接赋值
如果自己的作用域没有,那么就到父级作用域内查找
这个变量,如果有直接复制如果没有继续查找
以此类推到全局作用域window 如果还没有
就把这个当做全局变量赋值
不加var就变成一个全局变量
以后声明变量,必须加上var,变量记得加var,全局变量除外
局部变量仅仅出现在函数内部
函数执行完毕,变量空间被释放
全局变量 整个文件关闭,变量的空间才会被释放
为了节约空间 声明变量,记得加var