ES6 新特性

let const var

  1. var 存在变量提升,变量可以在声明前被使用,值为undefinedlet ``var不可以在声明前被使用,否则会报错,ES6 明确规定,如果区块中存在letconst`命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
function test() {
    console.log(a); //undefind
    var a = 1;
    console.log(b); //Uncaught ReferenceError: Cannot access 'b' before initialization
    let b = 2;
}

这就引发了一个叫暂时性死区的现象,所谓暂时性死区,就是只要在同一作用域内,包括函数和块级全局,letconst 就会绑定在这个区域,在这之前使用的话,都会报错,直到声明过后。举个例子:

var num = 2
function test() {
    console.log(num); //Uncaught ReferenceError: Cannot access 'num' before initialization
    const num = 1;
}
  1. var不存在块级作用域,letconst存在块级作用域。ES6前,Javascript只区分全局作用域(整个script标签内部或者一个独立的js文件)和函数作用域(局部作用域),ES5不存在块级作用域(凡是用{}包起来的都算),所以对于var来说:
function test() {
    for(var i=0; i<10; i++) {}
    console.log(i); //10
}

对于let来说:

function test() {
    for(let i=0; i<10; i++) {}
    console.log(i); // Uncaught ReferenceError: i is not defined
}
  1. 初始值
    var let 可以不设置初始值,const必须设置初始值
const a; // Uncaught SyntaxError: Missing initializer in const declaration
  1. 重复声明
    var 可以重复声明, let const不允许
  2. 数据修改
    varlet允许修改数据或者重新赋值,const定义的如果是基本数据类型,是不允许修改的,如果是引用数据类型,那么保存在栈中的堆地址是不可以修改的,真正的数据可以修改。

解构

  1. 数组解构
const [a, b, c, d] = [1,2,3,4]
console.log(a,b,c,d); // 1 2 3 4
  1. 对象解构
const {a, b, c, d} ={a: 1, b: 2, c: 3, d: 4}
console.log(a,b,c,d); // 1 2 3 4

模版字符串

var names = ["Tom", "Jane", "Tim"]
var name =  `${names} are coming.`

箭头函数

  1. 3分钟理解箭头函数的this
  2. 没有arguments
function test(){
  console.log(arguments)
}
test(1,2,3); // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
const test2 = () =>{
  console.log(arguments)
}
test2(); // Uncaught ReferenceError: arguments is not definedat test2
  1. 不能通过 new 关键字调用, 根据new的原理来看,箭头函数不具备调用的条件:
test.prototype
// {constructor: ƒ}
test2.prototype
// undefined

这里简单描述一下new Test('abc')的调用过程:

  • 创建一个空对象obj
  • obj__proto__指向Test的原型对象prototype,此时便建立了obj对象的原型链:obj->Animal.prototype->Object.prototype->null
  • obj对象的执行环境调用Test函数并传递参数'abc'。 相当于var result = obj.Test('abc').
  • 考察第3步返回的返回值,如果无返回值或者返回一个非对象值,则将obj返回作为新对象;否则会将返回值作为新对象返回。

形参默认值

有默认值的形参位置要放到最后

function add(a,b,c=2) {
    console.log(a + b + c);
}
add(1,2) //5

与解构赋值结合使用 结构赋值的先后不影响

function connect({name, age=18, sex}) {
    console.log(name);
    console.log(age);
    console.log(sex);
  }
  connect({
    name:'小宝',
    sex: 'man'
  })

Symbol

Symbol是ES6新引入的一种原始数据类型,表示独一无二的值,常用于命名不能冲突的场景,比如对象的key,或者定义常量替换无意义的字符串,创建时直接Symbol(),不能使用new
作为key:

const MY_KEY = Symbol();
let obj = {};
obj[MY_KEY] = 123;
console.log(obj[MY_KEY]); // 123
let obj2 = {
  [MY_KEY]: 123
};
console.log(obj2[MY_KEY]); // 123
let obj3 = {
  [MY_KEY]() {
    return 'bar';
  }
};
console.log(obj3[MY_KEY]()); // bar

作为常量

const levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
}

function log(type, message) {
  switch (type) {
    case levels.DEBUG:
      console.log(message)
      break
    case levels.INFO:
      console.log(message)
      break
    case levels.WARN:
      console.log(message)
      break
    default:
      console.log('default')
      break
  }
}

Symbol.for(key)

Symbol.for(key)通过key(一个字符串,作为 symbol 注册表中与某 symbol关联的键,同时也会作为该 symbol 的描述)来判断其唯一性,key必须是字符串或者可以被转换成字符串(因此Symbol类型不能作为key,不过很难想象它有toString方法却不能被动转换成字符串),不是字符串的,调用toString()转换为字符串,如果无法转换成字符串的,会报错。undefinednull 没有toString(),但是不会报错,当做字符串'undefined''null'处理,默认值即为undefined。返回由给定的 key 找到的 symbol,否则就是返回新创建的symbol
判断Symbol.for(key)的返回值是否相等其实就是在判断两个keytoString()返回结果是否相等.

Symbol.for([1,2,3]) === Symbol.for('1,2,3'); // true
Symbol.for() === Symbol.for(undefined); // true
Symbol.for() === Symbol.for('undefined'); // true
Symbol.for(null) === Symbol.for('null'); // true
Symbol.for({}) === Symbol.for({a: 123}); // true
Symbol.for([]) === Symbol.for(""); // true
Symbol.for(Infinity) === Symbol.for("Infinity"); // true
Symbol.for(NaN) === Symbol.for("NaN"); // true

Symbol.iterator

Symbol.iterator是一个内置值如果对象有Symbol.iterator, 即obj[Symbol.iterator] !== undefined那么这个对象就可以被for...of遍历

for (let i of [1,2,3] ) {
  console.log(i); // 1 2 3
}

for (let i of {num1: 1, num2: 2} ) {
  console.log(i); // Uncaught TypeError: {(intermediate value)(intermediate value)} is not iterable
}

因此如何让一个对象可以被for...of:

  1. 给对象添加一个keySymbol.iterator的属性方法
  2. 这个方法必须返回一个迭代器对象,它的结构必须如下:
{
    next: function() {
        return {
            value: any, //每次迭代的结果
            done: boolean //迭代结束标识
        }
    }
}

举个例子:

obj={names: ["Tom", "Jane", "Tim"];
obj[Symbol.iterator] = function () {
  let i = 0
  const _this = this
  return {
    next: () => {
      return { 
        value: _this.names[i++], 
        done: i === _this.names.length 
      }
    },
  }
}
for(let i of obj) {
    console.log(i); // Tom Jane Tim
}

获得属性名称

const MY_KEY = Symbol()
let obj2 = {
  [MY_KEY]: 123,
  enum: 2,
  nonEnum: 3,
}
console.log(Object.getOwnPropertyNames(obj2)) // ['enum', 'nonEnum']
console.log(Object.getOwnPropertySymbols(obj2)) // [Symbol()]
console.log(Reflect.ownKeys(obj2)) // ['enum', 'nonEnum', Symbol()]

Set

类似数组,但成员值都是唯一的,可以方便的去重,求并集、交集、差集。

Map

Promise

有三种状态pending fulfilled rejected

创建实例

new Promise(function(resolve, reject) {...})
创建Promise实例时,需要往构造方法中传入一个函数作为参数,这个函数可以通过调用传入的resolvereject方法来改变promise实例的状态,调用resolvepending转变为fulfilled,调用rejectpending转变为rejected

实例方法

  1. then 实例状态发生变化时,触发的回调函数,第一个参数是 resolved状态的回调函数,第二个参数是 rejected 的回调函数(一般使用 catch 方法来替代第二个参数)
  2. catch 用于指定发生错误的回调函数
  3. finally 用于指定 不管 Promise对象最后状态如何,都会执行的操作

静态方法

  1. Promise.all 将多个 Promise 实例包装成一个新的 Promise 实例
    const p = Promise.all([p1, p2, p3]);
    只有 p1,p2,p3 状态全为 fulfilledp 的状态才会变成fulfilled。此时,p1,p2,p3 的返回值组成一个数组,传递给 p 的回调函数。
    只要 p1,p2,p3 有一个状态为 rejected ,那么 p 的状态就变成 rejected。此时第一个被reject的实例的返回值,会传递给p的回调函数。

  2. Promise.race 将多个 Promise实例包装成一个新的 Promise 实例
    const p = Promise.race([p1, p2, p3]);
    三者谁先改变状态, p 也就会跟着改变状态。率先改变的会将返回值传递给 p 的回调函数。

await async

async 是加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象的实例,因此被async标记的函数可以直接then。
await也是一个修饰符,只能使用于被标记为async的方法内,取到值时才会往下执行。

浅聊 promise setTimeout aysnc await

  • 首先,这些不是同步任务。
  • JS是单线程语言(注意区分线程和进程,而且仅JS是单线程,浏览器渲染等占用的是其他线程),在这个单线程上,会有同步任务和异步任务,异步任务又包括宏观任务和微观任务。
  • 宏观任务包括(宿主发起):script(整体代码)setTimeout setIntervalI/O UI交互事件 postMessage MessageChannel setImmediate
  • 微观任务包括(JS引擎发起):promise.then MutationObserver process.nextTick await/async(实际上是promise + generator 语法糖)
  • 执行顺序 所有的微任务都存在于某个宏任务中,所以一定是由宏任务开始执行。首先我们分析有多少个宏任务,在每个宏任务中,分析有多少个微任务,根据调用次序,确定宏任务中的微任务执行次序,根据宏任务的触发规则和调用次序,确定宏任务的执行次序,最后确定整个顺序。
console.log('start')
setTimeout(() => {
    console.log('setTimeout complete')
})
setTimeout(() => {
    new Promise((resolve, reject) => {
        for(let i =0;i<5;i++) {
        }
        console.log('promise3 internal function complete')
        resolve()
    }).then(() => {
        console.log('promise3 complete')
    });new Promise((resolve, reject) => {
        for(let i =0;i<5;i++) {
        }
        console.log('promise4 internal function complete')
        resolve()
    }).then(() => {
        console.log('promise4 complete')
    });
})
new Promise((resolve, reject) => {
    for(let i =0;i<5;i++) {
    }
    console.log('promise internal function complete')
    resolve()
}).then(() => {
    console.log('promise complete')
});new Promise((resolve, reject) => {
    for(let i =0;i<5;i++) {
    }
    console.log('promise2 internal function complete')
    resolve()
}).then(() => {
    console.log('promise2 complete')
});
/**
console.log('end')
start
promise internal function complete
promise2 internal function complete
end
promise complete
promise2 complete
setTimeout complete
promise3 internal function complete
promise4 internal function complete
promise3 complete
promise4 complete
**/

以上代码的顺序大致可以描述为:

  1. script宏观任务
  2. 同步任务:console setTimeout Promise等初始化
  3. 所有微任务 promise1.then promise2.then
  4. 下一个宏任务 setTimeout1
  5. 同步任务: console
  6. 无微观任务,因此直接开始下一个宏观任务 setTimeout2
  7. 同步任务 Promise初始化
  8. 所有微任务 promise3.then promise4.then

深拷贝 浅拷贝

深拷贝后的所有数据均不受被拷贝的数据的影响,至于为什么有可能被影响不做赘述。
浅拷贝基本上是拷贝第一层的基本数据类型值,以及第一层的引用类型地址。

浅拷贝 Object.assign

Object.assign将一个或多个源对象的可枚举属性的值复制到目标对象,并返回目标对象。

const obj1 = {
    name: 'Sue',
    age: 18,
    getAge: () => this.age,
    key: Symbol('key'),
    mom: {
        name: 'Jane',
        age: 45,
        key: Symbol('key')
    }
}
const obj2 = {}
Object.assign(obj2, obj1)
// {name: 'Sue', age: 18, key: Symbol(key), mom: {…}, getAge: ƒ}
obj2.mom.age = 50
obj1.mom.age
// 50

以上例子可以证明使用Object.assign实现的是浅拷贝,且可以拷贝Symbol数据类型。

浅拷贝 数组

concat

let arr = [1, 2, 3];
let arr2 = arr.concat()

slice

let arr = [1, 2, 3];
let arr2 = arr.slice()

深拷贝 JSON.parse + JSON.stringify

let arr = [1, 3, { username: ' kobe' },function(){}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'duncan'; 
console.log(arr[2].username): //kobe

这种方式不能拷贝函数

let arr = [1, 3, { username: ' kobe' },function(){}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'duncan'; 
console.log(arr2[3]); // undefined

深拷贝 递归遍历

function checkType(value) {
  return Object.prototype.toString.call(value).slice(8, -1)
}

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

推荐阅读更多精彩内容

  • ES6 新特性 ES6 新特性 一ES6简介 二块级作用域绑定1 let声明2 const声明Constant D...
    _无为_阅读 832评论 0 3
  • 总结: var let const的区别 数组新增拓展 对象新增拓展 函数新增拓展 Set 和 Map Promi...
    Porsche_Apo阅读 701评论 0 0
  • ES6、ES7、ES8特性一锅炖(ES6、ES7、ES8学习指南) 概述 ES全称ECMAScript,ECMAS...
    李振亚_cb74阅读 305评论 0 0
  • ES6语法 Tags: javascript [TOC] const 与 let 变量 使用var带来的麻烦: 运...
    Showdy阅读 178,782评论 6 114
  • ES6改动很大,可以简单分为四类1、解决原有语法的缺陷和不足例如:let,const2、对原有语法进行增强解构、扩...
    少_游阅读 8,268评论 0 12