(JS six)ES6新特性以及运用

常用新特性:
变量定义:let Const
解构赋值
扩展运算符
Symbol数据类型
Set 和 Map 数据结构
Promise
Class
箭头函数
Proxy拦截
imoprt模块导入
for of循环
新增了一些数组的方法
新增了一些对象的方法

1.变量定义:let Const
块级作用域
ES5 中作用域有:全局作用域、函数作用域(function scope)。没有块作用域(block scope)的概念。
ES6 中新增了块级作用域。块作用域由 { } 包括,if语句和 for语句里面的{ }也属于块作用域。
变量声明
变量声明的三种方式:var、let、const
var声明的变量属于函数作用域(function scope),函数内部有效,可以在同一个作用域重复声明和赋值;
let声明的变量属于块级作用域(block scope),块作用域内部有效;
const声明的变量定义为常量,使用时必须初始化(赋值),属于块级作用域,块级作用域内部有效;
在这里插入图片描述

变量提升:Hoisting;

console.log(color); //undefined 变量提升
var color = 'yellow';

在JavaScript中,function中variables(变量)会被提升;

变量提升是指JavaScript将声明移至当前作用域(scope)顶部的行为;

所以以上代码可以理解为:

var color;
console.log(color); //undefined
color = 'yellow';

对于let和const关键字来说也有变量提升的概念,但是在let和const中还存在一个临时性死区的概念,即在当前变量的作用域开始到变量声明之前都是处在临时性死区中的,处于临时性死区的变量引用时会报referenceError错误;

{
    console.log(color); //referenceError 临时性死区
	let color = 'yellow';
}

var、let、const取舍之道
默认使用const声明变量(use const by default)
当需要改变变量值时使用let(only use let if rebinding is needed)
尽量不使用var(varshouldn’t be used in ES6)

2.解构赋值
解构赋值可以把对象或者数组里面的 属性/值 从里面取出,赋给其他的变量
例如:

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

// 输出 1

里面不止可以使用 let 来定义,还可以使用 var
假如变量名比数组里面的内容多,则未分配到内容的变量值为 undefined
对象结构同理:

let person = { name: '菜', age: 5 }
let { name, age } = person;
console.log(name);
console.log(age);
// 输出:菜 5

// 可以使用键值对的形式来获取变量
let {name: myname,age: myage} = person;
// 冒号左侧仅用于匹配,冒号右侧的变量用来赋值
console.log(myname);
console.log(myage);
// 输出:菜 5

变量名必须和属性名相同才能取到这个变量的值
更多解构赋值的应用可以看MDN的解构

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

console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]

该运算符主要用于函数调用

function push(array, ...items) {
    array.push(...items);
}
function add(x, y) {
    return x + y;
}
var numbers = [4, 38];
add(...numbers) // 42

上面代码中,array.push(…items)和add(…numbers)这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。

扩展运算符与正常的函数参数可以结合使用,非常灵活

function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);

扩展运算符的应用
1.合并数组
扩展运算符提供了数组合并的新写法。

// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

2.与解构赋值结合
扩展运算符可以与解构赋值结合起来,用于生成数组

// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list

下面是另外一些例子。

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []:
const [first, ...rest] = ["foo"];
first // "foo"
rest // []

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

const [...butLast, last] = [1, 2, 3, 4, 5];
//  报错
const [first, ...middle, last] = [1, 2, 3, 4, 5];
//  报错

3.函数的返回值
JavaScript 的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。

var dateFields = readDateFields(database);
var d = new Date(...dateFields);

上面代码从数据库取出一行数据,通过扩展运算符,直接将其传入构造函数Date。

4.字符串
扩展运算符还可以将字符串转为真正的数组。

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

JavaScript 会将 32 位 Unicode 字符,识别为 2 个字符,采用扩展运算符就没有这个问题

5.实现了 Iterator 接口的对象
任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组

var nodeList = document.querySelectorAll('div');
var array = [...nodeList];

上面代码中,querySelectorAll方法返回的是一个nodeList对象。它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因就在于NodeList对象实现了 Iterator 接口。

6.Map 和 Set
扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。

let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]

4.Symbol数据类型

es6中新增了symbol数据类型,一个 symbol类型的值通过Symbol函数生成的,例如:
var s1 = Symbol(‘s’)

