乐趣区

关于es6:ES6之常用开发知识点入门一

ES6 介绍

ES6,全称 ECMAScript 6.0,2015.06 发版。

let 和 const 命令

let 命令

let 命令,用来申明变量。它的用法相似于 var,区别在于 var 申明的变量全局无效,let 申明的变量只在它所在的代码块内无效。

应用 var 申明:

var a = [];
for (var i = 0; i < 10; i++) {a[i] = function () {console.log(i);
  };
}
a[6](); // 10

应用闭包解决:

var a = [];
for (var i = 0; i < 10; i++) {(function(i){a[i] = function () {console.log(i);
        };
    })(i); 
}
a[6](); // 6

应用 let:

var a = [];
for (let i = 0; i < 10; i++) {a[i] = function () {console.log(i);
  };
}
a[6](); // 6
  • let 不存在变量晋升,必须先申明后应用,否则报错;var 存在变量晋升,未声明前应用输入 undefined。
  • let 存在暂时性死区,在代码块内,应用 let 命令申明变量之前,该变量都是不可用的。
  • let 不容许反复申明。

const 命令

const 申明一个只读的常量。一旦申明,常量的值就不能扭转。不能只申明不赋值。

const a = 10;
a = 20; // 报错

const b; // 报错

const 的作用域与 let 雷同。

if(true) {const num = 5;}
console.log(num); // 报错

const 申明对象,常量对象内存地址,因而对象自身可改,然而给常量从新赋值就会报错。

const obj = {};
obj.a = 'a';

obj = {}; // 报错

块级作用域和函数作用域

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

第一种场景,内层变量可能会笼罩外层变量。

var tmp = new Date();

function f() {console.log(tmp);
  if (false) {var tmp = 'hello world';}
}

f(); // undefined

第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {console.log(s[i]);
}

console.log(i); // 5

ES6 的块级作用域

let 实际上为 JavaScript 新增了块级作用域。

function f1() {
  let n = 5;
  if (true) {let n = 10;}
  console.log(n); // 5
}

块级作用域的呈现,实际上使得取得广泛应用的匿名立刻执行函数表达式(匿名 IIFE)不再必要了。

// IIFE 写法
(function () {
  var tmp = ...;
  ...
}());

// 块级作用域写法
{
  let tmp = ...;
  ...
}

ES5 规定,函数只能在顶层作用域和函数作用域之中申明,不能在块级作用域申明。

// 状况一
if (true) {function f() {}}

// 状况二
try {function f() {}} catch(e) {// ...}

下面两种函数申明,依据 ES5 的规定都是非法的。

ES6 引入了块级作用域,明确容许在块级作用域之中申明函数。ES6 规定,块级作用域之中,函数申明语句的行为相似于 let,在块级作用域之外不可援用。

function f() { console.log('I am outside!'); }

(function () {if (false) {
    // 反复申明一次函数 f
    function f() { console.log('I am inside!'); }
  }

  f();}());

下面代码在 ES5 中运行,会失去“I am inside!”,因为在 if 内申明的函数 f 会被晋升到函数头部,理论运行的代码如下。

// ES5 环境
function f() { console.log('I am outside!'); }

(function () {function f() {console.log('I am inside!'); }
  if (false) { }
  f();}());

ES6 就齐全不一样了,实践上会失去“I am outside!”。因为块级作用域内申明的函数相似于 let,对作用域之外没有影响。然而,如果你真的在 ES6 浏览器中运行一下下面的代码,是会报错的,这是为什么呢?

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }

(function () {if (false) {
    // 反复申明一次函数 f
    function f() { console.log('I am inside!'); }
  }

  f();}());
// Uncaught TypeError: f is not a function

下面的代码在 ES6 浏览器中,都会报错。

原来,如果扭转了块级作用域内申明的函数的解决规定,显然会对老代码产生很大影响。为了加重因而产生的不兼容问题,浏览器的实现能够不恪守下面的规定,有本人的行为形式。

  • 容许在块级作用域内申明函数。
  • 函数申明相似于 var,即会晋升到全局作用域或函数作用域的头部。
  • 同时,函数申明还会晋升到所在的块级作用域的头部。

留神,下面三条规定只对 ES6 的浏览器实现无效,其余环境的实现不必恪守,还是将块级作用域的函数申明当作 let 解决。

