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,转载须征得作者授权。