let和const
- let 命令 (实际上为javascript新增了块级作用域)
1、let命令用来申明变量,类似于var,let命令生命的变量只在代码块内有效。
2、并且,不存在变量的提升。
3、只要会计作用域内讯在let命令,它所声明的变量就“绑定(binding)”这个区域,不再受外部的影响。在语法上成为“暂时性死区”(TDZ),本质是:只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
!调用bar函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于”死区“。
4、不许重复声明:let不允许在相同作用域内,重复声明同一个变量。
function () { let a = 10; var a = 1;}//报错
function () { let a = 10; let a = 1;}//报错
- const 命令
const声明一个只读的常量,一旦声明,常量的值就不能改变,不能改变就意味着,const一旦声明变量就必须立即初始化,不能留到以后赋值。
const 的作用域与let相同:只在所声明的块级作用域内有效 ;声明的常量也不能提升;同样存在‘暂时性死区’,只能在声明的位置后面使用;也一样不可重复声明。
多余符合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令指示保证变量名指向的地址不变,但并不保证改地址的数据不变,所以一个对象声明为常量必须小心。
const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; //报错
常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。
块级作用域
- 为什么需要块级作用域?
1、没有跨级作用域,内层变量可能会覆盖外层变量。eg:
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = "hello world";
}
}
2、用来计数的循环变量泄露为全局变量。eg:
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
扩展运算符和rest参数
- rest参数
es6引入rest参数的形式为:“...变量名”,用户获取函数的多余参数,就可以不用使用arguments对象了。** rest参数搭配你的变量是一个数组,会将多余的变量放入数组中**。eg:
function add(...items){
let sum = 0;
for(var val of items){
sum += val;
}
return sum;
}
add(4,4,5);//输出13
实际参数4,4,5会被放入items数组中。
!rest参数之后不可以再有其他参数,也就是说rest参数只能是最后一个参数。函数的length属性(用来获取函数参数的值)的值不包括rest参数。
- 扩展运算符
扩展运算符spread是三个点(...),它就像是rest参数的逆运算,可以展开数组,将一个数组转成用逗号分隔的参数序列。eg:...[a,b,c]会被转成a,b,c。
扩展运算符的作用:
1、数组合并
var arr1 = [1,2]; var arr2 = [3,4]; var arr3 = [5,6,'b'] ;
[...arr1, ...arr2, ...arr3]//输出[1,2,3,4,5,6,'b']
2、与结构赋值结合,rest参数必须放在最后一位。
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []:
3、可以将字符串转成真正的数组。
[...'world'] //输出['w', 'o', 'r', 'l', 'd']
凡是涉及到操作32位Unicode字符的函数,最好用扩展运算符改写, JavaScript会将32位Unicode字符,识别为2个字符,采用扩展运算符就没有这个问题。
function length(str) {
return [...str].length;
}
length('x\uD83D\uDE80y') // 3
不用扩展运算符:
'x\uD83D\uDE80y'.length // 4
箭头函数
es6允许使用“箭头”(=>)定义函数。
var f = v => v;
相当于:
var f = function(v){
return v;
}
1、如果箭头函数不需要使用参数或者需要多个参数,就是用圆括号代表参数部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
2、如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return
语句返回。
var sum = (num1, num2) => { return num1 + num2; }
3、由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。
var getTempItem = id => ({ id: id, name: "Temp" });
4、
(1)函数体内的this对象就是定时所在的对象,而不是使用时所在的对象。this对象的指向是可变的,但是在箭头函数中是固定的。
//call()方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法.
function foo() {
setTimeout(() => { console.log('id:', this.id); }, 100);
}
var id = 21;foo.call({ id: 42 });
setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向
全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。
(2)不可以当做构造函数,也就是说不可以使用new命令,否则会抛出错误、
(3)不可以使用arguments对象,该对象在函数体内不存在,如果要用,可以使用rest参数替代;
(4)不可因使用yield命令,因为箭头函数不能作为Generator函数。