Symbol函数的特性是每一个Symbol函数的返回值都是唯一的,可以通过给symbol函数传递不同的参数产生具有不同标记的值,但是即使相同参数返回的也是唯一的,不同的,也就是说

Symbol('miaov') != Symbol('miaov') 

ymbol的作用在于属性私有化和数据保护,属性私有化的含义是,构建一种对象,它的某一个属性存在,但是不能被外面访问和修改。

如何使用同一个Symbol值

Symbol.for("bar") === Symbol.for("bar")
// true

使用Symbol.for()方法,同样参数创建的变量就是同一个值。因为使用这个方法创建的Symbol变量会把参数在全局登记。而Symbol()创建的变量的参数则不会在全局登记。

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

使用Symbol.keyFor()则可以变量找到全局登记的参数的名字。
5.Set 和 Map 数据结构
Set,本质与数组类似。不同在于Set中只能保存不同元素,如果元素相同会被忽略。
构造函数:

// Set构造函数可以接收一个数组或空
let set = new Set();
set.add(1);// [1]
// 接收数组
let set2 = new Set([2,3,4,5,5]);// 得到[2,3,4,5]

方法:

set.add(1);// 添加
set.clear();// 清空
set.delete(2);// 删除指定元素
set.has(2); // 判断是否存在
set.forEach(function(){})//遍历元素
set.size; // 元素个数。是属性,不是方法。

map,本质是与Object类似的结构。不同在于,Object强制规定key只能是字符串。而Map结构的key可以是任意对象。

即:

object是 <string,object>集合
map是<object,object>集合

构造函数:

// map接收一个数组,数组中的元素是键值对数组
const map = new Map([
['key1','value1'],
['key2','value2'],
])
// 或者接收一个set
const set = new Set([
['key1','value1'],
['key2','value2'],
])
const map2 = new Map(set)
// 或者其它map
const map3 = new Map(map);

方法:

map.set(key, value);// 添加
map.clear();// 清空
map.delete(key);// 删除指定元素
map.has(key); // 判断是否存在
map.forEach(function(key,value){})//遍历元素
map.size; // 元素个数。是属性,不是方法
map.values() //获取value的迭代器
map.keys() //获取key的迭代器
map.entries() //获取entry的迭代器
用法:
for (let key of map.keys()) {
console.log(key);
}
或:
console.log(...map.values()); //通过扩展运算符进行展开

6.Promise
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法
上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样
的方法进行处理。

我们可以通过Promise的构造函数来创建Promise对象,并在内部封装一个异步执行的结果。

语法:

const promise = new Promise(function(resolve, reject) {
 // ... 执行异步操作
 if (/* 异步操作成功 */){
  resolve(value);// 调用resolve,代表Promise将返回成功的结果
} else {
  reject(error);// 调用reject,代表Promise会返回失败结果
}
});

这样,在promise中就封装了一段异步执行的结果。

如果我们想要等待异步执行完成,做一些事情,我们可以通过 promise的then方法来实现,语法:

promise .then(function(value){
  // 异步执行成功后的回调
});
如果想要处理promise异步执行失败的事件,还可以跟上catch:

promise .then(function(value){
  // 异步执行成功后的回调
}).catch(function(error){
  // 异

步执行失败后的回调
})
示例:

const p = new Promise(function (resolve, reject) {
  // 这里我们用定时任务模拟异步
  setTimeout(() => {
    const num = Math.random();
    // 随机返回成功或失败
    if (num < 0.5) {
      resolve("成功!num:" + num)
   } else {
      reject("出错了!num:" + num)
   }
 }, 300)
})
// 调用promise
p.then(function (msg) {
  console.log(msg);
}).catch(function (msg) {
  console.log(msg);
})

// 结果:
出错了!0.834567898765434567890

7.Class
class是一个语法糖,其底层还是通过 构造函数 去创建的。所以它的绝大部分功能,ES5 都可以做到。新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function() {
    return this.name;
}

const xiaoming = new Person('小明', 18);
console.log(xiaoming);
上面代码用ES6class实现,就是下面这样

class Person {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
  
    sayName() {
      return this.name;
    }
}
const xiaoming = new Person('小明', 18)
console.log(xiaoming);
// { name: '小明', age: 18 }

console.log((typeof Person));
// function
console.log(Person === Person.prototype.constructor);
// true

constructor方法,这就是构造方法,this关键字代表实例对象。
类的数据类型就是函数,类本身就指向构造函数。

