前言
ES6的出现解决了一些在以往中一些看似不合理的设计以及一些技巧性的问题,例如变量可一直声明、使用判断元素出现位置索引代替判断是否存在于数组中等
一、let const
let
ES6 新增了let
命令,用来声明变量。它和var
的区别主要有几种,也是优于var
的特性
- 不存在变量提升
- 不可重复声明
- 暂时性死区
- 具有块级作用域
- 全局声明不跟window挂钩
下面通过例子来看看
不存在变量提升
console.log(age); // undefined
var age = 20;
这段代码不会报错,因为js会对变量和函数进行提升。上面的代码在对变量age
提升后,如下
var age;
console.log(age); // undefined
age = 20;
所以一个声明但并未赋值的变量为undefined
,在来看看使用let
声明的变量
console.log(age); // Uncaught ReferenceError: Cannot access 'age' before initialization
let age = 20;
let
不会存在变量提升,只能在声明位置下面代码可用。若只声明未赋值也为undefined
不可重复声明
var
var age = 20;
var age = 21;
console.log(age); // 21
let
let age = 20;
let age = 21;
console.log(age); // Uncaught SyntaxError: Identifier 'age' has already been declared
重复声明用var
的也不行
var age = 20;
let age = 21;
console.log(age); // Uncaught SyntaxError: Identifier 'age' has already been declared
暂时性死区
暂时性死区就是在一个块级作用域里面如果用let
声明了变量。那么在这个块级作用域里let
声明的位置上面不可使用这个变量
{
age = 'abc'; // Uncaught ReferenceError: Cannot access 'tmp' before initialization
let age;
}
具有块级作用域
{
var age = 'abc';
}
console.log(age); // abc
{
let age = 'abc';
}
console.log(age); // Uncaught ReferenceError: age is not defined
可以看到使用let
在块级里面的变量外面是无法访问的,let
解决了最大问题是在for
循环中的问题
再看以下代码
for (var i = 0; i < 10; i++) {
}
console.log(i); // 10
在这里var
已经暴露成全局变量了,如果替换成let
则不会
for (let i = 0; i < 10; i++) {
}
console.log(i); // Uncaught ReferenceError: i is not defined
全局声明不跟window挂钩
var age = 20;
console.log(window.age); // 20
let age = 20;
console.log(window.age); // undefined
const
const
用于声明常量,常量一般指在程序的运行中不可变的量。const
拥有let
的所有特性,并且额外多两个特性:
- 声明必须赋值
- 不可重新赋值
声明必须赋值
const name; // Uncaught SyntaxError: Missing initializer in const declaration
不可重新赋值
const name = 'my name';
name = 'change name'; // VM513:2 Uncaught TypeError: Assignment to constant variable.
由于js引用类型的特殊类型、当该常量的值为引用类型时,指向的是该值得引用地址。更改其属性并不能算是重新赋值
const arr = [];
arr.push(1);
console.log(arr); // [1]
二、String.prototype.includes
在ES5中判断指定字符串是否在另一个字符串中包含用indexOf
方法,返回指定字符串在字符串的首次出现位置,ES6则在String原型上提供了一个includes
方法。直接返回true
或者false
来判断是否包含
let str = 'my string';
console.log(str.includes('str')); // true
三、Array.prototype.includes
和字符串类似,在ES5中判断一个值是否在一个数组中包含用indexOf
方法,返回指定字符串在数组的首次出现位置,ES6则在Array原型上提供了一个includes
方法。直接返回true
或者false
来判断是否包含
let arr = [1,2,3];
console.log(arr.includes(2)); // true
四、Array.of
Array.of
方法和new Array
差不多,都是将一组值转换成数组,但是主要是弥补Array
构造函数的“坑”。因为new Array
如果只有一个参数且是数值,实际指的是即将要实例数组的长度,如果是小数
则会报错
let arr = new Array(1);
console.log(arr); // [empty]
let arr2 = new Array(1.1);
console.log(arr2); // Uncaught RangeError: Invalid array length
如果我的预期new Array(1)
是[1]
、new Array(1.1)
是[1.1]
则可以使用Array.of
方法
let arr = Array.of(1);
console.log(arr); // [1]
let arr2 = Array.of(1.1);
console.log(arr2); // [1.1]
思考:
let arr = [];
arr.length = 1.1; // Uncaught RangeError: Invalid array length
let arr2 = new Array(1.1); // Uncaught RangeError: Invalid array length
// 所以 Array 可能是这么写的
function MyArray() {
var arr = [];
arr.constructor = MyArray;
if (arguments.length === 0) { // 空参数什么都不做
} else if (arguments.length === 1 && arguments[0].constructor === Number) {
arr.length = arguments[0]; // 判断到是一个 number
} else {
arr = arr.concat([].slice.call(arguments));
}
return arr;
}
五、Array.from
在开发中需要将NodeList
、arguments
需要转成数组的话经常使用ES5数组原型上的一些方法进行强制转换,显得略些别扭。而ES6则出了替代的方法,就是Array.from
function f() {
// let args = Array.prototype.slice.call(arguments);
let args = [].slice.call(arguments);
console.log(args); // ["a", "b"]
}
f('a', 'b');
如果使用Array.from
function f() {
let args = Array.from(arguments);
console.log(args); // ["a", "b"]
}
f('a', 'b');
六、Object.is
在js中判断两个值是否相等,一般使用==
或者===
去判断。===
可以准确的判断左右两边的值是否相等,但是也是有两个问题
- NaN
- -0 +0
console.log(NaN === NaN); // false
console.log(-0 === +0); // true
NaN
应该是要等于NaN
的,但是===
无法判断、-0
和+0
不应该相等的。所以ES6在Object
上新增了一个静态方法is用来弥补
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(-0, +0)); // false
七、Object.keys
获取一个对象的所有属性用for in
遍历,但是for in
会遍历该对象原型上的属性。但其实在日常开发中大部分需求都不是需要原型上属性的,所以for in
一般配合hasOwnProperty
使用
Object.prototype.age = 20;
Object.prototype.sayName = function() {
};
let obj = {name: 'my name'};
for (let key in obj) {
console.log(key); // name age sayName
}
上面代码中age
属性、sayName
方法都会被遍历出来,其实大部分情况都是不需要的。那么则可以使用hasOwnProperty
,它是判断指定属性是否是该对象上自身的属性
用法
obj.hasOwnProperty(prop: String)
Object.prototype.age = 20;
Object.prototype.sayName = function () {
};
let obj = {name: 'my name'};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key); // name
}
}
还是略显麻烦,ES6直接在Object
下提供了一个keys
方法直接获取对象所有自身的属性
Object.keys(obj: Object)
Object.prototype.age = 20;
Object.prototype.sayName = function () {
};
let obj = {name: 'my name'};
console.log(Object.keys(obj)); // ["name"]
注意:
for in 和 Object.keys都是只能遍历可枚举
属性