let和const
名字 | 特性说明 |
---|---|
var | 变量; 能重复声明; 函数级; 顶层对象的属性; 不限制修改 |
let | 变量; 不能重复声明; 块级; 不属于顶层对象的属性; 可修改 |
const | 常量; 不能重复声明; 块级; 不属于顶层对象的属性; 声明和赋值必须是同时进行; "不"可修改[ 实际上并非完全不可修改,如果引用类型时 修改里面的值和长度是可以的。 如果真的想把类型不允许修改,应该使用Object.freeze 方法] |
一、let命令
基本用法
*只在`let`命令所在的代码块内有效
{
let a = 10; var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
-------------------
for (let i = 0; i < 10; i++) {}
console.log(i); //ReferenceError: a is not defined.
for (var i = 0; i < 10; i++) {}
console.log(i); //10
-------------------
*`for循环时的let还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。`
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
输出了 3 次`abc`。这表明函数内部的变量`i`与循环变量`i`不在同一个作用域,有各自单独的作用域。
-------------------
变量提升 let在同个作用域,在执行声明之前是暂时性死区(TDZ)
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
-------------------
var tmp = 123;
if (true) {
tmp = 'abc'; // 变量提升 并开启TDZ开始 ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
*注意 在所以死区时 typeof x; // ReferenceError*
意味着`typeof`不再是一个百分之百安全的操作;
typeof x; //声明前检验 ReferenceError
let x;
typeof peo; //"undefined"无声明的
不允许重复声明
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
-------------------
*不能在函数内部重新声明参数
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
二、Es6块级作用域
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
-------------------
*ES6 允许块级作用域的任意嵌套。内层可读外层的变量
{{{{
let insane = 'Hello World';
let name = 'tom';
{
console.log(name); //'tom'
let insane = 'Hello'
}
console.log(insane); //Hello World
}}}};
-------------------
块级作用域与函数声明
环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
// 块级作用域内部的函数声明语句,建议不要使用
{
let a = 'secret';
function f() {
return a;
}
}
// 块级作用域内部,优先使用函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
注意:ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
// 第一种写法,报错
if (true) let x = 1;
// 第二种写法,不报错
if (true) {
let x = 1;
}
三、const命令
在块级作用域 和变量提升暂时性死区方面,效果和用法let保持一致。
基本用法
普通认为:const
声明一个只读的常量。一旦声明,常量的值就不能改变。
本 质 上: const
实际上保证的,并不是变量的值不得改动,而是变量指向的引用的数据不得改动,如下:
- 对于简单类型的数据(数值、字符串、布尔值)>> 等同于常量,不能改变。
-
对于复合类型的数据(主要是对象和数组)>> 变量指向的内存地址,保存的只是一个指向实际数据的指针,
const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
简单类型时:
//简单类型 修改值报错
---Es5常量 不允许修改---
Object.defineProperty(window,"my_PI",{
value:3.1415926,
writable:false
})
console.log(my_PI);//3.1415926
my_PI = 3;//能执行 但不起效
my_PI//3.1415926
---Es6常量 不允许修改---
const PI = 3.1415;
PI = 3;// TypeError: Assignment to constant variable.
*声明和赋值必须同时进行
const foo; //声明没值 报错
// SyntaxError: Missing initializer in const declaration
-------------------
复合类型时:
-------对象的例子--------
*`引用源没有变化 可以随便更改里面的值`
const foo = {};
foo.prop = 123; // 为 foo 添加一个属性,可以成功
foo.prop // 123
foo.prop = 456 // 再次修改同样成功;
delete foo.prop //删除里面的值 同样成功
*`修改到引用源都不成功 将 foo 指向另一个对象,就会报错`
foo = {}; // TypeError: "foo" is read-only
foo = {prop: 111}; // TypeError: "foo" is read-only
delete foo; //false; 修改到引用源了
-------数组的例子--------
*`引用源没有变化 可以随便更改里面的值`
const arr = [];
arr.push('Hello');//可执行 arr》["Hello"]
arr[2] = 'wellcome';//可执行 arr》["Hello",empty,"wellcome"]
arr.splice(1,1,"tom") //可执行 arr》["Hello","tom","wellcome"]
...
arr.length = 0; //可执行 arr》[]
*`修改到引用源都不成功 将 arr 指向另一个数组,就会报错`
arr = [];//TypeError
arr = ["see"];//TypeError
如果真的想将对象冻结(复杂类型不允许修改),应该使用Object.freeze
方法。~~~~
const foo = Object.freeze({});
// 常规模式时,执行但没效果;
// 严格模式时,该行会报错
foo.prop = 123;
console.log(foo);//{}
--------------------
数组同上
const arr = Object.freeze([]);
arr[0] = "hi";
console.log(arr);//[]
--------------------
*除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
四、顶层对象的属性
顶层对象,在浏览器环境指的是window
对象,在 Node 指的是global
对象。ES5 之中,顶层对象的属性与全局变量是等价的。
window.a = 1;
a // 1
a = 2;
window.a // 2
ES6 为了保持兼容性,var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
const c=12;
window.c //undefined
五、globalThis 对象
- 全局环境中,
this
会返回顶层对象。但是,Node 模块和 ES6 模块中,this
返回的是当前模块。 - 函数里面的
this
,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this
会指向顶层对象。但是,严格模式下,这时this
会返回undefined
。即指向window或undefined。 - 不管是严格模式,还是普通模式,
new Function('return this')()
,总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全策略),那么eval
、new Function
这些方法都可能无法使用。
菜鸟:https://www.runoob.com/w3cnot...
ES6基础之——new Set:https://www.cnblogs.com/ajaem...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。