变量
代码中的变量提升
/* 你应该见过下面的类似代码,那你知道这是为什么*/
console.log(a) // undefined
var a = 10
定义:变量提升是当栈内存作用域形成时,JS代码执行前,浏览器会将带有
var, function关键字的变量提前进行声明 declare(值默认就是 undefined),定义 defined(就是赋值操作),这种预先处理的机制就叫做变量提升机制也叫预定义。 在变量提升阶段:带var的只声明还没有被定义,带function的已经声明和定义。所以在代码执行前有带var的就提前声明,比如这里的a就赋值成undefined,在代码执行过程中遇到创建函数的代码浏览器会直接跳过。
带 var 和不带 var 的区别
-
全局作用域中不带
var声明变量虽然也可以但是建议带上var声明变量,不带var的相当于给window对象设置一个属性罢了。 -
私有作用域(函数作用域),带
var的是私有变量。不带var的是会向上级作用域查找,如果上级作用域也没有那么就一直找到 window 为止,这个查找过程叫作用域链。 -
全局作用域中使用
var申明的变量会映射到 window 下成为属性。
面试题
a = 12 // == window.a
console.log(a) // 12
console.log(window.a) // 12
var a = b =12 // 这里的 b 也是不带 var 的。
/* 相当于*/
var a = 12;
b = 12
console.log(a, b) //undefined undefined ,因为变量提升var a b 到这里还没有给值
var a =12, b ='ld'
function foo(){
console.log(a, b) undefined 'ld'
var a = b =13 //这里a是函数的局部变量,b还是全局变量,因此a提升到函数顶部没有给值,了拿到的是window上的值
console.log(a, b) //13 13
}
foo()
console.log(a, b) //12 13
console.log(a, b) //undefined undefined
var a =12, b = 'ld'
function foo(){
console.log(a, b) //12 'ld'
console.log(a, b) //12 'ld'
}
foo()
console.log(a, b) //12 'ld'
a = 2
function foo(){
var a =12;
b = 'ld'
console.log('b' in window)
console.log(a, b)
}
foo()
console.log(b)
console.log(a)
a = 2
function foo(){
var a =12;
b = 'ld'
console.log('b' in window) //true
console.log(a, b) //12 'ld'
}
foo()
console.log(b) //ld
console.log(a) //2
这道题注意带var和不带var区别
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);
}
等号左边下的变量提升
print()
function print(){ console.log('ld') } print()
//var print
print() //print1 is not a function
var print = function() {
console.log('ld')
}
print()
//同样由于变量提升机制带 `var` 的 print 是一开始值是 `undefined`,所以 print() 这时还不是一个函数,所以报出 类型错误TypeError
条件判断下的变量提升
console.log(a)
if(false){
var a = 'ld'
}
console.log(a)
**在当前作用域中不管条件是否成立都会进行变量提升**
- if 中
()内的表达式不会变量提升
var y = 1
if(function f(){}){
console.log(typeof f) // undefined
y = y + typeof f
}
console.log(y) //1undefined
console.log(print()) // == window.print()
if(true){
function print() {
console.log('ld')
}
}
console.log(print())
console.log(a) //undefined
console.log(p()) //p is not a function
if(true){
var a = 12
function p() {
console.log('ld')
}
}
if(true) {
console.log(print()) //
function print() {
console.log('ld')
}
}
console.log(print())
if(!("value" in window)){
var value = 2019;
}
console.log(value); //undefined
console.log('value' in window); //true
原因:不管条件是否成立带 `var` 的变量提升,当前在全局作用域 `value` 就是 `window` 的属性,所以结果显而易见输出 `undefined 和 true`
var fn = 12
function fn() {
console.log('林一一')
}
console.log(window.fn)
fn()
//解释
var fn
var fn function
给值阶段 fn=12
因此是window.fn是12 函数fn已经被给值12,所以最后fn()会报出 fn is not a function
重名问题下的变量提升
带 var 和带 function 重名条件下的变量提升优先级,函数先执行
在
var 和 function同名的变量提升的条件下,函数会先执行。所以输出的结果都是一样的。换一句话说,var 和 function的变量同名var会先进行变量提升,但是在变量提升阶段,函数声明的变量会覆盖var的变量提升,所以直接结果总是函数先执行优先。
console.log(a); //ƒ a(){
console.log(1);
}
var a=1;
function a(){
console.log(1);
}
console.log(a); //ƒ a(){
console.log(1);
}
function a(){
console.log(1);
}
var a=1;
console.log('1',fn())
function fn(){
console.log(1)
}
console.log('2',fn())
function fn(){
console.log(2)
}
console.log('3',fn())
var fn = 'ld' //因为在这里把fn函数变成了字符串,因此下面4中fn就报不是函数了
console.log('4',fn()) //fn is not a function
function fn(){
console.log(3)
}
3
VM473:1 1 undefined
VM473:16 3
VM473:6 2 undefined
VM473:16 3
VM473:11 3 undefined
var a=2;
function a() {
console.log(3);
}
console.log(typeof a); //Number **JS 代码自上而下执行时,`a` 被赋值成 2,输出就是 `number` 型**
console.log(fn);// fn(){} //原因是变量提升比函数在前
var fn = 2019;
console.log(fn);
function fn(){}
/*
var fn 变量
var function fn(){}
fn=2019
*/
console.log(fn); //undfined
var fn = 2019;
console.log(fn); //2019
var fn=function fn(){}
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);
fn(2);
let a
var b fn
a=0 b=0
fn= (a){}
fn(1)
fn=fn2(b){
console.log(a,b)
console.og(++a+b)
}
打印 console.log('a',a++) // a,1
fn(2) //这时候就执行fn=fn2(b)这个函数了,因为就打印里面2个console.log,这时候a=1,b传进去是2 打印出 2,2 5
函数形参的变量提升
function fn(b){
//这里开参的变量提升等于是在这里var b = undefined;
console.log(b);
}
fn(45);
var a = 1;
function fn(a) {
console.log(a)
var a
console.log(a)
}
fn(a);
//变量提升阶段
全局 var a
fn(){}
fn函数局部变量提升
var a
给值执行阶段
fn(a) 把函数里面的console.log(a) 直接打印出来的就是传进去的实参 1
var foo = 'ld';
(function(f){
console.log(foo); //undfined
var foo = f || 'hello';
console.log(foo) //ld
})(foo);
console.log(foo) //ld
变量提升阶段
var foo=undefined
函数内部变量提升
var foo=undefined
执行阶段从上到下、赋值阶段
全局foo='ld'
函数立即执行进入,局部赋值
console.log(foo) //undfined
foo=形参传进来的f ‘ld’
console.log(foo) // 'ld'
全局下面console.log(foo)//'ld'
var foo = 'ld';
(function(foo){ //注这里传进来已经有值,里面打印的就是这个值
console.log(foo); //ld
var foo = foo || 'world';
console.log(foo) //ld
})(foo);
console.log(foo) //ld
分析:
变量提升阶段
var foo=undefined
执行阶段、赋值阶段
僵尸foo='ld'
立即执行函数里
var foo=undefined
console.log(foo) //‘ld’ 原因是传进来的参数是ld
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);
分析:
变量提升阶段
var a=undefined
var b=undefined
执行阶段从上往下,到赋值
a=10
函数立即执行,里面变量提升
var a=undefined
console.log(a) //undfined
a=5 //函数私有变量
console.log(window.a) //10
a=20 //函数私有变量
console.log(a) //20
b={a,c:b} //b={a:10,c:undfined}
console.log(b.c) //undfined
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);
分析过程:
var a=undefined
foo function(){}
执行过程 赋值
a=1
foo(a) //foo(1)
function foo(1,b){
var a=undefined //变量提升
console.log(a) //1 输出的是传进来的参数a
a=2 把a赋值2
arguments[0]=3 //把第一个参数赋值3 也就是传进来的a
var a 因为变量提升已经做过一次了,这里注意点 var a 在形参阶段声明一次后不会再声明即可
console.log(a,this.a,b) //3,1,undfined //注this是window,b没有传进来值
}
匿名自执行函数
var a = 10;
(function c(){
})()
console.log(c)
// Uncaught ReferenceError: c is not defined 匿名自执行函数不会变量提升
var a = 10;
function c(){
}
console.log(c) //function c(){} //函数会变量提升
在函数内注意加只有声明var定义的变量就是局部变量和外面没有关系,但是如果没有var那就是全局的,除有传参数的情况下
var a = 10;
(function(){
console.log(a) //10 全局
a = 20
console.log(a) //20
})()
console.log(a) //20
看下面这个代码就是区别,加了var,就是局部了结果就不同了
var a = 10;
(function(){
//var a=undfined 变量提升局部变量
console.log(a) //undfined
var a
a = 20
console.log(a) //局部变量 20
})()
console.log(a) //全局变量10
非匿名自执行函数-全局函数不会提升
var a = 10;
(function a(){
console.log(a) //f a(){console.log(a) a=20 console,log(a)} //**而且非匿名自执行函数名是不可以修改的,即使修改了也不会有任何作用,严格模式下还会报错**
a = 20
console.log(a) //**而且非匿名自执行函数名是不可以修改的,即使修改了也不会有任何作用,严格模式下还会报错**因些这里还是第一次的赋值函数而不是20
})()
解释:
全局变量提升
var a=undefined
赋值 执行阶段
a=10
执行非匿名自执行函数内部
变量提升
变量a=undefined
函数a=(){}
函数赋值阶段
a=20 接着a又赋值函数 (){
console.log(a)
a = 20
console.log(a)
}
匿名和非匿名自执行函数区别
//匿名函数
var a = 10;
(function (){
console.log(a) //undfined
})()
//非匿名函数
var a = 10;
(function a(){
console.log(a) //ƒ a(){console.log(a)}
})()
匿名函数
var a = 10;
(function (){
console.log(a)
a = 20
})()
console.log(a)
VM9715:3 10
VM9715:6 20
//非匿名函数
var a = 10;
(function a(){
console.log(a)
a = 20
console.log(a)
})()
console.log(a)
VM9683:3 ƒ a(){
console.log(a)
a = 20
}
VM9683:6 10
//这种非匿名函数的情况下在自执行函数内部函数名本身就是a会做为内部函数的提升变量,而且赋值只能是一次,后面再赋值的不会修改,例如下面a=20就赋值失败了,而且内部的a=20也不会影响外层全局的a
接着再来点面试题,我以自己的理解写出了执行顺序过程
console.log(a, b, c); //
var a = 12,
b = 13,
c = 14;
function fn(a) {
console.log(a, b, c);
a = 100;
c = 200;
console.log(a, b, c);
}
b = fn(10);
console.log(a, b, c)
解释:
变量提升
var a=undefined b=undefined c=undefined fn=function(){}
开始从上往下执行,赋值
console.log(a,b,c) //undefined, undefined ,undfined
a=12 b=13 c=14 第一步赋值
b=fn(10) 执行fn函数进入函数内:
内部有传入a参数10
console.log(a,b,c)// 10,13 ,14
私有变量a=100 全局c=200
console.log(a,b,c)// 100,13,200
函数结束
console.log(a,b,c) //全局最后12,undefined,200
undefined undefined undefined
VM92:6 10 13 14
VM92:9 100 13 200
VM92:12 12 undefined 200
var a=1;
var obj ={
name:"tom"
}
function fn(){
var a2 = a;
obj2 = obj;
a2 =a;
obj2.name ="jack";
}
fn();
console.log(a);
console.log(obj);
分析:
全局变量提升:
var a=undefined obj={} fn=function
从下往下执行,赋值
a=1 obj={name:'tom'}
fn() 进入函数内部:
没有参数,看有没有var有,就变量提升
var a2=undefined
a2=1
obj2=obj 指向
a2=a
obj2.name='jack'
函数结束
console.log(a) //1
console.log(obj) //{name:'jack'}
var a = 1;
function fn(a){
console.log(a)
var a = 2;
function a(){}
}
fn(a);
分析:
全局变量提升:
var a=undefined fn(){}
执行赋值:
a=1
fn(1)
进入fn函数中
var a=function(){}
console.log(a) //undfined
a=2:
console.log(a);
var a=12;
function fn(){
console.log(a);
var a=13;
}
fn();
console.log(a);
分析:
全局变量提升:
var a=undefined fn=function
执行赋值:
a=12
fn() 进入函数中:
var a=undefined
console.log(a) //undfined
a=13
函数结束
console.log(a) //12
console.log(a);
var a=12;
function fn(){
console.log(a); //12
a=13;
}
fn();
console.log(a); //13
console.log(a); //a is not defined 没有var 变量不会提升
a=12;
function fn(){
console.log(a);
a=13;
}
fn();
console.log(a);
var foo='hello';
(function(foo){
console.log(foo);
var foo=foo||'world';
console.log(foo);
})(foo);
console.log(foo);