ES6

ES6 入门

本文参考阮一峰老师的《es6 入门》

前言

如今,距离 ES6 的正式发布时间已经过去了 3 年,随着时间的推移,各大浏览器对 ES6 的支持度已经越来越高了,超过 90%的 ES6 语法特性都实现。掌握 ES6 已经是一名前端工程师必备的能力。

ES6 let 和 const 命令

let 命令

基本用法

ES6 新增了let命令,用来声明变量,但是let所声明的变量只能在自己的代码块内有效,也就是说,原本没有块级作用域的javascript现在也有了块级作用域。

 {
  let a = 1;
 }

console.log(a); //a is not defined
复制代码

正因为产生块级作用域这个特点,let十分适用于 for 循环

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

上面代码中,变量ilet 声明的,当前的 i 只在本轮循环有效,所以每一次循环的 i 其实都是一个新的变量,所以最后输出的是 6。

如果使用var命令,最后输出的是 10

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

不存在变量提升

let 不像 var 那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。

console.log(a); // 输出 undefined
console.log(b); // 报错 ReferenceError

var a = 2;
let b = 2;
复制代码

上面代码中,变量avar 命令声明,会发生变量提升,即脚本开始运行时,变量 a 已经存在了,但是没有值,所以会输出undefined。变量 blet命令声明,不会发生变量提升。这表示在声明它之前,变量b 是不存在的,这时如果用到它,就会抛出一个错误。

暂时性死区

只要块级作用域内存在 let 命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

ES6 明确规定,如果区块中存在 letconst 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)

if (true) {
// TDZ 开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError

let tmp; // TDZ 结束
console.log(tmp); // undefined

tmp = 123;
console.log(tmp); // 123
}
复制代码

上面代码中,在 let 命令声明变量 tmp之前,都属于变量 tmp 的“死区”。

ES6 规定暂时性死区和 let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。

// 报错
function () {
  let a = 10;
  var a = 1;
}

// 报错
function () {
  let a = 10;
  let a = 1;
}
复制代码

因此,不能在函数内部重新声明参数。

do 表达式

本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。

{
  let t = f();
  t = t * t + 1;
}
复制代码

上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到 t 的值,因为块级作用域不返回值,除非t是全局变量。

现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上do,使它变为do表达式。

let x = do {
  let t = f();
  t * t + 1;
};
复制代码

上面代码中,变量x会得到整个块级作用域的返回值。

const 命令

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.
复制代码

上面代码表明改变常量的值会报错。

const命令大致特点和let相似,不再一一列举

ES6 数组的扩展

Array.from()

这个方法是 Array 构造器的静态方法

作用:将把类数组对象转成真正的数组。

格式:

Array.from(类数组对象)
Array.from(类数组对象,function(item,index){
  return;//
})
复制代码

代码如下

var lis = document.getElementsByTagName("li")
var rs  = Array.from(lis)
console.log(Array.isArry(rs))//true
复制代码

Array.of()

作用:将一组值转换为数组。与 Array.from 功能相似,理解用来创建数组。 主要目的是弥补构造器 Array()的不足

var arr1 = new Array.of(3)  //[3]
var arr2 = new Array.of("3")//['3']
复制代码

find 和 findIndex

find:用于找出第一个符合条件的数组元素。找不到则是 undefined 。注意,它是不会返回多 个,只找一个,找到了就返回。如果你要把所有的满足条件的数组元素素都找出来,你应该用filter()

findIndex:返回第一个符合条件的数组元素的索引。找不到则是-1;

格式:

arr.find(function(item,index){
  return 条件;//
})
复制代码
var arr = [
  {name:"zs1",score:90}
  {name:"zs2",score:90}
  {name:"zs3",score:90}
]

var rs = Array.find(item =>item.name=="zs1")// {name:"zs1",score:90}
复制代码

includes

作用:判断元素是否在数组中存在。返回值是 true|false

代码如下:

var myarr = [1,2,3]
myarr.includes(1)//true
myarr.includes(0)//flase
复制代码

