Menu

关于变量提升和作用域相关的题目

1.变量提升,且提升的是申明,赋值不会提升

变量提升是js的预解析过程,在代码块执行前会进行隐性操作。

提升只针对var申明的变量和function具名函数。

console.log(a) // undefined,如果后面没有申明变量a,这里就不是undefined而是直接报错:is not defined
var a = 1
// 同理如下
var a1 = a2+1
var a2 = 10
console.log(a1) // NaN

// 上面代码等同于--------------------------------------

var a
var a1
var a2
console.log(a)  // undefined
a = 1
a1 = a2+1
a2 = 10
console.log(a1) // NaN

2.var会忽略后续同一个变量申明,只认可先前的申明

var a = 10
var a 
console.log(a) // 10
var a  = 5
console.log(a) // 5

// 上面代码等同于--------------------------------------

var a = 10
var a // 被忽略,相当于没有这一行
var a // 被忽略
console.log(a) // 10
a  = 5
console.log(a) // 5

// 上面代码等同于--------------------------------------

var a = 10
console.log(a) // 10
a  = 5
console.log(a) // 5

3.变量申明与赋值为`undefined`不同

// 赋值
var a = 'hello world'; 
var a = undefined;
console.log(a); // undefined

// 申明
var a = 'hello world'; 
var a;
console.log(a); // 'hello world‘

4.函数提升优先于变量提升,且全部提升

题目1

console.log(a) // ƒ a() {}
// 这就是为什么函数可以放到下面,在上面依然能调用的原因
// 函数会被提升,且优先于变量,且为全部提升,不只是申明
var a = 1
function a() {}
console.log(a) // 1  在这里a被重新赋值为1

// 上面代码等同于--------------------------------------

function a() {}
var a // 第二次申明被忽略
console.log(a) // ƒ a() {}
a = 1
console.log(a) // 1  在这里a被重新赋值为1

// 上面代码等同于--------------------------------------

function a() {}
console.log(a) // ƒ a() {}
a = 1
console.log(a) // 1  在这里a被重新赋值为1

题目2(重要)

fn() // 5
function fn(){ console.log(1) }
fn() // 5
function fn(){ console.log(2) }
fn() // 5
var fn = function(){ console.log(3) }
fn() // 3
function fn(){ console.log(4) }
fn() // 3
function fn(){ console.log(5) }
fn() // 3

// 上面代码等同于--------------------------------------

function fn(){ console.log(1) }
function fn(){ console.log(2) }
function fn(){ console.log(4) }
function fn(){ console.log(5) }
var fn 
fn() // 5
fn() // 5
fn() // 5
fn = function(){ console.log(3) }
fn() // 3
fn() // 3
fn() // 3

5.局部变量优先于全局变量

var a = 1;
fn();
function fn(){
    console.log(a); // undefined
    var a = 2
    console.log(a) // 2
}

// 第一个打印 undefined 是由于
// 函数申明也会被提升,且优先于变量
// 函数作用域中的变量的申明也会提升到函数最顶部
// 函数作用域中变量为局部变量,作用域链优先取局部值,局部没有才会找外层
// 上面代码等同于--------------------------------------

function fn(){
    var a
    console.log(a); // undefind 局部变量优先
    a = 2
    console.log(a) // 2
}
var a
a = 1
fn()

6.预编译在执行前进行,执行结果不影响预编译

var a = 1;
fn();
function fn(){
    console.log(a); // undefined
    return
    var a = 2
    console.log(a) // 不执行
}

// 上面代码等同于--------------------------------------

function fn(){
    var a
    console.log(a); // undefined
    return
    a = 2  // 不执行
    console.log(a) // 不执行
}
var a
a = 1;
fn();

7.函数作用域

function fn(){
   var aaa = 1
}
fn()
console.log(window.aaa) // undefined 对象上不存在的属性返回undefined
console.log(aaa) // 报错,未定义

注意:对象上找属性是原型链的查找,直接找变量是作用域链的查找。

8.局部找不到会到外层找,且函数在执行时变量才会查找

题目1

