文章目录
一.let const
(一)let
1.声明的变量只能在当前作用域的内部有效(作用域其实就是{})
var 全局范围内有效(因为var声明的变量在Windows的整个对象上)
例子:
因为let出了当前作用域会无效,for循环每走一次,就相当于一个代码作用域。下一次循环就又是另一个代码作用域了。
变量 i 是用 var 声明的,在全局范围内有效,所以全局中只有一个变量 i, 每次循环时,setTimeout 定时器里面的 i 指的是全局变量 i ,而循环里的十个 setTimeout 是在循环结束后才执行,所以此时的 i 都是 10。
变量 j 是用 let声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 12345。
(若每次循环的变量 j 都是重新声明的,如何知道前一个循环的值?这是因为 JavaScript引擎内部会记住前一个循环的值)。
2.不能重复声明,只能声明一次
3.不存在变量提升 否则报错
(二)const
(1)声明的都是常量(只能读,不能设置的只读变量)
const 声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。
const 如何做到变量在声明初始化之后不允许改变的?
其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。此时,你可能已经想到,简单类型和复合类型保存值的方式是不同的。是的,对于简单类型(数值 number、字符串 string 、布尔值 boolean),值就保存在变量指向的那个内存地址,因此 const 声明的简单类型变量等同于常量。而复杂类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。
* 如果用const定义一个对象,可以修改对象的属性吗?
可以!
(2)暂时性死区
即代码块开始到变量声明语句完成之间的区域
如果代码区块中存在 let 和 const 命令声明的变量,这个区块对这些变量从一开始就形成了封闭作用域。直到声明语句完成,这些变量才能被访问(获取或设置),否则会报错
二、解构赋值
即相同的结构对应赋值
可详细了解:ES6——变量解构赋值
1.数组解构赋值
-
扩展运算符…
[把一个数组拆成一堆值]
(只用于部署了Iterator接口的类似数组对象)
把数组取为单个值:
let arr=[1,2,3,4,5];
console.log(...arr); //1 2 3 4 5
注意:在解构赋值中使用扩展运算符,只能放在最后面
let [a,b,...c]=[1,2,3,4,5,6];
console.log(a, b, c); //1 2 (4) [3, 4, 5, 6]
- 默认解构值
2.对象解构赋值
(1)对象解构赋值的内部机制,实际上是先找到同名属性,然后再赋值给同名的变量,真正被赋值的是后者,而不是前者。
var {foo:baz} = {bar:"mouse",foo:"lion"};
console.log(baz); //lion
console.log(foo); //error:foo is not defined
(2)es6中 当key和value一致时,可简写
var {a,b} = {b:"cat",a:"dog"};
console.log(a); //dog
console.log(b); //cat
就是下面的简写:
var {a:a,b:b} = {a:"dog",b:"cat"};
console.log(a); //dog
console.log(b); //cat
(3)对象结构也能使用扩展运算符
let {a,b,...c}={a:1,b:2,c:3,d:4};
console.log(a, b, c); //1 2 {c: 3, d: 4}
3.函数解构赋值
(1)传递数组参数
let method=function(a,b,c){
console.log(a, b, c);
}
let arr=[1,2,3];
//方法一
method(...arr); //1 2 3
//方法二
method.apply(null,arr); //1 2 3
(2)多层数组
let arr=[[1,2],[3,4],[5,6]];
arr.map(function ([a,b]) {
console.log(a, b);
})
4.变量声明不能使用()
即解构赋值声明变量时, 等号左边不能使用圆括号
三、新增数据类型 Symbol
声明的变量时独一无二的,主要用来定义对象的唯一属性名
它的出现我认为主要是为了解决可能出现的全局变量冲突的问题
1.独一无二
let sym=Symbol("abc");
let sym1=Symbol("abc");
console.log(sym,sym1); //Symbol(abc) Symbol(abc)
console.log(sym == sym1); //false
2.Symbol在对象中的使用
(1)方法一 正常操作
let sys=Symbol('name');
let obj={};
obj[sys]="唯一值";
console.log(obj); //{Symbol(name): '唯一值'}
//不能直接 对象.属性 获取值
console.log(obj.sys); //undefined
console.log(obj[sys]); //唯一值
(2)方法二 字面量操作
let syObject = {};
syObject[sy] = "kk";
syObject[sy]; // "kk"
syObject.sy; // undefined
Symbol 作为对象属性名时不能用.运算符,要用方括号。
因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
/*let sys=Symbol("list");
let obj={
sys:'abc' //这样写不解析
}
console.log(obj); //{sys: 'abc'}*/
//对象属性字面量(对象的属性是变量)
let sys=Symbol("list");
let obj={
[sys]:'abc' //对象属性是变量,解析直接[]
}
console.log(obj); //{Symbol(list): 'abc'}
(3)方法三 原生js操作
Object.defineProperty() 方法
会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
Object.defineProperty(obj, prop, descriptor)
参数:(1)obj:要定义属性的对象。
(2)prop:要定义或修改的属性的名称或 Symbol 。
(3)descriptor:要定义或修改的属性描述符。
返回值:被传递给函数的对象
在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty 是定义key为Symbol的属性的方法之一
//通过原生js操作对象属性赋值
let sys=Symbol('list');
let obj={};
Object.defineProperty(obj,sys,{
value:"唯一值"
})
console.log(obj); //{Symbol(list): '唯一值'}
3.Symbol.for()
Symbol.for() 类似单例模式【只返回一次】
首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol值, 如果有即返回该 Symbol 值;
若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境*搜索
//创建值时去全局环境找,当前值是否创建过
// 如果创建过,直接取值返回
// 如果全局环境没有,创建返回
let sys=Symbol('abc');
console.log(sys); //Symbol(abc)
let sys1=Symbol.for('abc');
console.log(sys1); //Symbol(abc)
console.log(sys === sys1); //false
let sys2=Symbol.for('abc');
console.log(sys1 === sys2); //true
4.keyFor
//keyFor 监测symbol值是否之前登记过
//登记过就返回值 没有登记过直接返回undefined
let sys=Symbol.for("abc")
console.log(Symbol.keyFor(sys)); //abc
console.log(Symbol.keyFor(Symbol('ABC'))); //undefined
5.description 直接通过属性获取描述
let sys=Symbol('abc');
console.log(sys.toString()); //Symbol(abc)
console.log(String(sys)); //Symbol(abc)
//es9 直接通过属性可以获取描述
console.log(sys.description); //abc