fill

作用:给数组填充指定值。fill方法用于空数组的初始化非常方便。已有数据会被覆盖。 fill 方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置

格式:

arr.fill(值)
arr.fill(值,起点,终点) 包括起点,不包括终点
复制代码
var arr = new Array(5)
arr.fill("*") //["*","*","*","*","*"]
复制代码

数组的扩展运算符

功能:把数据结构转成数组。

代码如下:

var arr1 = [1,2,3]
var arr2 = [...arr1] //[1,2,3]
复制代码

函数的扩展

函数参数的默认值

基本用法

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
复制代码

箭头函数

ES6 允许使用“箭头”(=>)定义函数。

var f = v => v;
复制代码

上面的箭头函数等同于:

var f = function(v) {
  return v;
};
复制代码

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

var f = () => 5;
// 等同于
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};
复制代码

箭头函数使得表达更加简洁。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。

箭头函数的一个用处是简化回调函数。

// 正常函数写法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭头函数写法
[1,2,3].map(x => x * x);
复制代码

注意

(1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。

(3)不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替。

(4)不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

ES6 对象的扩展

属性的简洁表示法

ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

var foo = 'bar';
var baz = {foo};
baz // {foo: "bar"}

// 等同于
var baz = {foo: foo};
复制代码

上面代码表明,ES6允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值。下面是另一个例子。

function f(x, y) {
  return {x, y};
}

// 等同于

function f(x, y) {
  return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}
复制代码

除了属性简写,方法也可以简写。

var o = {
  method() {
    return "Hello!";
  }
};

// 等同于

var o = {
  method: function() {
    return "Hello!";
  }
};
复制代码

Object.is()

比较两个值是否相等

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
复制代码

Object.assign()

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

注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

var str  = { a: 1, b: 1 };
var str1 = { b: 2, c: 2 };
var str2 = { c: 3 };

Object.assign(str, str1, str2);
str // {a:1, b:2, c:3}
复制代码
var v1 = 'abc';
var v2 = true;
var v3 = 10;

var obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
复制代码

上面代码中,v1、v2、v3分别是字符串、布尔值和数值,结果只有字符串合入目标对象(以字符数组的形式),数值和布尔值都会被忽略。这是因为只有字符串的包装对象,会产生可枚举属性。

Object.getOwnProertyDescriptor();

获取一个对象中某个属性的详细信息。

var a = 1 
console.log(object.getOwnProertyDescriptor(window,"a")
复制代码

getOwnPropertyNames()

获取自的属性,以数组的格式返回的。

var obj = {
    name:xiaoli
    age:22
}
console.log(object.getOwnPropertyNames(obj))//["name","age"]
复制代码

Object.keys()

var obj = {
    name:xiaoli
    age:22
}
console.log(object.keys(obj))//["name","age"]
复制代码

使用Object.getOwnPropertyNames()和Object.keys()都可以得到一个对象的属性名,属性名是放在一个数组中的。

对于对象的遍历目前有三种方式:

for in
Object.keys()
Object.getOwnPropertyNames()

for in : 会输出自身以及原型链上可枚举的属性。
Object.keys() : 用来获取对象自身可枚举的属性键
Object.getOwnPropertyNames() : 用来获取对象自身的全部属性名

__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()

(1)__proto__属性

隐式原型属性,用来设计对象的隐式原型

(2)Object.setPrototypeOf()

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象。它是ES6正式推荐的设置原型对象的方法。

// 格式
Object.setPrototypeOf(object, prototype)

// 用法
var o = Object.setPrototypeOf({}, null);
复制代码
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);

proto.y = 20;
proto.z = 40;

obj.x // 10
obj.y // 20
obj.z // 40
复制代码

(3)Object.getPrototypeOf()

该方法与setPrototypeOf方法配套,用于读取一个对象的prototype对象。

Object.getPrototypeOf(obj);
复制代码

ES6 变量的解构赋值

数组的解构赋值

基本用法

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

以前,为变量赋值,只能直接指定值。

var a = 1;
var b = 2;
var c = 3;
复制代码

ES6 允许写成下面这样。

var [a, b, c] = [1, 2, 3];
复制代码

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

默认值

解构赋值允许指定默认值。

var [f = true] = [];
f // true

[x, y = 'b'] = ['a']; // x='a', y='b'
[x, y = 'b'] = ['a', undefined]; // x='a', y='b'
复制代码

注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。

对象的解构赋值

解构不仅可以用于数组,还可以用于对象。

var { f, b } = { f: "aaa", b: "bbb" };
f // "aaa"
b // "bbb"
复制代码

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

var { b, f } = { f: "aaa", b: "bbb" };
f // "aaa"
b // "bbb"

var { baz } = { f: "aaa", b: "bbb" };
baz // undefined
复制代码

字符串的解构赋值

字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
复制代码

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello';
len // 5
复制代码

函数参数的解构赋值

函数的参数也可以使用解构赋值。

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

add([1, 2]); // 3
复制代码

上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量xy。对于函数内部的代码来说,它们能感受到的参数就是xy

ES6 字符串的扩展

repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'a'.repeat(0) // ""
复制代码

参数如果是小数,会被取整。

'a'.repeat(2.9) // "aa"
复制代码

如果repeat的参数是负数或者Infinity,会报错。

'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError
复制代码

trim()

除去字符串空格的。

trim 左右空格都是去掉

trimLeft 左空格去掉

trimRight 右空格去掉

var str       = "  ab ab abc  "
str.trim      = "ab ab abc"
str.trimLeft  = "ab ab abc  "
str.trimRight = "  ab ab abc"
复制代码

includes

var str = "abc"
str.includes("a")//true
str.includes("e")//flase
复制代码

startsWith

var str = "abc def"
str.startsWith("abc")//true
str.startsWith("abcd")//false
复制代码

padStart

var str = "abc def"
str.padStart(15,"*")
//"********abc def"
复制代码

padEnd

var s = 4
s.toFixed(2) 
"4.00"
s.toFixed(2).padStart
"04.00" 
复制代码

模板字符串

传统的JavaScript语言,输出模板通常是这样写的。

$('#result').append(
  'There are <b>' + basket.count + '</b> ' +
  'items in your basket, ' +
  '<em>' + basket.onSale +
  '</em> are on sale!'
);
复制代码

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

$('#result').append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);
复制代码

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

