es6的常用特性

let、const和块级作用域

let

  • let没有变量提升、可以重复定义检查,同一个作用域中重复定义就会报错、可以被用于块级作用域
  • 使用let形成的块级作用域可以在大部分具有{...}的语句中使用,例如for(){} , do{}while() , while{} 和switch(){}等。
if(true){                           //块级作用域
    let a=10;
}
//console.log(a);              //报错 a is not defined
---------------------------------------------
let b=10;
b=20;
console.log(b)                 //20  可以被重新赋值
---------------------------------------------
let x1=1;
{
    console.log(x1);            //1
}
//以上证明  作用域链是存在的

const

  • 定义常量,对常量赋值会报错
  • const定义对象不能完全锁死对象
  • 栈内存中存放的只是该对象的访问地址,在堆内存中为这个值分配空间
  • const的原理便是在变量名与内存地址之间建立不可变的绑定,当后面的程序尝试申请新的内存空间时,引擎便会抛出错误。
const person={'name':'aaa'};
person.name='bbb'
console.log(person.name);//不是完全锁死

const a=10;      //const必须给一个初值  否则会报错   并且不能重复声明
a=20;               //报错   不可修改
console.log(a)
  • 完全锁死对象要用Object.freeze();多层属性对象可以利用递归锁死。
const obj1=Object.freeze({
  a:1,
  b:2
})

内存与变量之间的关系

变量与内存关系.JPG
  • Ecmascript在对变量的引用进行读取时,会从该变量当前所对应的内存地址所指向的内存空间中读取内容。而当用户改变变量的值时,引擎会重新从内存中分配一个新的内存空间以存储新的值,并将新的内存地址与变量进行绑定。
  • 关于堆内存和栈内存
    栈内存存放的是该对象的访问地址,在堆内存中为这个值分配空间

let const的使用原则

  • 1.一般情况下,使用const来定义值的存储容器(常量)
  • 2.只有在值容器明确被确定将会被改变时才使用let来定义(变量)
  • 3.不再使用var

let const与循环语句for...of...
for...of..循环不能直接遍历对象(但是支持return)

const zootop=[
{ name: 'nick', gender: 1,species:'fox'},
{name: 'judy', gender: 0,species: 'Bunny'}
];
for(const {name, species} of Zootopia) {
console. log(`Hi, I am (name), and I am a (species)`)
}
//Hi, I am nick, and I am a fox
//Hi, I am judy, and I am a Bunny
  • 数组类型再次被赋予了一个名为entries的方法,它可以返回对应数组中每一个元素与其下标配对的一个新数组
const arr=['a','b','c'];
console.log(arr.entries())//[[0,'a'],[1,'b'],['2','c']];

模板字符串

把变量放在${ }中,字符串放在反引号中,即可渲染,代替字符串拼接

const zootop=[
    { name: 'nick', gender: 1,species:'fox'},
    {name: 'judy', gender: 0,species:'Bunny'}
];

for(const [index,{name,species}] of zootop.entries()){
    console.log(`${index} i am ${name} `)
}

简单实现:

function templatea(str,arr){
   var reg=/\$\{([^\{\}]+)\}/g;//匹配一个${...};
   var i=0;
   var str=str.replace(reg,function(){
       return arr[i++];
   })
   return str;
}
console.log(templatea('ajlj${aaa}sdkl${bbb}fjs${ccc}d',[111,222,333]))
//ajlj111sdkl222fjs333d

原理:

let aaa=111,bbb=222,ccc=333;
desc`ajlj${aaa}sdkl${bbb}fjs${ccc}d`;
function desc(raw,...Ary){
  let res=''
  for(let i=0;i<raw.length-1;i++){
    res+=raw[i]+Ary[i]
  }
  res+=raw[raw.length-1];
  return res;
}

默认参数 和 拓展运算符

默认参数

function fn(arg='foo'){
  console.log(arg);
}
fn();//foo
fn('bar')//bar

类数组转数组

//类数组转数组的一般方式
args=[].slice.call(arguments);
//Array的新方法
args=Array.from(arguments)
//剩余参数
function fn(foo,...rest){
  //rest是arguments除去foo的剩余数组[2,3,4,4]
}
fn(1,2,3,4,4)
--------------------
arr1=[...arr2];//数组拷贝

