一、作用域

  • 全局作用域
  • 私有作用域
  • ES6的块级作用域

1、全局作用域

当打开页面的时候,会提供一个給js代码执行的环境全局作用域,默认提供一个最大的window对象
【全局变量】:在全局作用域中保存的变量

全局变量与window的关系

判断一个对象有没有某个属性:属性名 in 对象
  1. var a = 10;
  2. undefined
  3. "a" in window; // 返回值为布尔类型
  4. true
  • 在全局作用域下声明的变量,相当于給window添加的一个属性(属性名就是变量名,属性值就是变量值)
  • window上的方法,可以省略window。(eg:window.alert(“1”可以写成alert(“1”)))
  • ES6中let、const声明的变量,阻断了与window的关系

在全局变量中带var 和 不带var

  • 相同点:都是給window添加属性
  • 不同点:

    1. 带var 有变量提升,不带var 没有变量提升;
    2. 带var 通过delete window.属性名 删除不掉;不带var的可以删除掉(也就说明隐含全局变量严格来说不是真正的变量,而是全局对象的属性,因为属性可以通过delete删除,而变量不可以。) ```

    var a = 10; “a” in window; true b = 20; delete window.a; false delete window.b; true ```

2、私有作用域

函数执行的时候,形成的作用域就是私有的,保存里面的变量不受干扰
【私有变量】:

  • 函数里边声明的变量
  • 形参

二、作用域链

查找变量的时候,先看自己私有作用域中有没有,没有的话,就向上一级作用域继续查找

获取

  • 未定义也未声明的变量,console的时候, 会报错:n is not defined;
  1. function fn() {
  2. a = 10;
  3. console.log(n);
  4. }
  5. undefined
  6. fn(); // Uncaught ReferenceError: n is not defined

赋值

  • 赋值但未声明的变量,会被默认定义未全局变量
  1. function fn() {
  2. a = 10;
  3. n = 20;
  4. console.log(n); // 20
  5. }
  6. fn();
  7. console.log(n); // 20

三、堆栈内存

栈内存

  1. 供js代码运行的环境(window全局作用域、函数执行时的局部作用域)
  2. 存储基本数据类型

堆内存

  • 存储引用数据类型
    • 对象:键值对形式存储
    • 函数:字符串形式存储

四、变量提升

  • 代码执行的时候,首先会形成一个供js执行的环境,接下来在代码自上而下执行之前有一步操作:“变量提升”(会把带var 和带function的变量找出来)
    • var:只声明
    • function:声明并且定义(赋值)变量
  • 变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的变量进行提升,因为此时函数中存储的都是字符串),栈内存形成之后,代码执行之前。
  • 当代码执行遇到创建函数(除匿名函数(函数表达式和立即执行函数)外)这部分代码后,会直接跳过,因为在提升阶段就已经完成函数的赋值操作;函数执行前 首先 生成私有作用域,私有作用域形成之后,先形参赋值,再进行变量提升。

变量提升1.png

变量提升的特殊性!!

1、判断语句:不论条件是否成立,都会进行变量提升

  • VAR 还是只声明,不定义
  • FUNCTION
    • 在IE10以及其以下老版本的浏览器中:声明+定义
    • 在最新版本的浏览器中:只声明
  1. // 例题1
  2. console.log(a);
  3. if(1==2){
  4. var a=12;
  5. }
  6. console.log(a);

(1) 自执行函数在全局作用域下不进行变量提升
  • 最新版本浏览器之条件语句中的变量提升(FUNCTION变量提升时只声明不定义)

