变量和函数提升


js引擎会在正式执行代码之前进行一次“预编译”,预编译简单理解就是在内存中开辟一些空间,存放一些变量和函数。 函数的优先级最高

提升过程分析

var a = 1;
function foo() { a = 10; console.log(a); return; function a() {}; }
foo();
console.log(a);

// ------------------------------

var a = 1; // 定义一个全局变量 a
function foo() {
    // 首先提升函数声明function a () {}到函数作用域顶端
    // 然后function a () {}等同于 var a =  function() {};最终形式如下
    var a = function () {}; // 定义局部变量 a 并赋值。
    a = 10; // 修改局部变量 a 的值,并不会影响全局变量 a
    console.log(a); // 打印局部变量 a 的值:10
    return;
}
foo();
console.log(a); // 打印全局变量 a 的值:1
  • 当存在同名的函数和变量时,函数优先。
var foo = 666;function foo(){ console.log(1) };console.log(foo) // 666

// 函数提升 等价于以下
var foo = function(){};foo = 666;console.log(foo) // 666

// 由于foo在预编译时已经定义为函数,因此再遇到变量时不会再提升并负值为undefined。如下错误代码
var foo = function(){};var foo = undefined;foo = 666;console.log(foo)
  • 存在同名的函数时,后面的会覆盖前面的
console.log(test); // 我是函数表达式
function test() { console.log('我是函数'); }
console.log(test); // 我是函数表达式
function test() { console.log('我是函数表达式'); }
console.log(test); // 我是函数表达式
test();

全局和局部变量


  • 局部变量:在JavaScript函数内部声明的变量(使用 var)。只能在函数内部访问它。函数运行完毕,本地变量就会被删除
  • 全局变量:在函数外声明的变量或未使用var定义的变量。网页上的所有脚本和函数都能访问它。全局变量会在页面关闭后被删除
注:值得注意的是:函数外块语句(大括号'{}”中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句内定义的变量还是全局变量
var c = 666;
function test(){
    a = 30;
    var b = 20;
    var c = 10;
    console.log("c=" + c);
}
test() // c=10 函数内改变的只是该函数内定义的局部变量,不影响函数外的同名全局变量的值 
console.log("c="+c); // c=666 
console.log("a="+a); // a=30 这里的a为全局变量,输出a=30
console.log("b="+b); // undefined

----------------------------------------------------------------

if (true) {
    // 'if' 条件语句块不会创建一个新的作用域
    var name = 'Hammad'; // name 依然在全局作用域中
}
console.log(name); // logs 'Hammad'

var和let、const区别


  • var定义的全局变量会挂载在window下,let const声明的变量则不会
  • const 声明之后必须马上赋值,否则会报错
  • const 简单类型一旦声明就不能再更改,复杂类型(数组、对象等)指针指向的地址不能更改,内部数据可以更改。
  • let、const不存在变量提升、不允许在相同作用域内,重复声明同一个变量

全局、函数、块级作用域

  • ES5 只有全局作用域和函数作用域
  • ES6 有块级作用域、全局作用域、函数作用域

块级作用域在如下情况被创建。1、在一个函数内部 2、在一个代码块(由一对花括号包裹)内部

块级作用域可通过新增命令let和const声明,所声明的变量在指定块的作用域外无法被访问

function getValue(condition) {
    if (condition) {
        let value = "blue";
        return value;
    } else {
        // value 在此处不可用
        return null;
    }
    // value 在此处不可用
}
------------------------------------------------------
for (let i = 0; i < 10; i++) {}
console.log(i);
// ReferenceError: i is not defined
// 计数器i只在for循环体内有效,在循环体外引用就会报错。

自由变量的取值

要到创建这个函数的那个域中取值,这里强调的是“创建”

var x = 10
function fn() {
    console.log(x)
}
function show(f) {
    var x = 20
    (function() {
        f() // 10,而不是20
    })()
}
show(fn)
----------------------------------------------------------
// fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。
// 取b的值时,直接在fn作用域取出。
// 取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找
// 结果找到了,所以最后的结果是30
var a = 10
function fn() {
    var b = 20
    function bar() {
        console.log(a + b) //30
    }
    return bar
}
var x = fn(), b = 200
x() // bar()