剩余参数注意事项

  • 使用剩余参数后,后面不可以再添加任何参数,否则会报错
  • 推荐使用 ...arg 代替 arguments

解构赋值

  1. 只要等号两边的模式相同,左边的变量就会被赋予对应的值 这种写法叫做‘模式匹配’
var [a,b,c]=[12,5,'aaa']
console.log(a,b,c)//12,5,'aaa'
  1. 跟顺序无关 键值对对应的名字 a,b,c 必须一致 否则解析不出来
var {a,b,c}={b:5,a:10,c:'aa'}
console.log(a,b,c)//10,5,'aa'
  1. 模式匹配 左右两边的格式一样
var [a,[b,c],d]=[10,[1,2],100]
console.log(a,b,c,d)//10,1,2,100
  1. 给默认值 可用于函数参数的默认值
var {time=12,a}={time,a:'111'}
console.log(time,a)//12,'111'
  1. 对象的解构赋值
let {foo,bar}={foo:'aaa',bar:'bbb'};
console.log(foo)  //aaa
//对象的解构于数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由他的位置决定;而对象的属性没有次序,变量必须与属性同名
  1. 匹配的模式与真正的变量
let { foo3: foo2, bar: bar2 } = { foo3: "aaa", bar: "bbb" };
let { foo2: baz2 } = { foo2: "aaa", bar: "bbb" };

console.log(baz2 +'---------baz2') // "aaa"
console.log(foo2 +'---------foo2')//'aaa'
console.log(foo3) // error: foo3 is not defined
//上面代码中,foo3是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo3。
  1. 属性的简洁表示法,属性名和属性值一样的情况下可以简写
 var foo='bar';
 var baz={foo};
 baz //{foo:'bar'}
 //等同于
 var baz={boo:foo}
 //上面代码表明,ES6允许在对象之中,直接写变量。这时,属性名为变量名,属性值为变量值
 function f(x,y){
   return {x,y}
 }
 //等同于
 function f(x,y){
   return {x:x,y:y}
 }
  1. 方法简写
var o={
   method(){
       return 'Hello';
   }
 }
 
  //等同于
  var o={
   method:function(){
      return 'Hello'
   }
 }

9. 变量值转换(三个杯子调换两杯水的问题)

传统方法
function swap(a,b){
var temp=a;
a=b;
b=temp;
}
现在的方法(利用解构赋值)
[a,b]=[b,a]

注:

  • 如果解构不成功,变量的值就等于undefined
  • 如果等号右边不是数组(或者严格的说,不是可遍历对象),那么将会报错

箭头函数

  1. 单行
  • 单行可以省略 return,
  • 只能包含一条语句
  • 返回对象时要用()包裹返回的对象
const fn=foo=>`${foo} world`//意思是return `${foo} world`
相当于
var fn=function (foo){
  return foo+' world';
}
  1. 多行(需要写return,需要 { } 包裹函数体)
foo=>{
    return `${foo} world`; 
}
  1. 有无参数
1.如果一个箭头函数无参数传入,则需要一个空括号占位
const fn=()=>{
  ........
}
2. 如果有多个参数(需要括号)
const fn=(arg1,arg2,...args)=>{
  ........
}
3. 如果有一个参数(不需要括号)
const fn=foo=>{
  return `${foo} world`
}
  1. this指针
  • 箭头函数中this会延伸至上一层作用域中,即上一层的上下文会穿透到内层的箭头函数中
const obj={
  a:111,
  foo(){
    const bar = ()=>this.a;
    return bar;
  }
}
window.a=222
window.bar=obj.foo();
window.bar();//111  不关心函数执行时前边是谁,this最开始指定谁,是不会再改变的
--------------------------
以上foo函数代码类似于
foo(){
  var that=this;
  var bar=function(){
    return that.a
  }
}
//可以说:在当前作用域的上层作用域定义that=this,函数内使用that
  • apply和call无法改变this的指向,bind是可以改变this的
  • 不要随意在顶层作用域使用this,防止指向window 报错
  1. 箭头函数中没有arguments,callee和caller对象,想要使用arguments的话,用 ...args代替
const fn=(...args){
  console.log(args[0])
}
fn(1,2,3)//1

对象、数组新特性

  1. 对象支持proto注入:相当于开放原型链,可以改变所属类的原型