自执行函数的变量提升.png

  1. // 例题2 360面试题
  2. f=function(){
  3. return true;
  4. };
  5. g=function(){
  6. return false;
  7. };
  8. ~function(){
  9. if(g()&&[]==![]){
  10. f=function(){return false;};
  11. function g(){
  12. return true;
  13. }
  14. }
  15. }();
  16. console.log(f());
  17. console.log(g());
  18. // 题目解析:(最新版本浏览器中)
  19. 1、在全局作用域中,变量提升:无
  20. 2、代码开始自上往下执行 f 就是window.f=function(){}, g就是window.g=function(){}
  21. 自执行函数,形成私用作用域,变量提升:不管条件是否成立,判断语句里面的代码都会进行变量提升,function g 在新版本浏览器中只声明未定义。 接着走到if语句中,g(),此时的g只声明未定义,相当于undefined(),所以g )会报类型错误,下面的代码都不会执行Uncaught TypeError: g is not a function
  • IE10以及其以下老版本浏览器之 条件语句中的变量提升(FUNCTION变量提升时 声明+定义)

自执行函数的变量提升之老版本.png

(2)条件判断下的变量提升到底有多坑!

在条件判断语句中,如果条件成立,会把执行体当成私有作用域,再进行变量提升
  1. // 例题3
  2. console.log(fn);
  3. if(1==1){
  4. console.log(fn);
  5. function fn(){
  6. console.log("ok");
  7. }
  8. }
  9. console.log(fn)
  10. 解析:
  11. console.log(fn); // undefined 在新版本浏览器中,不管条件是否成功,都会进行变量提升,function 只声明,
  12. if(1==1){
  13. console.log(fn);// fn 函数:在条件判断语句中,如果条件成立,会把执行体当成私有作用域,再进行变量提升
  14. // 再从上往下执行代码,此时fn 定义完成。
  15. function fn(){
  16. console.log("ok");
  17. }
  18. }
  19. console.log(fn) // 条件成立,给fn进行了赋值,打印出fn函数

在条件判断下,如果有function定义的变量,在这个function这个函数后面的更改变量的值,更改的都是私有变量。
  1. // 例题4
  2. var a=0;
  3. if(true){
  4. a=1;
  5. function a(){}
  6. a=21;
  7. console.log(a);
  8. }
  9. console.log(a);

2、只对等号左边的做变量提升

  1. console.log(fn);
  2. console.log(fn(1, 2));
  3. var fn=function (n, m){
  4. return n+m;
  5. }
  6. console.log(fn(3, 4));

3、函数里return 下面的代码本身是不执行的,但是可以进行变量提升;而return后边的代码不进行变量提升

  1. function fn(){
  2. console.log(f2);
  3. return function f1(){
  4. }// return后面的代码 不会进行变量提升
  5. // return下面的代码可以进行变量提升
  6. function f2(){
  7. console.log("f2")
  8. }
  9. }
  10. fn();

4、如果变量名字重复该如何?(重复变量只会声明一次,但可以多次赋值)

对于VAR的不会进行重复声明,但会重新赋值
  1. var num=2;
  2. var num=3;
  3. console.log(num);

对于FUNCTION 在变量提升阶段,遇到重复声明定义的,会进行重新赋值
  1. fn();
  2. function fn(){
  3. console.log(1);
  4. }
  5. function fn(){
  6. console.log(2);
  7. }
  8. fn();
  9. function fn(){
  10. console.log(3);
  11. }
  12. fn=100;
  13. function fn(){
  14. console.log(4);
  15. }
  16. fn();

5、自执行函数 在当前所在的作用域(包含自执行函数的区域)中,不进行变量提升(自执行函数自己所形成的私有作用域照常进行)

  1. function f2(){
  2. console.log("f2");
  3. }
  4. console.log(f1); // 报错 f1 is not defined;
  5. (function f1(){
  6. console.log(a); // undefined, 照常进行变量提升
  7. var a=3;
  8. })();

五、练习题

