var
接受重复声明
var a = 1;
var a = 2;
console.log(a) // 2
变量预解析
有时候叫 变量提升,就是在声明前使用
console.log(a)//undefined
var a = 12;
function fn(){
alert(a); // undefined
var a = 5;
}
fn()
不支持块级作用
支持全局作用域、局部(函数)作用域
for(var i = 0;i < 10; i++){
//
}
// 输出10,i=10结束
console.log(i)
if(false){
var a = 10
}
console.log(a) // 10
if(true){
var a = 10
}
console.log(a) // 10
挂载window
在ES5中,全局变量直接挂载到全局对象window的属性上,
所以能在window上看到 var 和 function声明的全局变量
var a = 10;
function f(){};
console.log(window.a); // 10
console.log(window.f); // f(){}
let
不接受重复声明
let a = 1;
let a = 10; //报错
不支持变量预解析
console.log(a); //报错,没有变量预解析
let a = 1;
let a = 1;
function fn(){
console.log(a);
let a = 5;
}
fn();
var m = 10;
function fun(){
m = 20;
let m;
console.log(m);
}
fun();//报错,暂时性死区
//支持块级作用域
{
var a=1
let g='zhou'
}
console.log(a)//1
//console.log(g)//报错 ReferenceError: a is not defined.
不影响作用域链
{
let s='外层块级'
function fn(){
console.log("s");
}
fn();
}
不能挂载window
使用 let / const 声明的全局变量,会被绑定到Script对象而不是Window对象
而let、const声明的全局变量在window对象上看不到,在script中形成了一个块级作用域,
这样在全局就可以访问到
let a = 1;
const B = 2;
console.log(window.a); // undefined
console.log(window.B); // undefined
console.log(a); // 1 通过块作用域访问到的
console.log(B); // 2 通过块作用域访问到的
使用var / let / const 声明的局部变量都会被绑定到 Local (本地)对象。
注:Script对象、Window对象、Local对象三者是平行并列关系。
块级作用域
1、通过var定义的变量可以跨块作用域访问到。
{
var a = 1;
console.log(a); // 1
}
console.log(a); // 1
2、通过var定义的变量不能跨函数作用域访问到
(function A() {
var b = 2;
//console.log(b); // 2
})();
console.log(b); // 报错,
3、var定义的变量没有块级作用域
if(true) {
var c = 3;
}
console.log(c); // 3
有意思的地方
for(var i = 0; i < 4; i ++) {
var d = 5;
console.log(i)// 0 1 2 3
};
console.log(i); // 4(循环结束i已经是4,所以此处i为4)
console.log(d); // 5
if 语句和 for 语句中用 var 定义的变量可以在外面访问到,可见,if语句和for语句属于块作用域,不属于函数作用域。
关于for循环的循环变量问题
for(var i =0 ;i < 5; i++){
}
alert(i); // 5 可以获取括号里的 i
let i = 7;
for(let i =0 ;i < 5; i++){
//let i="里面的i"//这里会覆盖上面括号的i
console.log(i)//0 1 2 3 4
}
console.log(i); //7 括号()里应该也算块作用域,和{}不是同一个,下面有解释
let i = "window";
for(let i = 0; i < 10 ;i++){
let i = 5
for(let j = 0,i = 101; j < 10 ;j++){
let i = 102
console.log(i)
}
}
for(let i =0 ;i < 10; i++){
console.log(i);
}
alert(i); //window
for循环的计数器,就很合适使用let命令。
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// ReferenceError: i is not defined
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域(同一个作用域 let 不能重复声明同一个变量)
暂时性死区
使用let、const声明的变量,在声明之前使用这些变量,就会报错。
var tdz = 123;
if (true) {
tdz = 'abc'; // 报错
let tdz;
}
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(TDZ)
const
特性同let
(不接受重复声明、不支持变量预解析、块作用域、不挂载window)
const声明一个只读的常量。一旦声明,常量的值就不能改变。
一般常量使用大写(潜规则)
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
一旦声明,必须立即初始化,不能留到以后赋值
const foo;
// SyntaxError: Missing initializer in const declaration
const a;
a = 1;
console.log(a); // 报错 语法错误
//Uncaught SyntaxError: Missing initializer in const declaration
//缺少初始值在常量定义时
不接受重复声明
var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "world";
const age = 30;
const定义的对象属性是否可以改变
const person = {
name : 'zs',
sex : '男'
}
person.name = 'kk'
console.log(person.name)//test
const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; // 报错
const person = {
name : 'zs',
sex : '男'
}
person = {
name : 'test',
sex : '男'
}
//报错
声明赋值的是基本数据类型不能修改
声明赋值的是引用数据类型不可修改引用类型的指针(地址),但是具体的值是可以修改的。
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
对于基本类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。
对于引用类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
Object.freeze
如果真的想将对象冻结,应该使用 Object.freeze 方法。
const FOO = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
FOO.prop = 123;
常量FOO指向一个冻结的对象,所以添加新属性不起作用,严格模式时还会报错。
只能冻结第一层, 不能冻结子属性的值。
// 只能冻结第一层, 不能冻结子属性的值。
const OBJ = {
name: 'zs',
person: {
name: 'ls'
}
};
Object.freeze(OBJ)
OBJ.person.name = 'ww'
console.log(OBJ.person.name)//ww
除了将对象本身冻结,对象的属性也应该冻结。
下面是一个将对象彻底冻结的函数。
var constantize = (obj) => {
Object.freeze(obj);//冻结对象及对象里的非对象属性
Object.keys(obj).forEach( (key, i) => {
//冻结对象里的对象属性
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
Object.keys
以数组的形式返回对象所有属性名(key),获得由对象属性名组成的数组;
返回一个 由一个给定对象的自身可枚举属性组成的数组,数组中属性名的
排列顺序和正常循环遍历该对象时返回的顺序一致
let obj={
name:'vue',
age:5,
author:'鱿鱼西'
}
console.log(Object.keys(obj))
//["name","age","author"]
foreach
对数组的每个元素执行一次提供的函数。
主要有三个参数,分别是 数组内容(item)、数组索引(index)、整个数组(array)
let obj={
name:'vue',
age:5,
author:'鱿鱼西'
}
console.log(Object.keys(obj))
Object.keys(obj).forEach(key=>{
console.log(obj[key])
})
//vue
//5
//鱿鱼西