fn() // 在这里执行时候,a只是提升申明,还没有赋值
var a = 1;
function fn(){
    console.log(a); // undefind
    a = 2
    console.log(a) // 2
}

// 以上代码等同于----------------------------

function fn(){
    console.log(a); // undefind
    a = 2
    console.log(a) // 2
}
var a
fn() // 在这里执行时候,a已经申明,还没有赋值
a = 1;

题目2

var a = 1;
fn() // 在这里执行,a已经被赋值
function fn(){
    console.log(a); // 1
    a = 2
    console.log(a) // 2
}

// 以上代码等同于----------------------------

function fn(){
    console.log(a); // 1
    a = 2
    console.log(a) // 2
}
var a
a = 1
fn() // 在这里执行,a已经被赋值

题目3(重要)

var n = 100
function fn(){
    n = 200
}
fn()
console.log(n) // 200

// 以上代码等同于----------------------------

function fn(){
    n = 200
}
var n
n = 100
fn()  // 在执行时,全局n为100,执行之后n被重新赋值为200
console.log(n) // 200

9.函数的作用域链外层要看函数申明位置,而不是在哪调用(重要)

函数执行时入栈开辟执行上下文,入栈位置时申明的位置。申明在全局执行上下文就在全局。

只不过时调用(执行)的时候才会去查找变量(第8条),局部没有就向外层查找。外层要看申明在哪里,而不是调用在哪里。

题目1(重要)

var a = 1;

function fn1(){
    console.log(a); // 1
}

function fn2(){
    var a = 2;
    console.log(a); // 2
    fn1()
}

fn2()
console.log(a) // 1

题目2(重要)

var a = 1;

function fn2(){
    function fn1(){
        console.log(a); // 2
    }
    var a = 2;
    console.log(a); // 2
    fn1()
}

fn2()
console.log(a) // 1

10.全局变量只要被申明,就会被挂到window上

只要申明,不需要赋值,就会挂到window

console.log('a' in window)  // false
console.log('sad' in window)  // true
var sad = 10
// in 可以判断对象自身的原型属性以及自身原型属性和方法(不可枚举的也能判断)

// 以上代码等同于----------------------------

var sad
// window.sad = sad  隐式操作
console.log('a' in window)  // false
console.log('sad' in window)  // true
sad = 10

11.var 不存在块级作用域

题目1

由于没有块级作用域,那么块级中的变量是全局变量,同样被提升

console.log('i:',i) // undefined
for(var i=0;i<3;i++){
    console.log("a:",i)
}
console.log("b:",i)
// i: undefined
// a: 0
// a: 1
// a: 2
// b: 3

// 以上代码等同于----------------------------

var i
console.log('i:',i) // undefined
i=0
for(;i<3;){
    console.log("a:",i)
    i++
}
console.log("b:",i)

// 以上代码等同于----------------------------

var i
console.log('i:',i) // undefined
i = 0
while(i<3){
    console.log("a:",i)
    i++
}
console.log("b:",i)

题目2(重要)

请注意,没有块级作用域,不管var变量在哪里申明,都会被提升。

变量提升是预编译的过程,预编译时候代码还未执行,不会有判断的操作。

console.log(a, b)  // undefined undefined
if(true){
  var a = 1
}else{
  var b = 2
}
console.log(a, b) // 1 undefined

// 以上代码等同于----------------------------

var a
var b
console.log(a, b)  // undefined undefined
if(true){
  a = 1
}else{
  b = 2
}
console.log(a, b) // 1 undefined

题目3(重要)

注意:函数在块{}中只提升申明,不提升整个函数

console.log(fn1)  // undefined
console.log(fn2)  // undefined
console.log('fn1' in window)  // true
// fn1()  // 报错:fn1 is not a function

{
    // 进了块中第一件事就是给fn1赋值
    fn1()  // 可运行

    function fn1(){
        console.log(1);
    }
}

if('fn2' in window){
    function fn2(){
        console.log(1);
    }
}

fn2() // 可运行

题目4

console.log(a) // undefined
if('a' in window){
  var a = 110
}
console.log(a)  // 110

// 以上代码等同于---------------------------

var a
// window.a = a 隐式操作
console.log(a) // undefined
if('a' in window){ // true
  a = 110
}
console.log(a)  // 110