模板字符串中嵌入变量,需要将变量名写在${}之中。

function authorize(user, action) {
  if (!user.hasPrivilege(action)) {
    throw new Error(
      // 传统写法为
      // 'User '
      // + user.name
      // + ' is not authorized to do '
      // + action
      // + '.'
      `User ${user.name} is not authorized to do ${action}.`);
  }
}
复制代码

ES6 数值的扩展

Number.parseInt(), Number.parseFloat()

ES6 将全局方法parseInt()parseFloat(),移植到Number对象上面,行为完全保持不变。

// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45

// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
复制代码

这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。

Number.isInteger()

Number.isInteger()用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以 3 和 3.0 被视为同一个值。

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false
复制代码

Number.EPSILON

ES6 在Number对象上面,新增一个极小的常量Number.EPSILON

Number.EPSILON
// 2.220446049250313e-16
Number.EPSILON.toFixed(20)
// '0.00000000000000022204'
复制代码

引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。

Math 对象的扩展

ES6 在Math对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。

Math.trunc()

Math.trunc方法用于去除一个数的小数部分,返回整数部分。

Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
复制代码

Math.cbrt()

Math.cbrt方法用于计算一个数的立方根。

Math.cbrt(-1) // -1
Math.cbrt(0)  // 0
Math.cbrt(1)  // 1
Math.cbrt(2)  // 1.2599210498948734
复制代码

Math.clz32()

JavaScript 的整数使用 32 位二进制形式表示,Math.clz32方法返回一个数的 32 位无符号整数形式有多少个前导 0。

