es6初步深入学习

es6前言(只作了解就好)

ECMAScript和JavaScript的关系

ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现(另外的ECMAScript方言还有Jscript和ActionScript)。

检测node环境对ES6的支持情况

阮一峰写了一个ES-Checker模块,用来检查各种运行环境对ES6的支持情况。运行下面的命令,可以查看你正在使用的Node环境对ES6的支持程度。

sudo npm i -g es-checkeres-checker

babel转码器

ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。es6在线编辑器/在线转码器。

babel stage

在es6标准出来之前,大家都会参与讨论,把各自觉得好的语法加进去,便有了很多阶段(准标准、草案、提案..)。stage-0是一个提案,里面基本包括大家讨论的所有内容,stage-1 -- stage-3以此类推,stage-3基本确定要进入es6了。所以使用webpack的同学会看到,安装的依赖基本都是babel-preset-stage-0。

babel-polyfill

本人之前开发中遇到过一个问题,array的includes方法(string也有),当时运行在chrome47环境下,整个页面打不开,报错(babel includes can not a function),在chrome52环境下支持。解决办法--安装babel-polyfill。最后在你的文件中import 'babel-polyfill'。

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。

let和const

let用于声明变量,const用于声明常量。但是聊这个之前,我们还是简单提提老生常谈的问题吧---scope。

块级作用域

在es5中是不存在块级作用域的,一般我们听得比较多的是函数作用域(定义在函数中的参数和变量在函数外部是不可见的)。