12.没有加var的变量都是全局变量,不论在哪赋值,在任何位置都能访问

没有用关键字var赋值的变量会被隐性申明到全局。只有赋值之后才会被申明,不存在提升。

题目1

console.log(abc) // 报错 Uncaught ReferenceError: abc is not defined
fn()
function fn(){
 abc = 109
}

// 以上代码等同于----------------------------

function fn(){
 window.abc = 109
}
console.log(abc) // 在这里fn未执行, abc还未赋值
fn()

题目2

fn()
console.log(abc)  // 109
function fn(){
 abc = 109
}

// 以上代码等同于----------------------------

function fn(){
 window.abc = 109
}
fn()
console.log(abc)  // 这里已执行函数,abc 被赋值

题目3

fn()()
console.log(abc) // 109 
function fn(){
  return function(){
     abc = 109
  }
}
// 不管嵌套多少次都是挂到全局

13.var 连等,只有第一个变量是var申明且赋值,后面变量都是全局变量

题目1

console.log(aaa) // undefined
console.log(bbb) // 报错bbb is not defined
var aaa = bbb = ccc = 10
console.log(aaa, bbb, ccc) // 10, 10, 10

// 以上代码等同于----------------------------

console.log(aaa) // undefined
console.log(bbb) // 报错bbb is not defined
var aaa = 10
bbb = 10
ccc = 10
console.log(aaa, bbb, ccc)

// 以上代码等同于----------------------------

var aaa
console.log(aaa) // undefined
console.log(bbb) // 报错bbb is not defined
aaa = 10
bbb = 10
ccc = 10
console.log(aaa, bbb, ccc)

题目2

bbb = ccc = 10
console.log(bbb, ccc) // 10, 10

// 以上代码等同于----------------------------

bbb = 10
ccc = 10

题目3

function fn(){
    var aaa = bbb = ccc = 10 
    console.log(aaa, bbb, ccc) // 10, 10, 10
}
fn()
console.log(bbb, ccc) // 10, 10

// 以上代码等同于----------------------------

function fn(){
    var aaa =10 
    bbb = 10 // 挂到全局
    ccc = 10
    console.log(aaa, bbb, ccc) // 10, 10, 10
}
fn()
console.log(bbb, ccc) // 10, 10

题目4

var aaa = bbb = {a: 1, b: 2}
console.log(aaa, bbb) // {a: 1, b: 2}, {a: 1, b: 2}
console.log(aaa===bbb)  // true

// 等式后面先执行
// {a: 1, b: 2} 先在堆中开辟地址
// aaa 和 bbb 都指向这个地址

// 以上代码等同于----------------------------

0x666 = {a: 1, b: 2}  // 0x666 代表内存地址
var aaa = 0x666
bbb = 0x666 

14.关于形参和函数返回值(重要)

形参相当于函数内部申明的一个局部变量。

函数返回值默认是undefined。注意是需要执行过后。

var a = 12; b = 13; c = 14;  // 这里注意分号分开和逗号不同,逗号会全部使用var申明
function fn(a){
	console.log(a, b, c); // 10 13 14
	a = 100; // a已经被申明
	b = 200; // b在全局已经被申明
	console.log(a, b, c); // 100 200 14
}
b = fn(10); // 这里注意赋值给b的不是函数,而是函数执行后的返回值
console.log(a, b, c); // 12 undefined 14

// 以上代码等同于----------------------------

var a
function fn(x){
	var a = x
	console.log(a, b, c); // 10 13 14
	a = 100; // 在调用时a已经被申明,在函数作用域,赋值只修改了局部变量
	b = 200; // 在调用时b在全局已经被申明
	console.log(a, b, c); // 100 200 14
}
a = 12; 
b = 13; 
c = 14;
// 这里调用时函数内部的变量才会去查值
b = fn(10); // 这里注意赋值给b的不是函数,而是函数执行后的返回值
console.log(a, b, c); // 12 undefined 14

本文固定连接:https://code.zuifengyun.com/2017/06/3004.html,转载须征得作者授权。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

¥ 打赏支持