前言

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

在开发中需要将NodeListarguments需要转成数组的话经常使用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都是只能遍历可枚举属性