定义类的时候,前面不需要加 function, 而且方法之间不需要逗号分隔,加了会报错。

类的所有方法都定义在类的prototype属性上面。

class A {
    constructor() {}
    toString() {}
    toValue() {}
}
// 等同于

A.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

在类的实例上面调用方法,其实就是调用原型上的方法。

let a = new A();
a.constructor === A.prototype.constructor // true

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

class A {
}

// 等同于
class A {
  constructor() {}
}

constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

class A {
  constructor() {
      return Object.create(null);
  }
}

console.log((new A()) instanceof A);
// false

实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。class不存在变量提升
类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为"静态方法"。
A 类的classMethod 方法前有 static关键字,表明这是一个静态方法,可以在 A 类上直接调用,而不是在实例上调用
在实例a上调用静态方法,会抛出一个错误,表示不存在改方法。
如果静态方法包含this关键字,这个this指的是类,而不是实例。
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。
写法是在实例属性的前面,加上static关键字。
Class 可以通过extends关键字实现继承
super这个关键字,既可以当作函数使用,也可以当作对象使用
super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
在普通方法中,指向父类的原型对象;
在静态方法中,指向父类。
8.箭头函数

const numbers = [5,6,13,0,1,18,23];

//一般方式
const double = numbers.map(function(number){
	return number * 2;
})

//箭头函数,当且仅当只有一个参数时可以省略小括号,无参数也不可省略
const double2 = numbers.map(number => {
	return number * 2;
})

//箭头函数,当函数内部只有一句时可以省略大括号,称为隐式返回
const double2 = numbers.map((number, i)=> number * 2);

箭头函数属于匿名函数,可以通过赋值给变量得到匿名函数引用;

const greet = number => {alert('The number is ${number}')};

箭头函数没有其this值,它的this值是继承于它的父作用域的;