练习题1、2、7、11、15 ——-重点

  1. - 1 !!!
  1. var ary = [12, 23];
  2. var s = 100;
  3. function fn(ary,s) {
  4. // ary的形参赋值为空间地址(全局ary的空间地址)
  5. s = 200;
  6. ary[0] = 100;
  7. console.log(ary);
  8. console.log(s);
  9. ary = [100]; // ary赋值 重新开启一个空间地址
  10. console.log(ary);
  11. }
  12. fn(ary,s); // ary = 空间地址传入函数
  13. console.log(ary);
  14. console.log(s)
  1. - 2 !!
  1. console.log(a);
  2. console.log(b);
  3. console.log(c);
  4. var a, b, c = 10;
  5. console.log(a);
  6. console.log(b);
  7. console.log(c);
  1. - 3
  1. console.log(a, b);
  2. var a = 12,
  3. b = 12;
  4. function fn() {
  5. console.log(a, b);
  6. var a = b = 13;
  7. console.log(a, b);
  8. }
  9. fn();
  10. console.log(a, b);
  1. - 4
  1. var a = 4;
  2. var f = 3;
  3. function b(x,y,a) {
  4. alert(a); //4
  5. // 形参和arguments存在映射关系;
  6. arguments[2]=10;
  7. alert(a); 10
  8. }
  9. a=b(1,2,f);
  10. alert(a);
  11. console.log(f); //10
  1. - 5
  1. fn();
  2. function fn() {
  3. console.log(1);
  4. };
  5. fn();
  6. function fn() {
  7. console.log(2);
  8. };
  9. fn();
  10. var fn = function () {
  11. console.log(3);
  12. };
  13. fn();
  14. function fn() {
  15. console.log(4);
  16. };
  17. fn();
  18. function fn() {
  19. console.log(5);
  20. };
  1. - 6
  1. var foo='hello';
  2. (function(foo){
  3. console.log(foo);
  4. var foo=foo||'world';
  5. console.log(foo);
  6. })(foo);
  7. console.log(foo);
  1. - 7、(自执行函数没有变量提升) !!
  1. f = function(){return true}
  2. g = function(){return false}
  3. ~function(){
  4. if(g() && [] == ![]){
  5. f = function(){return false}
  6. function g(){return true}
  7. }
  8. }();
  9. console.log(f())
  10. console.log(g())
  1. * 8
  1. var foo = 1;
  2. function bar() {
  3. if (!foo) {
  4. var foo = 10;
  5. }
  6. console.log(foo); //
  7. }
  8. bar();
  1. * 9

var a = 10; (function () { console.log(a); // undefined a = 5; console.log(window.a); // 10 var a = 20; console.log(a); // 20 })()

  1. * 10

console.log(a);
console.log(b);
var a = 1; function a(){} var b= function(){}; console.log(a);

  1. * 11 !!!!

// 函数提升优先级高于变量提升,且不会被同名变量声明时覆盖,但是会被变量赋值后覆盖 fun(); function fun() { console.log(1); } var fun = function () { console.log(2); } function fun() { console.log(3); } fun(); function fun() { console.log(4); } fun(); var fun = function () { console.log(5); } fun();

  1. * 12

console.log(fn); if (1 == 1) { console.log(fn);

  1. function fn() {
  2. console.log("ok");
  3. }

} console.log(fn);

  1. -13
  2. let a=10,
  3. b=10;
  4. fn=function(){
  5. console.log(a);
  6. let a=b=20;
  7. console.log(a,b);
  8. };
  9. fn();
  10. console.log(a,b)
  11. -14
  12. function forEach() {
  13. var i = 0;
  14. for (i = 0; i <= 5; i++) {
  15. console.log(i);
  16. }
  17. console.log(i);
  18. }
  19. forEach();
  20. -15 !!!!!!
  21. var a=9;
  22. function fn(){
  23. a=0;
  24. return function(b){
  25. return b+a++; // 先+ 再 ++ (先return b+a,再a++)
  26. }
  27. }
  28. var f=fn(); // 把函数function(b) { ... }的空间地址赋值给f
  29. console.log(f(5));
  30. console.log(fn()(5)); // fn() 重新执行一次,a又赋值为0
  31. console.log(f(5));
  32. console.log(a);
  33. -16
  34. var i=10;
  35. function fn(){
  36. return function(n){
  37. console.log(n+(++i));
  38. }
  39. }
  40. var f=fn();
  41. f(20);
  42. fn()(20);
  43. fn()(30);
  44. f(30);