变量
- 全局变量:在全局作用域下定义的变量
- 私有变量:在私有作用域中定义的变量,只有两种情况是私有变量
- 声明的变量:带var和function的
- 形参
变量提升
当浏览器开辟出供代码执行的栈内存后,代码不是自上而下的立即执行,在此之前需要完成:
把当前作用域里带有var/function关键字的进行提前的声明和定义=>变量提升机制** ,变量提升只发生在当前作用域里面**
带var的只提前声明(declare)不定义 ,类似于'var q'的情况,默认值是undefined
带function的会完成声明和定义(defined),“a=13”定义其实就是赋值,这是让变量和某个值进行关联
let/const定义变量时,不能进行变量提升
私有作用域的变量提升
私有作用域形成后,也不是立即代码执行,需要先进行变量提升,**在变量提升之前需要形参赋**值。
es3/es5语法中,只有全局作用域和函数执行的私有作用域(栈内存),其他大括号不会形成栈内存
带var的区别与不带var的区别
全局作用域
- 不带var,相当于给全局作用域对象window设置一个属性a,变量的值就是属性值,不加VAR的本质是WIN的属性
- 带var的,是在全局作用域下声明一个变量b(全局变量),但在全局下声明的变量也同样相当于给window增加一个对应的属性(只有全局作用域具备此特点)
私有作用域
- 带var的在任何私有作用域变量提升阶段,都声明为私有变量,和外界没有任何的关系
- 不带var的不是私有的变量,会向他的上级作用域查找,看是否为上级的变量,不是的话,继续向上级查找,一直找到window为止(此种机制叫做:作用域链),相当于私有作用域操作的这个非私有变量是一直操作别人的。
特殊情况下的变量提升
只对等号左边进行变量提升
/*1注意函数表达式的情况:
使用函数表达式时,var会进行变量提升,只声明不定义,因此右边的函数处理一样是如此(项目里常用)
条件判断下的变量提升
在条件判断中,带function的在旧版本浏览器中会在变量提升时完成声明和定义,但是在新版本浏览器里,为适应ES6,条件判断带function的不管该条件是否成立,只会完成声明,不会进行定义。
在条件判断中,首先是变量提升,然后才是代码执行。
f = function () {return true;};//=>window.f=...(TRUE)
g = function () {return false;};//=>window.g=...(FALSE)
~function () {
/*
* 变量提升:
* function g; //=>g是私有变量 g=undefined
*/
if (g() && [] == ![]) {//=>Uncaught TypeError: g is not a function (此时的g是undefined)
//=>[]==![]:TRUE g()=>undefined()
f = function () {return false;};//=>把全局中的f进行修改 window.f=...(FALSE)
function g() {return true;}
}
}();
console.log(f());
console.log(g());
/*
* 变量提升:
* function fn;
*/
// console.log(fn);//=>undefined
if (1 === 1) {
console.log(fn);//=>函数本身:当条件成立,进入到判断体中(在ES6中它是一个块级作用域)第一件事并
不是代码执行,而是类似于变量提升一样,先把FN声明和定义了,也就是判断体中代码执行之前,FN就已经赋值
了
function fn() {
console.log('ok');
}
}
// console.log(fn);//=>函数本身
- return 出去的内容不进行变量提升,但是return下面的代码需要进行变量提升
function sum(){
var a=10;
console.log(b);//[function fn]
console.log(c);//undefined
console.log(fn);//fn is not defined
return function(){
console.log("hello");
};
console.log(b);//此处代码不执行
var c=100;
function b(){
var d=10;
}
}
var f=sum();
//
f();//函数的定义发生在变量提升阶段
console.log(a);
- 变量名重复,那么不再进行声明,但是要重新定义
console.log(a);// 函数
var a = 100;
console.log(a);// 100
// var a = 1000;
function a() {
}
fn()// 3
var fn = function () {
console.log(0)
}
fn()// 3 0
function fn() {
console.log(1)
}
fn()// 3 0
function fn() {
console.log(2)
}
var fn = 10
function fn() {
console.log(3)
}
fn();// 报错 10() 不成立
- 匿名函数不进行变量提升
function f(){}
//函数表达式
var fn=function(){};
fn();
//自执行函数定义和执行是一起完成的
(function(){
console.log(1);
})()
var c=10
var c=1;
- es6中的let/const等创建变量或者函数时,不存在变量提升机制
1. let/const切断了全局变量和window属性的映射机制
2. 相同的作用域里,基于let创建的变量不能重复声明相同名字的变量(不论之前如何创建,再次使用let创建都会报错)
3. let/const中没有变量提升,但是有词法解析,在当前作用域代码自上到下执行前,浏览器会进行语法检测,检查所有的变量,当发现有重复的时候
就会抛出错误代码就不会执行了
4. 基于LET创建变量,会把大部分{}当做一个私有的块级作用域(类似于函数的私有作用域)
5. 在基于es6语法时,没有声明变量时,使用typeof检测会直接报错,不是es5之前的undefined,可以解决js的死区
重名问题
- 带VAR和FUNCTION关键字声明相同的名字,是重名(就是同一个FN,只是存储值的类型不一样)
- 重名处理:名字重复,不会重新的声明,但是会重新的定义(重新赋值)[不管是变量提升还是代码执行阶段皆是如此]
- 所谓重复是:不管之前通过什么办法,只要当前栈内存中存在了这个变量,我们使用let/const等重复再声明这个变量就是语法错误
fn();//=>4
function fn() {console.log(1);}
fn();//=>4
function fn() {console.log(2);}
fn();//=>4
var fn=100;//=>带VAR的在提升阶段只把声明处理了,赋值操作没有处理,所以在代码执行的时候需要完成赋值 FN=100
fn();//=>100() Uncaught TypeError: fn is not a function
function fn() {console.log(3);}
fn();
function fn() {console.log(4);}
fn();