const Jelly = {
	name: 'jelly',
    hobbies: ['coding', 'Sleeping', 'Reading'],
    printHobbies: function(){
    	this.hobbies.map(function(){
        	console.log(this) // Window,全局变量,严格模式下undefined	
        })
        //修改为箭头函数即可
        this.hobbies.map(() => {
        	console.log(this) // Jelly
        })
    }

9imoprt模块导入
ES6自带了模块化, 也是JS第一次支持module, 可以直接作用import和export在浏览器中导入和导出各个模块了, 一个js文件代表一个js模块;
10.for of循环

const numbers = ['one', 'two', 'three', 'four'];

//普通for循环
for(let i = 0; i< numbers.length; i++){
	console.log(numbers[i]);
}
//forEach遍历
numbers.forEach(number => {
	console.log(number);
})
//for in循环
for(let index in numbers){ //每次循环的是索引值,且遍历过程中不可中断(break)
	console.log(numbers[index]);
}
//for of循环
for(let number of numbers){ //每次循环的是属性值;可使用break和continue
	console.log(number)
}
for of 循环可以利用entries()同时获得索引值和属性值;
for(let [index, number] of numbers.entries()){
	console(index,number)
}

11数组方法
forEach
forEach()会遍历数组, 循环体内没有返回值,forEach()循环不会改变原来数组的内容, forEach()有三个参数, 第一个参数是当前元素, 第二个参数是当前元素的索引, 第三个参数是当前元素所属的数组.

let array = [1, 2, 3, 4];
array.forEach((item, index, arr) => {
  console.log(item);
});

forEach()的用法大概就是这样的, 不过不知道各位发没发现一个问题, forEach()不能跳出循环, 我说的跳出循环是跳出整个循环而不是跳出当前的循环。

let array = [1, 2, 3, 4, 5];
array.forEach((item) => {
    if (item > 2) {
        return;
    }
    console.log(item);
});

答案是: 1 2
这时候有人就会说了, 这不是跳出循环了吗, 其实并不是, 它仍然把数组挨个遍历了一遍, 把输出换个位置我们就会发现了.

let array = [1, 2, 3, 4, 5];
array.forEach((item) => {
    console.log(item);
    if (item > 2) {
        return;
    }
});

输出了 1 2 3 4 5
看过了上面两段代码之后, 不难发现, 在forEach()循环体里写return, 只是能跳出当前循环去执行下一次循环,并不能跳出整个循环.
再说说什么叫forEach()循环没有返回值, 这个问题在看过接下来的map循环之后就会明白了.

map
map()的主要作用, 其实是创建一个新的数组, map()的参数和forEach()是一样的, 这里就不在多说了, 直接上例子.

let array = [1, 2, 3, 4 ,5];
let temp = array.map((item, index, arr) => {
    return item + 1;
});
console.log(temp);
console.log(array);

猜猜输出的temp和arr的值都是什么
temp: [2, 3 ,4, 5, 6]
arr: [1, 2, 3, 4, 5]
各位, 到这里应该明白map()和forEach()有什么区别了吧, 使用map()遍历数组, 可以返回一个新数组, 并且不会改变原数组里的内容.
当然了, map()也可以这么用, 直接把数组里的元素都转成字符串.

let temp = array.map(String);
filter
接下来再说说filter(), filter()参数和forEach()也是一样的, filter()主要是过滤的, 用来过滤数组中不满足条件的元素, 把满足条件的元素放到新的数组里, 并且不会改变原数组.
怎么用呢, 直接上代码.

let array = [1, 2, 3, 4, 5];
let temp = array.filter((item, index, arr) => {
    return item > 3;    
});
console.log(temp);
console.log(array);

会输出什么呢, temp是4, 5, array没有变化, 清晰明了吧, 是不是比用for循环硬写方便多了.

every
every() 就不解释入参了, 都一样的, 主要说说every()的作用, 它会遍历数组, 在循环体内写条件, 如果每一项都是true, 就会返回true, 只要有一个是false, 就会返回false, 下面看看实例代码.

let array = [1, 2, 3, 4, 5];
let bo = array.every((item, index, arr) => {
    return item > 2;
});
console.log(bo);

这个输出肯定是false啊,

some
some()又是做什么的呐, 同样, 遍历数组的每一项, 然后根据循环体内的条件去判断, 只要有一个是true, 就会停止循环.

let array = [1, 2, 3, 4, 5];
array.some((item, index, arr) => {
    console.log(item);
    return item > 3;
});

根据输出的item, 我们可以知道一共循环了多少次.

reduce
reduce(), 官方说明: 接收一个函数作为累加器, 数组中每个值(从左到右)开始缩减, 最终为一个值. 看完这句话, 心里莫名的想说一句 卧槽, ‘这什么玩意’. 其实说白了, reduce()接收的那个函数就是回调函数, 回调函数调用数组里的每一个元素, 直到循环结束.
reduce()跟其他几个方法不一样, 它有4个参数, 按顺序分别是 上一次的值, 当前的值, 当前值的索引, 数组. 参数介绍完毕, 直接看例子.
假如, 有一个数组, 里面的元素都是数字, 现在要计算数字的和, 正常情况就直接循环数组, 然后弄个中间变量挨个加了吧, 但是用了reduce()就省事儿多了

let array = [1, 2, 3, 4, 5];
let total = array.reduce((a, b) => {
    return a + b;
});
console.log(total);     // 15

11.对象的方法
Object.is()方法
ES5 比较两个值是否相等,只有两个运算符:=‘=’相等运算符’和严格的相等运算符‘===’它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。

ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

语法:Object.is(value1, value2);

参数:value1 被比较的第一个值;value2 被比较的第二个值

返回值Boolea 类型表示两个参数是否是一个值;

描述Object.is() 方法判断两个值是否为同一个值。如果满足以下条件则两个值相等:
都是 undefined
都是 null
都是 true 或 false
都是相同长度的字符串且相同字符按相同顺序排列
都是相同对象(意味着每个对象有同一个引用)
都是数字且
都是 +0
都是 -0
都是 NaN
或都是非零而且非 NaN 且为同一个值

与相等运算不同。 相等运算符在判断相等前对两边的变量(如果它们不是同一类型) 进行强制转换 (这种行为的结果会将 “” == false 判断为 true), 而 Object.is不会强制转换两边的值。

与严格相等运算也不相同。严格相等运算符 (也包括 == 运算符) 将数字 -0 和 +0 视为相等 ,而将Number.NaN 与NaN视为不相等;
Object.assign()
Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

语法

Object.assign(target, …sources)
参数,target:目标对象 sources 源对象
返回值: 目标对象

const target = { a: 1 };

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

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

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。

注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
Object.keys、Object.values、Object.entries
Object.keys 获取所有可遍历(可枚举)的属性键值
Object.values 获取所有可遍历(可枚举)的属性值
Object.entries 上面两个方法的合并,获取所有可遍历的属性简直及属性值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值