声明提前
var
在ES6之前JS使用函数作用域,使用var声明的变量在声明它的函数体以及该函数体中的嵌套函数体内都可以访问 。
函数内所有使用var声明的变量在函数体内可见,这意味这变量在声明之前已经可用,这个特性叫做声明提前,即函数中所有变量及函数声明都回提升至函数体的顶部
var x=5;
function f(params) {
//虽然x的局部声明在后面,但是依旧会被识别到,相当于var x,但是值还没有赋值
console.log(x); //输出undefined
var x=10;
}
f();
相当于
var x=5;
function f(params) {
var x
console.log(x); //输出undefined
x=10;
}
f();
同理,
- 函数可以提前,只要函数定义了,不管在什么位置都可以调用
- 函数表达式不可以提前,函数表达式相当于变量,只是变量的值是一个函数,如果在定义之前调用那么就上当于上文的,仅仅定了变量,但没有定义值,会undefined
变量声明和函数声明的提升优先级
函数声明提前高于变量声明提前,虽然不大可能遇到函数名和变量名重叠的情况,但是假如重叠了,那么不管变量的定义在哪,优先级都高于函数
function double(params) {
return params*2
}
var double=2;
console.log(typeof double); //number而不是function
块级作用域
是指变量在指定块的作用域外无法被访问,它位于一对花括号中,语法和var一样,只是它使用let或const来声明
let y=1;
if(true){
var xa=2;
let y=2;
}
console.log(xa); //2
console.log(y); //1
const
使用const声明的原始类型,是常量,之后不能更改,不然汇报类型错误,使用const声明的对象类型变量,变量本身无法赋值其他类型,但他的属性可以修改
const xb=1;
xb=2; //报错
console.log(xb);
const ca={};
ca.name="123"
console.log(ca); //输出{name: "123"}
const obj=Object.freeze({});
obj.name="123"
console.log(obj); //输出:{},Object.freeze()方法定义的对象不可修改
循环语句中的函数
for(var i=0;i<3;i++){
setTimeout(()=>alert(i));
//输出3个3,并不是1,2,3因为js是单线程的,实际上等到执行setTimeout时,for循环已经结束了
//因此,实际开发中ajax等获取也会导致结果是最后一个
}
for(let i=0;i<3;i++){
setTimeout(()=>alert(i));
//输出0,1,2,let定义的会在每次迭代中重新定义一个i并对其初始化
}
在for...in中也一样
全局块级绑定
使用var声明的变量会成为全局对象的属性,意味着可能会无意覆盖已存在的全局属性,而用let和const声明的全局变量,不会添加到全局对象上
最佳实践
前提是支持es6
- 优先使用const
- 如果之后改变,则使用let
- 避免使用var
作用域链
变量的查询会现在本体内查找,如果没有,就返回上一级,直到全局作用域,假如没有找到,就会报错