继承的又一种方式
obj.__proto__=new Array();//对象obj继承数组的属性和方法;
  1. 可动态计算的属性名:可以使用一个表达式作为属性名
const obj={};
const key='foo';
obj[key+'abc']='aaa';
  1. 变量名和属性名相同的可以省略
const userInfor='aaa';
const obj={
  userInfor
}
相当于
const obj={
  'userInfor':userInfor
}
  1. Array.from(arg)//类数组转数组
  2. 对象拷贝
let obj={a:{c:{d:1}}}
let newObj=Object.assign({},obj)
newObj.a==obj.a//true
Object.assign实现了对象的浅拷贝
let newObj2=JSON.parse(JSON.stringify(obj))
newObj2.a==obj.a//false JSON.parse(JSON.stringify(obj))实现了对象的深拷贝

class类

基本定义

  • 函数原型实现
function Person(name,age){
  this.name=name;//私有属性
  this.age=age
}
Person.prototype.sayName=function(){//公有属性
  console.log(this.name);
}
var person1=new Person('jack',19);
person1.sayName();//jack
  • class实现
class Person{
  constructor(name,age){//私有属性
    this.name=name;
    this.age=age
  }
  sayName(){//公有属性
    console.log(this.name)
  }
}
//原本的构造函数被类的constructor方法代替,其余原本需要在constructor中的方法则可以直接定义在class内
  • 注意:在类中的方法,都是带有作用域的普通函数,而不是箭头函数,方法第一层所引用的this都是指向当前实例

继承语法extends 和 super(原理是this继承:把父类的私有属性和方法,克隆一份一模一样的作为子类的私有属性)

class Woker extends Person{
  constructor(name,age,job){
    super(name);//调用父级的构造的属性 , 此处没有继承父类的age属性
    this.job=job;//添加自己的属性
  }
  sayWork(){//添加自己的方法
    console.log(this.job+' '+this.name+' '+this.age)
  }
}
var p1=new Person('');
var w1=new Worker('mmm',59,"worker");
w1.sayWork()//worker mmm undefined  所以此处没有age属性,即使传值也无效
w1.sayName()//继承了父类的公有属性
  • 如果一个子类继承了父类,那么在子类的constructor构造函数中必须使用super函数调用父类的构造函数后才能在子类的constructor构造函数中使用this,否则会报错

Getter、Setter在类中的使用

//还是这个例子
class Person{
  constructor(name,age,banzhaun){//私有属性
    this.name=name;
    this.age=age;
    this.banzhaun=banzhaun
  }
  sayName(){//公有属性
    console.log(this.name)
  }
  get gongqian(){
      console.log(10*this.banzhaun+'块钱')
  }
}
var person1=new Person('jack',19,100);
person1.gongqian//1000块钱

静态方法

  • 如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是通过类来调用,实例不能调用,属于静态方法
class Person{
  constructor(name,age,banzhaun){//私有属性
    this.name=name;
    this.age=age;
    this.banzhaun=banzhaun
  }
  sayName(){//公有属性
    console.log(this.name)
  }
  get gongqian(){
      console.log(10*this.banzhaun+'块钱')
  }
  static own(){
    console.log(this+'这是一个实例不能调用的方法,只有类能调用')
  }
}
Person.own()//函数中zhis指向的是类不是实例,没有实例的属性
var person1=new Person('jack',19,100);
person1.gongqian//1000块钱

ES6 module

导出模块和引入模块需要相对应 否则有时会报错
导出页面 多个export 相当于导出一个对象 引入是也需要一个对象解构

导出
export let a=1;
export let b=2;
导入
import {a,b} from 'modulePath'
或者
import  *  as  c  from 'modulePath'//*代表所有的,取时候可以用c.a,c.b

导出的是一个对象的情况下

导出
export default {xxx:xxx,xxx:xxx}
导入
import xxx from 'modulePath'

引入模块(import有预解释的功能 )

1. import name from 'module-name'//从module-name引入所有接口对象
2. import * as name from 'module-name'//给引入的所有接口对象起别名
3. import {member1,member2} from 'module-name'//引入部分接口
4. import {member as alias} from 'module-name'//给引入的部分接口起别名
5. import  from 'module-name'//不引入接口,只执行内部代码

导出模块