Math.clz32(0) // 32
Math.clz32(1) // 31
Math.clz32(1000) // 22
Math.clz32(0b01000000000000000000000000000000) // 1
Math.clz32(0b00100000000000000000000000000000) // 2
复制代码

Math.imul()

Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。

Math.imul(2, 4)   // 8
Math.imul(-1, 8)  // -8
Math.imul(-2, -2) // 4
复制代码

Math.fround()

Math.fround方法返回一个数的单精度浮点数形式。

Math.fround(1.337) // 1.3370000123977661
复制代码

对于整数来说,Math.fround 方法返回结果不会有任何不同,区别主要是那些无法用 64 个二进制位精确表示的小数。这时,Math.fround 方法会返回最接近这个小数的单精度浮点数。

Math.hypot()

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

Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755
Math.hypot();            // 0
Math.hypot(NaN);         // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5');   // 7.0710678118654755
Math.hypot(-3);          // 3
复制代码

上面代码中,3 的平方加上 4 的平方,等于 5 的平方。

如果参数不是数值,Math.hypot 方法会将其转为数值。只要有一个参数无法转为数值,就会返回 NaN。

ES6 set和map数据结构

Set 基本用法 ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成Set数据结构。

var s = new Set();

[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));

for (let i of s) {
  console.log(i);
}
// 2 3 5 4
复制代码

上面代码通过add方法向Set结构加入成员,结果表明Set结构不会添加重复的值。

Set函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。

// 例一
var set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]

// 例二
var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5

// 例三
function divs () {
  return [...document.querySelectorAll('div')];
}

var set = new Set(divs());
set.size // 56

// 类似于
divs().forEach(div => set.add(div));
set.size // 56
复制代码

上面代码中,例一和例二都是Set函数接受数组作为参数,例三是接受类似数组的对象作为参数。

// 去除数组的重复成员
[...new Set(array)]
复制代码

class

从形式上,向主流的面向对象的语言靠拢。

我们之前写对象方式可以用 Class 用进行优化

前面我们都是创建构造器,然后去new构造器,构造器就相当于一个类,在ES6中,就可以使用class来创建对象了。

1,class创建对象 格式:

class 类名{
    constructor(参数){
        this.属性 = 参数;
    }
    method(){
        
    }
}
复制代码

注意:

1.class 是关键字,后面紧跟类名,类名首字母大写,采取的是大驼峰命名法则。类名之后是

2.在{}中,不能直接写语句,只能写方法,方法不需要使用关键字

3.方法和方法之间没有逗号。不是键值对

class NBAPlayer{
    constructor(name,age,height){
        this.name = name;
        this.age = age;
        this.height = height;
    }
    say(){
        console.log("`我是${this.name},我今年${this.age}岁,身高${this.height}`")
    }
    
}

var p1 = new NBAPlayer("库里","25","190")
复制代码

使用extends实现继承

格式:

class 子类 extend 父类 {
    constructor (参数){
        super(参数)
        this.属性=值
    }
}
复制代码

注意:

使用 extends 关键字来实现继承,在子类中的构造器 constructor 中,必须要显式调用父类的 super 方法,如果不调用,则 this 不可用

class NBAPlayer{
    constructor(name,age,height){
        this.name = name;
        this.age = age;
        this.height = height;
    }
    say(){
        console.log("`我是${this.name},我今年${this.age}岁,身高${this.height}`")
    }
    
}

class MVP extends NBAPlayer{
    constructor(name,age,height,year){
        super(name,age,height)
        this.year = year;
    }
     showMvp(){
        console.log("`我是${this.name},是${this.year}年的MVP`")
    }
    
}

var p1 = new MVP("库里","25","190",2016)
复制代码

类的静态方法 static

直接通过类名来访问的方法就是静态方法。如:Math.abs();这里的 abs()是静态方法。 Array.isArray();isArray()是静态方法. 在方法名前加 static 就可以了

后记

花一天的时间总结了阮一峰老师的《es6 入门》,其中还是有很多不懂的地方,还要继续学习。

转载于:https://juejin.im/post/5b6e3f5ce51d45348813cbb0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值