依据这三条规定,浏览器的 ES6 环境中,块级作用域内申明的函数,行为相似于 var 申明的变量。下面的例子理论运行的代码如下。

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
  var f = undefined;
  if (false) {function f() {console.log('I am inside!'); }
  }

  f();}());
// Uncaught TypeError: f is not a function

思考到环境导致的行为差别太大,应该防止在块级作用域内申明函数。如果的确须要,也应该写成函数表达式,而不是函数申明语句。

另外,还有一个须要留神的中央。ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

// 第一种写法,报错
if (true) let x = 1;

// 第二种写法,不报错
if (true) {let x = 1;}

下面代码中,第一种写法没有大括号,所以不存在块级作用域,而 let 只能呈现在以后作用域的顶层,所以报错。第二种写法有大括号,所以块级作用域成立。

函数申明也是如此,严格模式下,函数只能申明在以后作用域的顶层。

// 不报错
if (true) {function f() {}}

// 报错
'use strict';
if (true)
  function f() {}

变量的解构赋值

ES6 容许依照肯定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

数组的解构赋值

模式匹配赋值,如果解构不胜利,变量的值就等于 undefined。

let [a, [[b], c]] = [1, [[2], 3]];
console.log(a,b,c); // 1, 2, 3

let [x, , y, z] = [1, 2, 3];
console.log(x); // 1
console.log(y); // 3
console.log(z); // undefined

不齐全解构赋值,等号右边的模式,只匹配一部分的等号左边的数组。

let [x, [y], z] = [1, [2, 3], 4];
console.log(x); // 1
console.log(y); // 2
console.log(z); // 4

数组构造赋值左边必须是数组,模式不匹配则报错。

let [a] = {}; // 报错

解构赋值能够增加默认值,并且能够援用解构赋值的其余变量。

let [a = 1, b = 2] = [, 3];
console.log(a); // 1
console.log(b); // 3

let [x = 1, y = x] = [];  // x = 1; y = 1
let [x = 1, y = x] = [2]; // x = 2; y = 2

数组解构赋值可用于替换变量的值。

let [a, b] = [1, 2];
console.log(a, b); // 1, 2
[b, a] = [a, b];
console.log(a, b); // 2, 1

对象的解构赋值

变量必须与属性同名

let {a, b, c} = {a: 'aaa', b: 'bbb'};
console.log(a); // 'aaa'
console.log(b); // 'bbb'
console.log(c); // undefined

变量名与属性名不统一

let {a: x, b: y} = {a: 'aaa', b: 'bbb'};
console.log(x); // 'aaa'
console.log(y); // 'bbb'

嵌套赋值,如果子对象所在的父属性不存在,会报错,慎用。

let {a, a: {x}, b: y } = {a: {x: 'xxx',y: 'yyy'}, b: "bbb" };
console.log(a); // {x: 'xxx', y: 'yyy'}
console.log(x); // 'xxx'

let {c: {d: {e}}} = {c: 'ccc'}; // 报错
console.log(e)

字符串解构赋值

字符串解构赋值,将字符串转化成数组对象

const [a,b,c] = '123456789';
const {length} = '123456789';
console.log(a, b, c, length); // 1, 2, 3, 9

相似数组的对象都有一个 length 属性,因而还能够对这个属性解构赋值。

let {length : len} = 'hello';
len // 5

函数参数解构赋值

function add([x, y]){return x + y;}

add([1, 2]); // 3

上面是另一个例子:

const arr = [[1, 2], [3, 4]].map(([a, b]) => a + b);
console.log(arr); // [3, 7]

函数参数的解构也能够应用默认值。

