let
1.ES6简介
ECMA是一个标准 JavaScript是ECMAScript的实现。
ES6是ECMAScript的一个版本,是2015年发布的,ES2015.
2.let关键字
ES6新增的关键字,用来声明变量。用法类似于var,但是所声明的变量,只在let所在的代码块有效。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function(){
console.log(i);
}
}
a[6](); //10
var定义的变量遇到for循环的大括号是不会形成作用域的。所以在上面代码调用时,调用的是一个全局的变量。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
}
}
a[6](); //6
以上代码中,i是使用let
声明的,当前的i只在本轮的循环中有效。每一个循环中的i其实都是一个新的变量。所以输出的是6.
for (var i = 0; i < 3; i++) {
var i = "abc";
console.log(i); //执行一次abc
}
// 循环一次时输出abc,i++时,i已经变成abc,无法i++,i就变成NaN,直接从循环跳出
for (let i = 0; i < 3; i++) {
let i = "abc";
console.log(i);
}
结果:输出3次abc。
原因:在代码块中的变量i与for循环中的变量i不在用一个作用域。
2.1 不存在变量提升
var 存在变量提升。即变量在声明之前使用时,值为undefined。js会将声明语句放在所有js代码之前执行。
console.log(a);
var a = 10;
//相等于
var a;
console.log(a);
a = 10;
console.log(a);
let a = 10;
结果:提示错误Cannot access 'a' before initialization
2.2 暂时性死区
var tmp = 123;
if(true){
tmp = "abc";
let tmp;
console.log(tmp);
}
结果:提示错误Cannot access 'a' before initialization
使用变量时,会先寻找同一作用域下的变量。以上代码中,tmp=abc
会优先寻找到下面的let tmp
而let tmp
不存在变量提升,所以提示错误。
总结:在代码块中,使用let
命令声明变量之前,该变量都是不可用的状态,在语法上,称为“暂时性死区”
2.3 不允许重复声明
let
不允许在相同的作用域中,重复声明同一个变量。
//Identifier 'a' has already been declared
let a = "yasuo";
let a = "jianhao"
console.log(a);
//Identifier 'a' has already been declared
function show(a){
let a = 10;
}
show(100);
以上代码中,函数的形参a
与函数内部的a
作用域相等,所以会报错。
function show(a){
{
let a = 10;
}
}
show(100);
3.块级作用域
3.1 为什么需要块级作用域
ES5 只有全局作用域和局部作用域(函数作用域),没有块级作用域。
缺点1:内部变量可能会覆盖外层的变量
var date = new Date();
function f(){
console.log(date);
if(false){
var date = "今天是个好日子"
}
}
f();//undefined
上面案例中,if
代码块外部原意为使用外层的date
,内部使用内部的date
。但是函数执行后,结果却为undefined
,原因是存在变量的提升。导致内部的date
覆盖了外部的date
变量
缺点2:for循环中的计数变量泄露为全局变量。
var hello = "hello"
for (var i = 0; i < hello.length; i++) {
console.log(hello[i]);
}
console.log(i);
以上案例中,for·
循环中的变量i
只是用来作为计数使用,但是for循环执行完成后,它没有消失,而是作为全局变量仍然存在。以后可能不会再使用,造成资源的浪费。
3.2 ES6块级作用域
function show(){
let a = "亚索";
if(true){
let a = "剑豪";
{
let a = "孤儿索"
}
console.log(a);
}
console.log(a);
}
show()
块级作用域下的变量不会对外层的变量造成影响,同时支持多层嵌套。
if(true)let a = "亚索";
console.log(a);
ES6的块级作用域必须有大括号,所以使用if判断时,不可以省略大括号。
4.案例
<ul>
<li>诺手</li>
<li>狼人</li>
<li>亚索</li>
<li>卡莎</li>
<li>猫咪</li>
</ul>
var lis = document.querySelectorAll("li");
// for (var i = 0; i < lis.length; i++) {
// lis[i].dataset.index = i;
// lis[i].onclick = function(){
// alert(this.dataset.index);
// }
// }
for (let i = 0; i < lis.length; i++) {
lis[i].onclick = function(){
alert(i);
}
}
以上案例,使用let关键字实现点击li,弹出相当元素的下标。
const
1.基本用法
const
也是用来声明变量的,不过它声明的是一个只读的常量。一旦声明,值不可修改。
const PI = 3.1415926;
// alert(PI);
PI = 3.14;
//Assignment to constant variable 为一个常量赋值了
alert(PI)
1.2 必须初始化
const PI;
PI = 3.1415926;
alert(PI);
//Missing initializer in const declaration const声明中缺少初始化式
因为const声明的变量的值不可修改,所以,const一旦声明变量,则必须初始化。
1.3 一般变量名大写
规范。
1.4 也有块级作用域
const
与``let`相同,只会在声明的所在代码块作用域内生效。
{const PI = 3.1415926;}
alert(PI);
//报错
1.5 不存在变量提升
alert(PI);
const PI = 3.1415926;
//报错
1.6 不可重复声明
var PI = "3.14";
const PI = 3.1415926;
1.7 对于数组和对象中的元素进行修改,不算对常量的改变
const
变量保存的变量指向的值的内存地址。对于简单的数据(数值,字符串,布尔值)值保存在变量指向的内存地址。
对于复合数据类型,变量指向的内存地址,只是该对象的地址,其中的内容发生变化不会使该对象的内存地址发生变化。
const obj = {
name:"亚索"
}
obj.name = "疾风剑豪";
console.log(obj.name);
//疾风剑豪
const obj = {
name: "亚索"
}
obj.name = "疾风剑豪";
obj = {
name: "孤儿索"
}
console.log(obj.name); //Assignment to constant variable.为一个常量赋值了
变量的解构赋值
1.什么是解构
ES6中,按照一定的模式,从数组或对象中提取值,对变量进行赋值。这个过程叫做解构赋值。
2.数组解构
const arr = ["诺手","蝎子","劫","EZ","女坦"];
let nuo = arr[0];
let lailai = arr[1];
let jie = arr[2];
let ez = arr[3];
let f = arr[4];
//ES6
let [nuo,lailai,jie,ez,f] = ["诺手","蝎子","劫","EZ","女坦"];
本质上,这种写法属于“模式匹配”。只要赋值号两边的模式相同,左边的变量就会被赋予对应的值。
let [a,[b,c]] = [1,[2,3]];
let [a, b] = [1, 2, 3];
console.log(a); //1
console.log(b); //2
解构失败时,变量的值为undefined。
let [a,b] = [1];
console.log(a);//1
console.log(b);//undefined
不完全解构
let [a,b] = [1,2,3];
console.log(a);//1
console.log(b);//2
赋值号两边不是完全相等的。
3.对象解构赋值
const obj = {
name:"亚索",
skill:"hasakei"
}
let {name,skill} = obj;
变量名与属性名相同,才能取到正确的值。
如果想要自定义变量名,则用:进行修改。
let {name:n,skill} = obj;
console.log(n);
方法解构
var obj = {
r:function(){
console.log("狂风绝息斩");
},
e:function(){
console.log("e去不复返");
}
}
const {r,e} = obj;
r(); //狂风绝息斩
e(); //e去不复返
const {
log
} = console;
log(log);
const obj = {
p: [
"hello", {
y: "world"
}
]
}
let {
p: [a, {
y: b
}]
} = obj;
console.log(a); //hello
console.log(b); //world
注意点
let x;
{x} = {x:1};
以上代码是错误写法。{x}
前面如果没有let const的变量声明的关键字,则js会将其视为代码块。
如果需要提前声明变量,则需要将解构语句用小括号包裹起来;。
let x;
({x} = {x:1});
console.log(x); //1
数组本质也是特殊的对象,也可以对数组进行对象属性的解构。
const arr = ["诺手","蝎子","劫","EZ","女坦"];
let {length,0:first,4:last} = arr;
console.log(first);//诺手
console.log(last);//女坦
4.字符串解构
const str = 'hello';
let [a,b,c,d,e] = 'hello';
let {length} = 'hello';//5
类数组的对象都有一个length
属性,我们可以对这个属性进行解构赋值。
5.函数参数的解构赋值
function add([x,y]){
return x+y;
}
let sum = add([1,2]);
console.log(sum);//3
以上案例,函数add的参数表面上一个数组,但是在传入参数的那一刻,数组参数会被解构成变量x和与y.
6.用途
6.1 交换变量的值
let a = 1;
let b = 2;
// let tmp = a;
// a = b;
// b = tmp;
[a,b] = [b,a];
6.2 从函数中返回多个值
function fn(){
return [1,2,3];
}
let [a,b,c] = fn();
function fn2(){
return {
name:"yasuo",
age:"25"
}
}
let {name,age} = fn2();
6.3 函数参数的定义
function fn({a,b,c}){
console.log(a);//1
console.log(b);//2
console.log(c);//3
}
fn({c:3,b:2,a:1})
函数扩展
1.函数参数的默认值
ES6之前,不能为函数的参数设置默认值,只能在代码中进行判断
function show(a,b){
if(typeof b === "undefined"){
b = 0;
}
console.log(a,b);
}
show(1);
ES6的写法
function show(a,b=0){
console.log(a,b);
}
show(1);
设置默认值的参数应该在函数的最后面,否则默认值设置是无效的。
function show(a=0,b){
console.log(a,b);
}
show(1)//1 undefined
函数的length属性为函数的参数个数。若设置了默认值,length的计数中是不计算设置了默认值的参数的。
作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。
等到初始化结束,这个作用域就会消失。
var a = 1;
function fn(a,b = a){
console.log(b);
}
fn(2)//2
let a = 1;
function fn(b = a) {
let a = 2;
// console.log(a);//2
console.log(b); //1
}
fn();
函数调用中,(b=a)是一个单独的作用域,a变量不存在,则会去外部寻找。
2.rest参数
2.1 基本用法
function add(...v){
for (var i = 0; i < v.length; i++) {
console.log(v[i])
}
}
add(2,1,3)//2 1 3
rest参数必须是最后一个正式参数。
//错误写法
function add(...v,a){
}
2.2 rest参数和arguments对象的区别
arguments对象不是一个数组,只是一个类似数组的对象。不能使用数组的方法。
rest参数是一个真正的数组,所以可以使用数组的方法
function sort(...v){
// return arguments.sort(); 报错arguments.sort is not a function
return v.sort()//ƒ sort() { [native code] }
}
console.log(sort(3,1,4,8));
3.箭头函数
3.1 定义
箭头函数是对于匿名函数表达式的一种简写方法。
3.2 语法
//普通匿名函数
var a = function(a,b){
}
//箭头函数
//()形参的位置
//=> 箭头函数的标志
//{} 函数代码块
()=>{}
var fn = function(a,b){
console.log(a+b);
console.log("我是普通匿名函数");
}
fn(10,20);
var fn2 = (a,b)=>{
console.log(a+b);
console.log("我是箭头函数");
}
fn2(10,20);
3.3 其他写法
如果箭头函数只有一个形参,则可以省略小括号。
var fn = a =>{
console.log(a);
}
如果只包含一个语句,则省略{}和return。
var fn = a => a*a
箭头函数中没有arguments对象。
var fn = a => {
console.log(arguments.length);//报错arguments is not defined
}
3.4 箭头函数中this指向
箭头函数内部的this是由上下文确定的。
它会找到自身定义的位置中的this值,作为自身的this。
let div = document.querySelector("div");
div.onclick = function(){
console.log(this);//div
}
div.onclick = ()=>{console.log(this);}//window
div.onclick = function(){
let fn = function(){
console.log(this);
}
fn();//window
let fn = ()=>{
console.log(this);
}
fn();//div
}
let obj = {
name:"亚索",
fn:function(){
console.log(this);
},
fun:()=>{
console.log(this);
}
}
obj.fn();//obj
obj.fun();//window