function getVal(boo) {if (boo) {var val = 'red'// ...return val} else {// 这里可以访问 valreturn null}// 这里也可以访问 val}

那么在es5中如何使用块级作用域呢?js中在一个函数中定义的变量,当这个函数调用完后,变量会被销毁,我们可以利用这种特性(闭包,the most important feature!!)。

function caniuse() { for(var i=0;i<10;i++){} console.log(i); //10} caniuse();function caniuse2() {(function() {for(var i=0;i<10;i++){}})()console.log(i) //i is not defined}caniuse2()

在es6中,任何一对花括号中(for,if)的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

function getVal(boo) {if (boo) {let val = 'red'// ...return val} else {// 这里访问不到 valreturn null}// 这里也访问不到 val}

有了es6,上面闭包的写法我们用es6可以不需要用到立即执行函数。

    for(let i = 0; i < 10; i++){}

let

let声明的变量只在他的代码块内有效

    {let i = 9;console.log(i) //9}console.log(i) //ReferenceError: i is not defined
function test1() {for(var i = 0; i < 10; i++) {setTimeout(function() {console.log(i) //10次10}, 1000)}}function test2() {for(let i = 0; i < 10; i++) {setTimeout(function() {console.log(i) //0-9}, 1000)}}function test3() {for(var i = 0; i < 10; i++) {(function(i) {setTimeout(function() {console.log(i) //0-9}, 100)})(i)}}test1()test2()test3()

不存在变量提升

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

    console.log(i) //undefinedconsole.log(ii) //ReferenceError: ii is not definedvar i;let ii;

还有一些比较不容易发现的暂时性死区

function test1(y = 1, x = y) {console.log(x, y) // 1 1}function test2(x = y, y = 1) {console.log(x, y) //error}test1()test2()

不允许重复声明

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

const

  • 用const声明,常量的值就不能改变。

  • const一旦声明变量,就必须立即初始化,不能留到以后赋值。

  • 只所在的块级作用域内有效。(见let)

  • 不能变量提升,存在暂时性死区,只能在声明的位置后面使用。(见let)

  • 在一个scope中不可重复declare。

const foo; // SyntaxError: Missing initializer in const declaration
  • 对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。

const foo = {};foo.prop = 123;foo.prop// 123foo = {}; // TypeError: "foo" is read-onlyconst a = [];a.push('Hello'); // 可执行a.length = 0;    // 可执行a = ['Dave'];    // 报错const foo = Object.freeze({});// 常规模式时,下面一行不起作用;// 严格模式时,该行会报错foo.prop = 123;

小结

ES5只有两种声明变量的方法:var命令和function命令。ES6除了添加let和const命令,另外两种声明变量的方法:import命令和class命令。所以,ES6一共有6种声明变量的方法。

Iterator(简单说下,下次再仔细补充)

有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。Iterator是一种机制,为不同的数据结构提供可遍历操作。可遍历结构,我的理解是可以执行for...of。

Iterator作用:

  • 为各种数据结构,提供一个统一的、简便的访问接口

  • 使得数据结构的成员能够按某种次序排列

  • ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

Iterator的遍历过程

    var it = makeIterator(['a', 'b']);it.next() // { value: "a", done: false }it.next() // { value: "b", done: false }it.next() // { value: undefined, done: true }function makeIterator(array) {var nextIndex = 0;return {next: function() {return nextIndex < array.length ?{value: array[nextIndex++]} :{done: true};}};}

for...of

let arr = [2,3,4,,7,8]for(let val of arr) {console.log(val) //2,3,4,undefined,7,8}for(let k in arr) {console.log(k) //"0" "1" "2" "4" "5",下标都是字符串哦}console.log(arr["2"]) //4let arr2 = {0: 'a', 1: 'b', 3: 'c'}for(let key of arr2) {console.log(key, arr2[key]) //arr2[Symbol.iterator] is not a function}for(let key in arr2) {console.log(key, arr2[key]) //"0" "a"//"1" "b"//"3" "c"}

Iterator的使用场合

  • 解构赋值(下一章)

  • 扩展运算符(spread, 方便,使用频率很高。。。spread arguments in function)。

    let [a, ...c] = [3, 5, 6, 7, 7, 9]console.log(a) //3console.log(c) //[5, 6, 7, 7, 9]let [e, ...f, g] = [3, 5, 6, 7, 7, 9]console.log(e) //3console.log(f) //[5, 6, 7, 7, 9]console.log(g) //errorvar str = 'hello';[...str] //  ['h','e','l','l','o']let arr = ['b', 'c'];['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
  • yield*

let generator = function* () {yield 1;yield* [2, 4];yield 5}let iterator = generator()console.log(iterator.next()) //{"done": false, "value": 1}console.log(iterator.next()) //{"done": false, "value": 2}console.log(iterator.next()) //{"done": false, "value": 4}console.log(iterator.next()) //{"done": true, "value": 5}
  • 其他(for...of, Array.form(), Map(), Set())

变量的解构赋值(Destructuring)

解构赋值——我的字面理解是先分解结构,再匹配等号两边相对应的结构,如果解构成功给每个相对应地变量赋值,一般情况下如若不成功,就给对应的变量赋值undefined。

模式匹配

只要等号两边的模式相同,解构完全匹配。左边的变量就会被赋予对应的值。

let [a, ...c] = [3, 5, 6, 7, 7, 9]console.log(a) //3console.log(c) //[5, 6, 7, 7, 9]let [x, y, ...z] = ['a'];x // "a"y // undefinedz // []

不完全解构

等号右边的解构匹配左边的,但是还有多余的值。如果右边不具备Iterator接口,解构的过程中会报错。

    let [a, [b], d] = [1, [2, 3], 4];a // 1b // 2d // 4let [foo] = 1;let [foo] = false;let [foo] = NaN;let [foo] = undefined;let [foo] = null;let [foo] = {};

默认值

之前写redux的时候,经常会用到默认值。

    export function receiveCustomers({source=null, origin=null, start_at=null, end_at=null, keyword=null}) {return {type: types.CUSTOMERS_QUERY_CHANGE,source,origin,start_at,end_at,keyword}}
  • null/undefined

    var [x = 1] = [undefined];x // 1var [x = 1] = [null];x // null
  • function

function aaa() {console.log(1)}let [a = aaa()] = [1] //a=1
  • 变量不能提升

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

对象解构

这个灰常常用。一般用法就行,不需要很怪异的写法,可读性差。总结几点注意事项:

  • 如果要将一个已经声明的变量用于解构赋值,必须非常小心。

  • 默认值生效的条件是,对象的属性值严格等于undefined。

    // 错误的写法var x;{x} = {x: 1};// SyntaxError: syntax errorvar {x = 3} = {x: undefined};x // 3var {x = 3} = {x: null};x // null

对象的扩展

属性的简洁表示

这个比较常用,之前写vuex的时候有使用到。用过vuex/redux的同学应该不会陌生

    import {fn1, fn2} from 'action'//action.js里面有两个方法fn1,fn2,最后 export {fn1,fn2}//等同于export{fn1: fn1, fn2: fn2}const { dispatch } = this.props//等同于const dispatch = this.props.dispatchvuex: {actions: {fn1,fn2}}//等同于vuex: {actions: {fn1: fn1,fn2: fn2}}
    function f(x, y) {return {x, y};}// 等同于function f(x, y) {return {x: x, y: y};}f(1, 2) // Object {x: 1, y: 2}

方法的简写

    handleText = e => {this.setState({inputValue: e.target.value})};//等同于handleText: function(e) {...}

Object.assign

deep clone and shallow clone

JavaScript存储对象都是存地址的,所以浅复制会导致 obj 和 obj1 指向同一块内存地址,大概的示意图如下。而深复制一般都是开辟一块新的内存地址,将原对象的各个属性逐个复制出去。

let obj = {0: "a", 1: "b", 2: "c"}let deepObj = JSON.parse(JSON.stringify(obj))//is not a really deep clone, but it works.
let shallowObj = obj
obj[3] = 'd'console.log(deepObj) //{0: "a", 1: "b", 2: "c"}
console.log(shallowObj) //{0: "a", 1: "b", 2: "c", 3: "d"}
console.log(obj) //{0: "a", 1: "b", 2: "c", 3: "d"}

JSON.parse(JSON.stringify(obj))这样的使用方式并不是真正的深拷贝,因为它会丢失一些东西,一些obj的内在property之类的,
比如

    obj_test1 = {a: 1,b: 2,get_a() {return this.a}}

这样的一个obj,你用上面的方式会丢掉get_a,如果你定义一些prototype,也会丢失掉,或者如果包含一些dom元素之类的。但是我们多数时候只是用它来复制数据,不关心对象的方法之类的东西,这样的话是够用的。

常见作用

  • 对象拷贝(copy)

Object.assign方法实行的是浅拷贝,而不是深拷贝,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。之前自己的一个疑惑

let obj = {0: "a", 1: "b", 2: "c"}let objClone = Object.assign({}, obj)
obj[3] = 'd'
console.log(objClone) // {0: "a", 1: "b", 2: "c"} why the same, but not change

Javascript has five primitive data types:

  1. Number

  2. String

  3. Boolean

  4. Undefined

  5. Null

Anything that doesn’t belong to any of these five primitive types is considered an object.

primitive types are passed by value, while objects passed by reference.

a = {a: 1, b: 2, c: 3, d: {aa: 11, bb: 22}}
a1 = Object.assign({}, a, {a: 2})
a1.d.aa = 'i am shllow copy'
第二行是浅拷贝,a拷贝到a1了,并且把property a的值改成2了 (你觉得a.a会变成2么,为什么?)
第三行把a1.d这个object理得aa这个property改了 (你觉得a.d.aa也会改么,为什么?)

  • 合并对象 (merge)

Object.keys && Object.value

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

Class

不得不说,这是我从没仔细看过的一部分,写react的时候都是三板斧套路。。。
特意留意一下super()。ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。es6中先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

    class Teachers extends React.Component {constructor(props, context) {super(props, context)}componentDidMount(){const { siteId, dispatch } = this.propsif (siteId) {dispatch(fetchTeachersList(siteId))}}componentWillReceiveProps(nextProps) {const { siteId, dispatch } = this.propsif (nextProps.siteId !== siteId) {dispatch(fetchTeachersList(nextProps.siteId))}}}export default Teachers

搭配Object.assign()

    class Point {constructor(){// ...}}Object.assign(Point.prototype, {toString(){},toValue(){}});

不存在变量提升

    new Foo(); // ReferenceErrorclass Foo {}

class表达式

Class表达式,可以写出立即执行的Class。

    let person = new class {constructor(name) {this.name = name;}sayName() {console.log(this.name);}}('张三');person.sayName(); // "张三"

this指向和箭头函数

箭头函数很方便,解决了之前es5遗留下来的问题(继承,this作用域)。箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域。

    //es5中, var self = this;这样的代码你肯定常见,es6箭头函数中不用鸟var obj = {field: 'hello',getField: () => {console.log(this.field)}}obj.getField() // undefined,这时this是window,var obj = {field: 'hello',getField(){console.log(this.field)}}obj.getField() // hello,

自己常用的拓展

    let arr = [1,2,3,4,5,6]let arr2 = arr.reduce( (init, prev, index) => {init.push(prev*prev); return init}, [] )console.log(arr2) //[1,4,9,16,25,36]

想起之前写的数组转对象的方法

警告 yield 关键字通常不能在箭头函数中使用(except when permitted within functions further nested within it)。因此,箭头函数不能用作Generator函数。

Module

js历史上没有标准的模块化。在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。但是这些都只能在运行时起作用,es6的模块化可以在编译时就能确定模块的依赖关系,以及输入和输出的变量。

模块功能主要由两个命令构成:export和import。先抛出一个问题:以下两行代码有什么区别?

import {message, menu} from 'antd'
import message from 'antd/lib/message'

export

对外的接口名与模块内部变量的关系

export对外的接口名与模块内部变量之间,建立了一一对应的关系。当export一个function时,语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

export 1; //error
export var m = 1 //rightvar m = 1;
export m//error
export {m} //right

import

注意 import命令具有提升效果,会提升到整个模块的头部,首先执行。所以在文件中的任意位置,exort也一样,不过利于可读性,还是import写在文件开头,export写在文件结尾好点。

注意 import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

import {message, menu} from 'antd'
import message from 'antd/lib/message'

所以这之间的区别就是前者中括号里的变量名要与export出的接口名一致,后者message是随意起的名字,可以不与export出来的变量名保持一致,它代表export default出来的接口。export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export deault命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能对应一个方法。本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。

模块的整体加载

    import * as circle from './circle';console.log('圆面积:' + circle.area(4));console.log('圆周长:' + circle.circumference(14));

es6 初步深入学习相关推荐

  1. ES6与JavaScript学习总结

    菜鸟的ES6与JavaScript学习总结 说明 一.概述 (1)什么是javascript? (2)什么是ES6 (3)ES6和JavaScript的关系 (4)ES6的浏览器兼容情况 二.语法 ( ...

  2. SimMechanics/Second Generation倒立摆模型建立及初步仿真学习

    笔者最近捣鼓Simulink,发现MATLAB的仿真模块真的十分强大,以前只是在命令窗口敲点代码,直到不小心敲入simulink,就一发不可收拾.话说simulink的模块化建模确实方便,只要拖拽框框 ...

  3. C语言初步入门学习大略

    C语言的入门学 C语言初步入门 2022/11/29 01. 看前需知 02. 内容构成 03. 学习时间 04. 预定计划 05. 网站推荐 2022/12/01 01. 驱动程序是什么?和其他电脑 ...

  4. tty驱动初步了解学习

    一.linux tty驱动框架 本人是linux驱动初学者,最近在初步学习uart驱动,在这记录下来自己的理解 linux3.10 soc:君正x1000e 四位大佬写的很好 https://blog ...

  5. HTML5初步标签学习1——H标签,P标签,Hr标签,img标签,br标签,a标签,base标签

    首先关于html5的学习,采用的是webstorm软件,所有的h5标签不是用来改变文本样式的,而是用来添加语义的,改变样式是由css来实现的. 标签学习: 1. H标签:给文本添加标题语义,并非修改文 ...

  6. VC++ 下使用QT初步入门学习

    初步了解,VC++下使用QT,一种方式是,用QT的工具或命令行,生成VS的工程,然后导入VC进行构建:下面来看一下,下载一个QT免费试用版本: 安装之后,先设置一下环境变量:test3目录下放一个示例 ...

  7. ES6语法的学习与实践

    ES6是JavaScript语言的新一代标准,是ECMAScript的第六个版本,加入了很多新的功能和语法,在很多框架,如在使用Vue,React等框架的项目中一般都采用ES6语法来编写的,下面对经常 ...

  8. Python爬虫初步个人学习及心得

    自从毕设开始搞了Python之后就觉得这个东西值得研究. 但是毕设的东西非常的浅显,个人觉得最值得训练的还是Python的爬虫,谨以此开篇,作为学习和练习Python的起步. ----------学习 ...

  9. ES6新特性学习笔记

    ES6, 全称 ECMAScript 6.0,弥补了ES5的不足,新增了大量的语法糖,可以使代码编写更简洁. 变量 var ES6之前声明变量用var,如下: var name = 'admin'; ...

最新文章

  1. python读取word
  2. FPGA/CPLD按键硬件延时消除抖动的思路
  3. android 牛人必修 ant 编译android工程
  4. Mybatis 强大的结果映射器ResultMap
  5. matlab点云处理函数
  6. TCP/IP协议栈在MSP430单片机上的实现
  7. MySQL 一般操作语句
  8. css深入浅出 宽度和高度
  9. 分页与无限滚动在评论区的设计差异
  10. Java虚拟机运行流程
  11. selection at the technology management
  12. 外企面试官最爱提的10个问题
  13. mybatis学习(45):开启二级缓存
  14. 话里话外:流程管理评价的两大要素
  15. 农村树上的野菜“刺老芽”,价格比肉还贵,这是为啥呢?
  16. 测试对比度的软件,WCAG颜色对比度检测工具,网页及App文字背景配色检测软件...
  17. 日系插画学习笔记(三):光影与结构
  18. 计算机无法读取配置文件,由于权限不足,无法读取配置文件
  19. Rocksdb prefix Bloomfilter
  20. 字符串和时间转换sql

热门文章

  1. nothing about web app azure
  2. WinCE5.0中文模拟器SDK(VS2005,VS2008)的配置
  3. deepin启动盘无法引导安装_深度启动盘制作工具(Deepin Boot Maker)怎么安装kubuntu?Deepin Boot Maker图文教程...
  4. 单例模式双重校验锁_被面试官虐过之后,他轻蔑的问我:你还说你了解单例模式吗?...
  5. 【数字信号处理】相关系数 ( 相关系数概念解析 | 信号能量常数 | 共轭序列 | 序列在相同时刻的相关性 )
  6. 【C 语言】指针数据类型 ( 野指针 | 避免野指针推荐方案 )
  7. 【Android 安全】DEX 加密 ( 常用 Android 反编译工具 | apktool | dex2jar | enjarify | jd-gui | jadx )
  8. VB 宏+mysql解决EXCEL表格实现自动化处理
  9. 2016年第七届蓝桥杯javaB组 试题 答案 解析
  10. 安振平老师的4911号不等式问题的证明