1. export 除了输出变量,还可以输出函数和类
export var sex="boy";
export var echo=function(value){
  console.log(value)
}
export class Foo{
  ...
}
2. 导出的语句必须要有声明和赋值两部分
const foo ='bar';
export foo//报错
3.加 { } 才代表是接口不是值
var sex="boy";
var echo=function(value){
  console.log(value)
}
export {sex,echo}
4.让一个值直接成为模块的内容,而无需声明
var sex="boy";
export default sex(sex不能加大括号)
模块的示例
  • 1.导出一个对象
// math.js
export default math = {
    PI: 3.14,
    foo: function(){}
}
// app.js
import math from "./math";
math.PI
  1. 导出函数、变量、对象
// export Declaration
export function foo(){
    console.log('I am not bar.');
}
// export VariableStatement;
export var PI = 3.14;
export var bar = foo;   // function expression
// export { ExportsList }
var PI = 3.14;
var foo = function(){};

export { PI, foo };//对象简写(导出对象)
  1. 引入
// import { ImportsList } from "module-name"
import { PI } from "./math";//引入单个接口
import { PI, foo } from "module-name";//引入两个接口
// import IdentifierName as ImportedBinding
import { foo as bar } from "./math";//引入foo函数并起别名叫bar
bar();  // use alias bar
// import NameSpaceImport
import * as math from "./math";//引入math文件中所有接口
math.PI
math.foo()
  1. 把引入的接口再导出
// components.js
import Button from './Button';
import Header from './Header';

// export { ExportsList }
// Not ES6 Destructing. Not object property shorthand
export {
    Button,
    Header
}
// app.js
// import { ImportList }
import { Button, Header } from "./components";

Set和Map

set无序集合(无序,元素不可重复的集合对象)

//Set本身是一个构造函数,用来生成Set数据结构
const s1 = new Set();
  • api:
    1. set.add(value):添加元素到集合内
    2. set.delete(value):删除集合内指定元素
    3. set.clear(value):清空集合内元素
    4. set.forEach(callback):遍历集合内所有元素,并调用callback
    5. set.has(value):检查集合内是否含有某元素
[2, 3, 5, 4, 5, 2, 2].forEach(x => s1.add(x));
for (let i of s1) {
    console.log(i);
}
//上面代码通过add方法向set结构加入成员,结果表明Set结构不会重复添加相同的值。
  • 实例
// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
// 例三
function divs () {
    //return [...document.querySelectorAll('div')];
}
//const set = new Set(divs());
set.size // 56
// 类似于
//divs().forEach(div => set.add(div));
set.size // 56

数组去重

2 种去除数组重复成员的方法。
[...new Set(array)];

function dedupe(array) {
    return Array.from(new Set(array));
}
  • 向Set加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。Set内部判断两个值是否不同,使用的算法叫做“Same-value equality”,它类似于精确相等运算符(===),主要的区别是NaN等于自身,而精确相等运算符认为NaN不等于自身。
只能添加一个NaN
let set1 = new Set();
let a = NaN;
let b = NaN;
set1.add(a);
set1.add(b);
set1 // Set {NaN}
  • 遍历操作
    1. keys():返回键名的遍历器
    2. values():返回键值的遍历器
    3. entries():返回键值对的遍历器
    4. forEach():使用回调函数遍历每个成员
//由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
let set2 = new Set(['red', 'green', 'blue']);

for (let item of set2.keys()) {
    console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
    console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
    console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

Map( 值--对--值 的映射关系 )

  • 含义和基本用法
    1. js的对象,本质上是键值对的集合,但是传统上只能用字符串当做键 这给他的使用的带来很大的限制
    2. 它类似于对象,也是键值对的集合,但是 键 的范围不限于字符串 各种类型的值(包括对象)都可以当做键。
    3. 也就是说,Object结构提供了‘字符串---值’的对应,Map提供了‘值---值’的对应,是一种更完善的hash结构形式。如果你需要“键值对”的形式map比Object更合适
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
  • 遍历
const map = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three'],
]);

console.log([...map.keys()])
// [1, 2, 3]

console.log([...map.values()])
// ['one', 'two', 'three']

console.log([...map.entries()])
// [[1,'one'], [2, 'two'], [3, 'three']]

console.log([...map])
// [[1,'one'], [2, 'two'], [3, 'three']]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容