function move({x = 0, y = 0} = {}) {return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

下面代码中,函数 move 的参数是一个对象,通过对这个对象进行解构,失去变量 x 和 y 的值。如果解构失败,x 和 y 等于默认值。

留神,上面的写法会失去不一样的后果。

function move({x, y} = {x: 0, y: 0}) {return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

下面代码是为函数 move 的参数指定默认值,而不是为变量 x 和 y 指定默认值,所以会失去与前一种写法不同的后果。

字符串扩大

for…of 遍历字符串

for(let codePoint of 'string'){console.log(codePoint)
}
// 's'
// 't'
// 'r'
// 'i'
// 'n'
// 'g'

includes(),startsWith(),endsWith()

let s = 'Hello world!';

const [a, b, c] = [s.startsWith('Hello', 2),
    s.endsWith('!'),
    s.includes('o w')
];

console.log(a, b, c); // false true true

repeat()

repeat 办法返回一个新字符串,示意将原字符串反复 n 次。

  • 参数为 [-Infinity,-1] 或者 Infinity,会报错;
  • 参数为 (-1,1) 时,相当于参数为 0;
  • 参数为小数时向下取整;
  • 参数 NaN 等同于 0;
  • 参数是字符串,则会先转换成数字。
'str'.repeat('3') // 'strstrstr'

padStart(), padEnd()

padStart(),padEnd()有两个参数,第一个参数为字符串补全失效的最大长度,第二个参数为补全的字符串。

第二个参数默认为空格,省略第二个参数时默认用空格补全。

第一个参数小于字符串原长度时,返回原字符串。

如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。

常见用处:补全指定位数,提醒字符串格局。

'123456'.padStart(10, '0') // "0000123456"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

模版字符串(“)

const str = 'world';
const template = `Hello ${str}`;
console.log(template); // Hello world

数值扩大

二进制、八进制表示法

应用二进制表示法,前缀为 0b,应用八进制表示法,前缀为 0o,ES6 不反对应用 00 前缀示意八进制。

进制转换应用 toString 办法,应用 Number 办法间接转十进制。

0b1100100 === 100; // true
0o144 === 100; // true

(0b1100100).toString(8); // 144
(0b1100100).toString(10); // 100
Number('0b1100100'); // 100

Number.isFinite(),Number.isNaN()

Number.isFinite()用来查看一个数值是否为无限的(finite),即不是 Infinity。参数类型不是数值,Number.isFinite 一律返回 false。

Number.isNaN()用来查看一个值是否为 NaN。参数类型不是 NaN,Number.isNaN 一律返回 false。

Number.isFinite(15); // true
Number.isFinite(-Infinity); // false

Number.isNaN(15) // false
Number.isNaN(9/0) // true

Number.parseInt(), Number.parseFloat()

ES6 将全局办法 parseInt()和 parseFloat(),移植到 Number 对象下面,行为齐全放弃不变。

Number.isInteger()

Number.isInteger()用来判断一个数值是否为整数。

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false

ES6 新增 Number 常量

  • Number.EPSILON 极小常量,浮点数误差小于这个值能够认为不存在误差;
  • Number.MAX_SAFE_INTEGER 平安整数的最大范畴;
  • Number.MIN_SAFE_INTEGER 平安整数的最小范畴;
  • Number.isSafeInteger() 用来判断一个整数是否落在平安整数范畴之内。
Number.isSafeInteger(9007199254740993) // false
Number.isSafeInteger(990) // true
Number.isSafeInteger(9007199254740993 - 990) // true

Math 对象的扩大

Math.trunc() 除去一个数的小数局部,返回整数局部。参数不是数值,外部会先调用 Number()专为数值,对于空值和无奈截取整数的值,返回 NaN。(Math 对象的扩大的办法对于非数值的解决办法都一样)

Math.trunc(5.9) // 5
Math.trunc(-4.9) // -4
Math.trunc(null) // 0
Math.trunc('foo'); // NaN

Math.sign() 判断一个数是负数、正数、还是零。

Math.sign(-5) // -1 正数
Math.sign(5) // +1 负数
Math.sign(0) // +0 零
Math.sign(-0) // -0 零
Math.sign(NaN) // NaN

Math.cbrt() 计算一个数的立方根。

Math.cbrt(2)  // 1.2599210498948734

// Math.sqrt(x) 计算平方根
Math.sqrt(2) // 1.4142135623730951

// 幂运算 Math.pow(x,y)
Math.pow(2, 3)

Math.hypot() 返回所有参数的平方和的平方根。

Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755

函数扩大

rest 参数

ES6 引入 rest 参数(模式为 … 变量名),用于获取函数的多余参数,rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。只能是最初一个参数,函数的 length 属性,不包含 rest 参数。

function sum1(x, y, ...args) {
    let sum = 0;

    for (let arg of args) {sum += arg;}

    return sum;
}

console.log(sum1(1, 2, 3, 4)) // 7

function sum2(...args) {return args.reduce((prev, curr) => {return prev + curr}, 0)
}

console.log(sum2(1, 2, 3)); // 6

name 属性

函数的 name 属性,返回该函数的函数名。对于匿名函数,ES5 返回 ’ ‘,ES6 返回变量名;
Function 构造函数返回的函数实例,name 属性的值为 anonymous;bind 返回的函数,name 属性值会加上 bound 前缀。

function fn() {}
fn.name // 'fn'

function foo() {};
foo.bind({}).name // 'bound foo'

(new Function).name // "anonymous"

(function(){}).bind({}).name // 'bound'

箭头函数

const fn = v => v;

// 等同于
const fn = function (v) {return v;};

留神要点

  • 函数体内的 this 对象,就是定义时所在的对象,而不是应用时所在的对象;
  • 不能够当作构造函数,即不能够应用 new 命令,否则会抛出一个谬误;
  • 不能够应用 arguments 对象,该对象在函数体内不存在。如果要用,能够用 rest 参数代替;
  • 不能够应用 yield 命令,因而箭头函数不能用作 Generator 函数。

数组扩大

扩大运算符

扩大运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

const arr = [1, 2, 3];
arr.push(...[4, 5, 6]);

扩大运算符的利用:

  • 数组开展
const arr = [1, 2, 3];
...arr // 1, 2, 3
  • 复制数组
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;

// 相当于
const a1 = [1, 2];
const a2 = a1.concat();
  • 解构赋值,字符串转数组
const list = [1, 2, 3];
[a, ...b] = list;
console.log(a) // 1
console.log(b) // [2, 3]

[...'hello'] // ['h', 'e', 'l', 'l', 'o']

Array.from()

Array.from 办法用于将两类对象转为真正的数组:相似数组的对象(array-like object)和可遍历(iterable)的对象(包含 ES6 新增的数据结构 Set 和 Map)。

常见的相似数组的对象有 DOM 操作返回的 NodeList 汇合,以及函数外部的 arguments 对象。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5 的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6 的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

Array.from('hello');
// ['h', 'e', 'l', 'l', 'o']

let namesSet = new Set(['a', 'b']);
Array.from(namesSet); // ['a', 'b']

Array.from 还能够承受第二个参数,作用相似于数组的 map 办法,用来对每个元素进行解决,将解决后的值放入返回的数组。

let arrayLike = {
    '0': 1,
    '1': 2,
    '2': 3,
    length: 3
};
Array.from(arrayLike, x => x * x); // [1, 4, 9]

Array.of()

Array.of 办法用于将一组值,转换为数组。这个办法的次要目标,是补救数组构造函数 Array()的有余。因为参数个数的不同,会导致 Array()的行为有差别。

Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

copyWithin()

参数:

  • target(必须):必须。复制到指定指标索引地位。
  • start(可选):可选。元素复制的起始地位。
  • end(可选):可选。进行复制的索引地位(默认为 array.length)。如果为负值,示意倒数。

这三个参数都应该是数值,如果不是,会主动转为数值。

var result = [1, 2, 3, 4, 5].copyWithin(0, 3) 
console.log(result)//[4,5,3,4,5]

find() 和 findIndex()

数组实例的 find 办法,用于找出第一个符合条件的数组成员,如果没有符合条件的成员,则返回 undefined。

findIndex 办法返回第一个符合条件的数组成员的地位,如果所有成员都不符合条件,则返回 -1。

[1, 4, -5, 10].find(n => n < 0); // -5
[1, 4, -5, 10].findIndex(n => n < 0); // 2

两个办法都能够承受第二个参数,用来绑定回调函数的 this 对象。

function f(v){return v > this.age;}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);  // 26

这两个办法都能够发现 NaN,补救了数组的 indexOf 办法的有余。

fill() 填充数组

fill 办法应用给定值,填充一个数组。fill 办法能够承受第二个和第三个参数,用于指定填充的起始地位和完结地位。如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象,扭转数组中的一项,则所有项都扭转。

let arr = Array.of(1, 2, 3).fill({num: 20});

console.log(arr); // [{ num: 20}, {num: 20}, {num: 20} ]

arr[0].num = 10;
console.log(arr); // [{ num: 10}, {num: 10}, {num: 10} ]

entries(),keys() 和 values() 遍历数组

for (let index of ['a', 'b'].keys()) {console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {console.log(index, elem);
}
// 0 "a"
// 1 "b"

includes()

includes 办法返回一个布尔值,示意某个数组是否蕴含给定的值,与字符串的 includes 办法相似。该办法的第二个参数示意搜寻的起始地位,第二参数是正数,取它的倒数,第二参数大于数组长度,取 0。

[1, 2, 3].includes(3, -1); // true

flat(),flatMap()

flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,能够将 flat()办法的参数写成一个整数,示意想要拉平的层数,默认为 1。

flat()的参数为 2,示意要“拉平”两层的嵌套数组。如果不论有多少层嵌套,都要转成一维数组,能够用 Infinity 关键字作为参数。

[1, [2, [3]]].flat(Infinity);
// [1, 2, 3]

flatMap()先遍历数组,再“拉平”一层,也只能拉平一层。参数与 map()办法相似。

[2, 3, 4].flatMap(x => [x, x * 2]); // [2, 4, 3, 6, 4, 8]

// 相当于
[2, 3, 4].map(x => [x, x * 2]).flat(); // [2, 4, 3, 6, 4, 8]

对象扩大

属性简洁表示法

const a = 1;
const b = 2;

const c = {a, b};
// 等同于
const c = {a: a, b: b};

const o = {method() {return "Hello!";}
};
// 等同于
const o = {method: function() {return "Hello!";}
};

function f(x, y) {return {x, y};
}
// 等同于
function f(x, y) {return {x: x, y: y};
}

对象的扩大运算符

对象扩大符相似数组扩大符,次要用于解构赋值。

let {x, y, ...z} = {x: 1, y: 2, a: 3, b: 4};
x // 1
y // 2
z // {a: 3, b: 4}

let ab = {...a, ...b};
// 等同于
let ab = Object.assign({}, a, b);

Object.is()

它用来比拟两个值是否严格相等,与严格比拟运算符(===)的行为基本一致。

Object.is('str', 'str'); // true
Object.is({}, {}); // false

不同之处只有两个:一是 + 0 不等于 -0,二是 NaN 等于本身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

Object.assign()

Object.assign 办法用于对象的合并,将源对象(source)的所有可枚举属性,复制到指标对象(target)。

Object.assign 办法的第一个参数是指标对象,前面的参数都是源对象。如果指标对象与源对象有同名属性,或多个源对象有同名属性,则前面的属性会笼罩后面的属性。

因为 undefined 和 null 无奈转成对象,所以如果它们作为首参数,就会报错。

const target = {a: 1, b: 1};

const source1 = {b: 2, c: 2};
const source2 = {c: 3};

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

常见用处:

  • 为对象增加属性和办法
  • 克隆或合并对象
  • 给属性指定默认值

Object.keys(),Object.values(),Object.entries()

ES5 引入了 Object.keys 办法,返回一个数组,成员是参数对象本身的(不含继承的)所有可遍历(enumerable)属性的键名。

var obj = {foo: 'bar', baz: 42};
Object.keys(obj)
// ["foo", "baz"]

Object.values 办法返回一个数组,成员是参数对象本身的(不含继承的)所有可遍历(enumerable)属性的键值。

const obj = {foo: 'bar', baz: 42};
Object.values(obj)
// ["bar", 42]
const obj = {100: 'a', 2: 'b', 7: 'c'};
Object.values(obj)
// ["b", "c", "a"]

下面代码中,属性名为数值的属性,是依照数值大小,从小到大遍历的,因而返回的程序是 b、c、a。

Object.values 只返回对象本身的可遍历属性。

const obj = Object.create({}, {p: {value: 42}});
Object.values(obj) // []

下面代码中,Object.create 办法的第二个参数增加的对象属性(属性 p),如果不显式申明,默认是不可遍历的,因为 p 的属性形容对象的 enumerable 默认是 false,Object.values 不会返回这个属性。只有把 enumerable 改成 true,Object.values 就会返回属性 p 的值。

const obj = Object.create({}, {p:
  {
    value: 42,
    enumerable: true
  }
});
Object.values(obj) // [42]

Object.entries()办法返回一个数组,成员是参数对象本身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

const obj = {foo: 'bar', baz: 42};
Object.entries(obj)
// [["foo", "bar"], ["baz", 42] ]

除了返回值不一样,该办法的行为与 Object.values 基本一致。

Object.fromEntries()

Object.fromEntries()办法是 Object.entries()的逆操作,用于将一个键值对数组转为对象。

Object.fromEntries([['foo', 'bar'],
  ['baz', 42]
])
// {foo: "bar", baz: 42}
退出移动版