1.JS预解析 #

当浏览器加载html页面的时候,会先提供一个供全局JS代码执行的环境,称为全局作用域(global/window)

1.1 变量的预解析 #

变量在预解析时仅提前声明

console.log(num);  //undefined
var num = 12;
console.log(num);  //12
if(false){
  var a = 13;
}
console.log(a);  //undefined

预解析时仅var num; 告诉浏览器在全局作用域中有一个num变量了,此时还未定义
不管if条件成立不成立,里面有var声明都进行预解析

if(!("num" in window)){
  var num = 12;
}
//条件成立才会赋值
console.log(num);  //undefined

1.2 函数的预解析 #

1.2.1 预解析时提前声明和定义 #

showMsg();  //这是第二个函数
function showMsg(){
  alert("这是第一个函数");
}
showMsg();  //这是第二个函数
function showMsg(){
  alert("这是第二个函数");
}

函数执行时先创建自己的私有作用域,然后如果有形参,先给形参赋值,再进行私有作用域的预解析,再代码从上至下执行
私有变量:在私有作用域中声明的变量和函数的形参

1.2.2 匿名函数的预解析 #

var fn = function(){};在JS预解析时,在匿名函数表达式中只预解析等号(=)左边的,右边的作为值不参与预解析.即var fn;

fn();  //报错 TypeError: fn is not a function //该项不为函数,不能执行
var fn = function(){
  console.log("ok");
}

1.2.3 自执行函数的预解析 #

自执行函数定义的function在全局作用域下是不进行预解析的,当代码执行到这个位置时,定义和执行一起完成

1.2.4 return后的预解析 #

函数体中return下面的代码虽然不再执行了,但是也会预解析;return后面跟着的都是返回值,所以不进行预解析

function fn(){
  console.log(num);
  return function(){};  //该function作为返回值不进行预解析
  var num = 12;  //该num作为声明变量进行预解析
}
fn();

1.3 全局变量 #

带var的变量可以进行预解析,在赋值之前执行不会报错
不带var的变量是不能进行预解析的,在赋值之前执行会报错

console.log(num);  //报错:num is not defined
num = 12;
num = 12;
console.log(num); // 12  window.num

全局作用域中增加了一个全局变量num,相当于给window增加了一个num属性,值为12

注:JS中在不做任何特殊情况处理下,上面的代码报错,下面的代码都不再执行

在JS中,变量名和函数名重复了,是冲突情况;在预解析时如果名字已经声明过了不需要重新声明,但会重新赋值

var fn = 12;
function fn(){...}
//这两个fn是一个名字,最终只能保留一个值,最终fn为变量
fn();
function fn(){console.log(1)}
fn();
var fn = 12;
fn();
function fn(){console.log(2)}
fn();

2.作用域链 #

2.1 私有作用域 #

console.log(total);  // undefined
var total = 0;
function fn(num1,num2){
  console.log(total);  //undefined
  var total = num1+num2;
  console.log(total);  //300
}
fn(100,200);
console.log(total);  //0

2.2 查找上级作用域 #

  1. 如何查找当前作用域的上级作用域:看当前函数是在哪个作用域下定义的,那么它的上级作用域就是谁
  2. 和函数在哪里执行没关系,和函数在哪里定义有关系
var num = 10;
function fn(){
    var num = 100;
    return funciton(){
        console.log(num);
    }
}
var f = fn();
f();
~function(){
  var num = 1000;
  f();
}();

f()的值和执行环境无关,和定义环境有关

3.内存释放和作用域销毁 #

3.1 堆内存 #

3.2 栈内存(作用域) #

3.2.1 栈内存不销毁情况一 #

函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他东西接收了则不销毁

function fn(){
    var num = 100;
    return function(){
      console.log(num);
    }
}
var f = fn();  //返回值被f接收,fn不能被销毁

3.2.2 栈内存不销毁情况二 #

自执行函数形成的私有作用域在这种情况下也不销毁

var oDiv = document.getElementById('div1');
~function(){
oDiv.onclick = function(){

}
}();

注:通过DOM方法获取的元素、元素集合都是对象数据类型的值

var oDiv = document.getElementById('div1');

3.2.3 栈内存不销毁情况三 #

不立即销毁:fn返回的函数没有被其他东西占用,但还需要执行一次,所以暂时不能销毁,当返回的值执行完后浏览器在空闲时间把它销毁

function fn(){
    var num = 12;
    return function(){

    }
}
fn()(); //表示执行函数fn,再执行返回值的函数

4.this关键字 #

4.1 window下的this #

函数执行首先看函数名前有没有点,有的话前边是谁this就是谁,没有的话就是window

function fn(){
    console.log(this);
}
var obj = {fn:fn};
fn();  // 方法名前没有点
obj.fn();  //方法名前有点
function sum(){
    fn();
}
sum();  //与执行环境无关
var aa = {
    sum:function(){
      fn();
    }
}
aa.sum();  //this --> window与执行环境无关

4.2 自执行函数this #

自执行函数的this永远是window

4.3 指定元素this #

给元素的某一事件绑定方法,当事件触发的时候,执行对应方法时当前this指该元素

document.getElementById('div1').onlick = fn;  //this ==> #该方法
document.getElementById('div1').onlick = function(){
    //该方法的this指#div1
    fn(); //该方法的this依然是window
}