JavaScript基础:变量提升和函数提升

156 阅读3分钟

变量提升与函数提升

image.png

var let const 区别

image.png

var 和 let 的作用域规则一样,其声明的变量只在其声明的块或子块中可用

例子: 作用域、重复声明

function varTest() {
  var a = 1;
  {
    var a = 2; // 函数块中,同一个变量
    console.log(a); // 2
  }
  console.log(a); // 2
}


function letTest() {
  let a = 1;
  {
    let a = 2; // 代码块中,新的变量
    console.log(a); // 2
  }
  console.log(a); // 1
}
varTest();
letTest();

例子: 全局绑定

var foo = 'global'
let bar = 'global'
console.log(this.foo) // global
console.log(this.bar) // undefined

暂时性死区 TDZ
当程序的控制流程在新的作用域进行实例化时,在此作用域中用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区

例题

例子1

function b(){};
var b = 11;
typeof b;

预编译后↓

function b;  // => 声明一个function b
var b;       // =》 声明一个变量 b
b = (){};    // =》 function b 初始化
b = 11;      // =》 变量 b 初始化 =》变量初始化没有被提升,还在原位
typeof b;    // number

例子2

var foo = 'hello';
(function(foo){
  console.log(foo);
  var foo = foo || 'world';
  console.log(foo);
})(foo);
console.log(foo);
//
//
//
// 依次输出 hello hello hello

预编译后↓

var foo = 'hello';
(function (foo) {
    var foo;  // undefined;
    foo= 'hello'; //传入的foo的值
    console.log(foo); // hello
    foo = foo || 'world';// 因为foo有值所以没有赋值world
    console.log(foo); //hello
})(foo);
console.log(foo);// hello,打印的是var foo = 'hello' 的值(变量作用域)

例子3


console.log(a, b)

var a =12, b ='666'

function foo(){

    console.log(a, b)

    var a = b =13
  
    console.log(a, b)
}98

foo()

console.log(a, b)

// 输出:
//

/* 
    undefined undefined
    undefined "666"
    13 13
    12 13
*/

例子4

fn();
console.log(v1);
console.log(v2);
console.log(v3);
function fn() {
  var v1 = v2 = v3 = 2019;
  console.log(v1);
  console.log(v2);
  console.log(v3);
}

/*
输出 


2019 
2019 
2019 
Uncaught ReferenceError: v1 is not defined 
/

例子5

console.log('1',fn())
function fn(){
    console.log(1)
}

console.log('2',fn())
function fn(){
    console.log(2)
}

console.log('3',fn())
var fn = '林一一'

console.log('4',fn())
function fn(){
    console.log(3)
}

/* 输出
*   3
*   1 undefined
*   3
*   2 undefined
*   3
*   3 undefined
*   Uncaught TypeError: fn is not a function
/

例子6

let a = 0, b = 0;
function fn(a) {
  fn = function fn2(b) {
    console.log(a, b)
    console.log(++a+b)
  }
  console.log('a', a++)
}
fn(1); // a, 1
fn(2); // 2, 2   5

例子7

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

var b = {
    a,
    c: b
}
console.log(b.c);
//
//
// undefined 10 20 undefined

例子8

var a = 1;
function foo(a, b) {
  console.log(a); 
  a = 2;
  arguments[0] = 3;
  var a;
  console.log(a, this.a, b); 
}
foo(a);
//
//
// 1
// 3, 1, undefined

思考题

带var和不带var的区别

全局作用域中不带var声明变量相当于给window对象设置一个属性。

私有作用域(函数作用域),带 var 的是私有变量。不带 var 会沿作用域链查找。

函数的形参也会进行一次变量提升

var foo = '666';
(function(f){
    console.log(foo);
    var foo = f || 'hello';
    console.log(foo)
})(foo);
console.log(foo)
//
//

// undefined, 666,666

if语句中的变量提升

  • 在当前作用域中不管条件成立都会进行变量提升
  • if 中 () 内的表达式不会变量提升
  • JS 执行到条件语句,判断条件是成立的才会对条件内的函数赋值,不成立不被赋值只被定义成undefined 1
    if()内是单独的作用域?
var y = 1
if(function f(){}){ 
    console.log(typeof f)  // undefined
    y = y + typeof f
}
console.log(y)  // 1undefined

2

console.log(print())    // == window.print()
if(true){
    function print() {
        console.log('666')
    }
}
console.log(print())
/* 输出
    undefined
    666
    undefined
*/

匿名自执行函数在自己的作用域内存在正常的变量提升

var a = 10;
(function(){
    console.log(a)
    a = 20
    console.log(a)
})()
console.log(a)
// 10, 20, 20

非匿名自执行函数的函数名在自己的作用域内变量提升,且修改函数名的值无效

var a = 10;
(function a(){
    console.log(a)
    a = 20
    console.log(a)
})()
// ƒ a(){a = 20 console.log(a)}  ƒ a(){a = 20 console.log(a)}