ES6学习笔记

2016-09-25

  • let相当于var,块级作用域,不存在变量提升,暂时性死区,不允许重复声明,块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
  • const声明一个只读的常量。一旦声明,常量的值就不能改变。const一旦声明变量,就必须立即初始化,不能留到以后赋值。作用域与let相同。对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,也就是对象本身可变,如键和值。所以将一个对象声明为常量必须非常小心。如果真的想将对象冻结,应该使用Object.freeze方法。
  • yield和function*一起使用。在构造器函数中,yield可以暂停然后返回当前表达式的值。
  • 为了保持兼容性,var命令和function命令声明的全局变量,依旧是全局对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。
  • 解构赋值.两边形式一样且可遍历,如数组。对象的结构赋值是键值对应。解构赋值的应用:(1)交换变量的值(2)从函数返回多个值(3)函数参数的定义(4)提取JSON数据(5)函数参数的默认值(6)遍历Map结构(7)输入模块的指定方法
  • 字符串字节查找什么的各种扩展。includes(str,index),startsWith(str,index),endsWith(str,index)。repeat(number)重复字符串。拼接字符串:…${js代码}…。所有模板字符串的空格和换行,都是被保留的,如果你不想要这个换行,可以使用.trim()方法消除它。标签模板:functionname...。“标签模板”的一个重要应用,就是过滤HTML字符串,防止用户输入恶意内容。标签模板难得一比!回头再看!目前理解是:第一个参数是原字符串被变量分割组成的数组,后面参数是变量。
  • Number。isFinite(),EPSILON,isSafeInteger()。Math:Math.trunc方法用于去除一个数的小数部分,返回整数部分。Math.sign方法用来判断一个数到底是正数、负数、还是零。Math.cbrt方法用于计算一个数的立方根。Math.hypot方法返回所有参数的平方和的平方根。Math.expm1(x)返回ex - 1,即Math.exp(x) - 1。Math.log1p(x)方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN。Math.log10(x)返回以10为底的x的对数。如果x小于0,则返回NaN。Math.log2(x)返回以2为底的x的对数。如果x小于0,则返回NaN。指数运算**,如:2**3=8。
  • Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。如果参数是一个真正的数组,Array.from会返回一个一模一样的新数组。扩展运算符(...)也可以将某些数据结构转为数组。如:var arr = [...arguments]。扩展运算符背后调用的是遍历器接口(Symbol.iterator)。任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。如:Array.from({length:3}) //[undefined, undefined,undefined]。对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代。Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。如果map函数里面用到了this关键字,还可以传入Array.from的第三个参数,用来绑定this。Array.from()的另一个应用是,将字符串转为数组,然后返回字符串的长度。因为它能正确处理各种Unicode字符,可以避免JavaScript将大于\uFFFF的Unicode字符,算作两个字符的bug。 Array.of方法用于将一组值,转换为数组。如:Array.of(3,5,9) //[3,5,9]。Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。只有当参数个数不少于2个时,Array()才会返回由参数组成的新数组。参数个数只有一个时,实际上是指定数组的长度。Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。 copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。 find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。 findIndex返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。 fill方法使用给定值,填充一个数组。fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。 keys()是对数组键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。 Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。该方法属于ES7,但Babel转码器已经支持。该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置。类似的,Map结构的has方法,是用来查找键名的,比如Map.prototype.has(key)、WeakMap.prototype.has(key)、Reflect.has(target, propertyKey)。Set结构的has方法,是用来查找值的,比如Set.prototype.has(value)、WeakSet.prototype.has(value)。
  • 函数的扩展:ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。 ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。函数的length属性,不包括rest参数。...扩展运算符,将一个数组转为用逗号分隔的参数序列:[1,2,3] => 1,2,3。由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。扩展运算符的应用:(1)合并数组(2)与解构赋值结合(3)函数的返回值(4)字符串:扩展运算符还可以将字符串转为真正的数组。(5)实现了Iterator接口的对象:任何Iterator接口的对象,都可以用扩展运算符转为真正的数组。(6)Map和Set结构,Generator函数:扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符,比如Map结构。如果对没有iterator接口的对象,使用扩展运算符,将会报错。***箭头函数:普通函数的this指向运行时所在的作用域,箭头函数的this绑定定义时所在的作用域(这他妈太难了,什么鬼!) function Timer() { this.s1 = 0; this.s2 = 0; // 箭头函数 setInterval(() => this.s1++, 1000); // 普通函数 setInterval(function () { this.s2++; }, 1000); }

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);// s1: 3 setTimeout(() => console.log('s2: ', timer.s2), 3100);// s2: 0 上面这段需要慢慢理解与询问。 this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。函数绑定(ES7):函数绑定运算符是并排的两个双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。尾调用:只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,节省了内存。尾递归:递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。ES6的尾调用优化只在严格模式下开启,正常模式是无效的。

  • 对象的扩展 属性与方法的简洁表示, ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。*Symbol类型的值是什么鬼。 Object.is(x,y)相当于 x===y,不同之处只有两个:一是+0不等于-0,二是NaN等于自身。 Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。Object.assign可以用来处理数组,但是会把数组视为对象。常见用途:(1)为对象添加属性(2)为对象添加方法(3)克隆对象(4)合并多个对象(5)为属性指定默认值属性的遍历:操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替。 ES6一共有5种方法可以遍历对象的属性。

(1)for...in

for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。

(2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。

以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。

  • 首先遍历所有属性名为数值的属性,按照数字排序。
  • 其次遍历所有属性名为字符串的属性,按照生成时间排序。
  • 最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
  • __proto__属性(前后各两个下划线),用来读取或设置当前对象的prototype对象。目前,所有 浏览器(包括IE11)都部署了这个属性。如:obj.proto = father 相当于 obj = Object.create(father)。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替。 Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象。它是ES6正式推荐的设置原型对象的方法。getPrototypeOf方法与setPrototypeOf方法配套,用于读取一个对象的prototype对象。还有很多ES7提案中的,没看。
  • Symbol:注意,Symbol值作为对象属性名时,不能用点运算符。var a = {[mySymbol]: xxx} //这里的中括号要加,不加是字符串的意思Symbol作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有Symbol属性名。Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的Symbol值。 Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和Symbol键名。由于以Symbol值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。 Symbol.for接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。Symbol.keyFor方法返回一个已登记的Symbol类型值的key。Symbol.for为Symbol值登记的名字,是全局环境的,可以在不同的iframe或service worker中取到同一个值。使用场景:Node的单例模式ES6还提供了11个内置的Symbol值,没看
  • Proxy和Reflect (1)Proxy代理: proxy用于拦截操作,如;将对象调用的方法存储到数组然后reduce,实现方法的链式调用。 set,get,apply等等 (2)Reflect: 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。
  • Set和Map数据结构 ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

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

WeakSet结构与Set类似,也是不重复的值的集合。但是,它与Set有两个区别。

首先,WeakSet的成员只能是对象,而不能是其他类型的值。

其次,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的。

ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。WeakMap结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。

  • IteratorIterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找Iterator接口。 在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。例如:let it = arrSymbol.iterator;***调用 Iterator的场合: 除了下面的for...of还有如下: (1)对数组和Set结构进行解构赋值时,会默认调用Symbol.iterator 方法。 (2)扩展运算符。 只要某个数据结构部署了Iterator接口,就可以对它使用扩展运算符,将其转为数组。

(3) yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。 (4) 由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。 **字符串是一个类似数组的对象,也原生具有Iterator接口。 for...in循环读取键名,for...of循环读取键值。for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样。Set结构遍历时,返回的是一个值,而Map结构遍历时,返回的是一个数组在对象上部署iterator接口的代码,一个方便的方法是将数组的Symbol.iterator属性,直接赋值给其他对象的Symbol.iterator属性。另一个方法是使用Generator函数将对象重新包装一下。

  • Generator函数yield句本身没有返回值,或者说总是返回undefined。 next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。 应用场景: 一步操作的同步化表达 控制流管理 部署Iterator接口 作为数据结构(数组)
  • Promise对象 遇到的问题: resolve(变成成功状态)//构造promise对象时带的两个回调函数 reject(变成失败状态) //构造promise对象时带的两个回调函数 return(传值给下个then)//不用上面两个因为then的参数只有一个value,上面return的 then//只有一个上面return的值作为参数,没有resolve和reject catch//捕获报错
  • Generator函数与Thunk配合用于流程控制,函数的执行权移出移入。nodejs有thunk的中间件。co模块用于Generator函数的自动执行的小工具。ES7提供了async函数,使得异步操作变得更加方便。async函数是什么?一句话,async函数就是Generator函数的语法糖。async函数就是将Generator函数的星号(*)替换成async,将yield替换成await,仅此而已。
  • ClassES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到 prototype是函数的一个属性(每个函数都有一个prototype属性),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。

proto 是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性。 用chrome和FF都可以访问到对象的__proto__属性,IE不可以。

new 的过程拆分成以下三步:

(1) var p={}; 也就是说,初始化一个对象 p (2) p.proto = Person.prototype; (3) Person.call(p); 也就是说构造p,也可以称之为初始化p (1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

class A { } class B extends A { } B.proto === A // true B.prototype.proto === A.prototype // true

  • Decorator 修饰器(Decorator)是一个函数,用来修改类的行为。这是ES7的一个提案 ,目前Babel转码器已经支持。修饰器不支持函数(声明提前),支持类和方法。 感觉修饰器就是一个函数,给传入的对象进行操作。@方法名 感觉就是语法上不一样而已。 @方法名 就相当于默认执行一个定义过的函数,给这个类/方法什么的做预处理。定义这个方法是默认会传入三个参数。
  • module 浏览器目前还不支持ES6模块,为了现在就能使用,可以将转为ES5的写法。Babel可以用来转码。 循环加载需要回头看看,看起来就是说ES6的比CommonJS好呗,CommonJS是拷贝值,遇到循环就输出没毛病的部分,会出现null,ES6是引用所以屌咯。
  • 定义类:constructor中的 this.xx是自身属性,外面的方法是原型链上的属性,怎么在原型链上添加变量属性并不会,写在prototype上吧
  • 静态方法前加static就好了,但ES6规定没有静态属性,除非 Me.xx这样直接写。
  • Class.prototype === obj.proto //true
  • 类总结: (类)静态方法与属性是类的,不是对象的。 extends继承的属性是所有属性,包括constructor中的和原型链上的 要想实现私有属性,得用闭包与get实现
  • Module CommonJS输出的是值拷贝,ES6输出的是值只读引用。 模块里面的值改变了,外面引用的值也会一起改变,且外面不能改这个值,因为在外面是只读的。module.exports与require(commonJS)是拷贝了一份不是引用,所以模块里的值变了,跟外面没关系 一个模块只能有一个export default,export default本质上是输出一个叫default的变量或方法,系统允许引入时取任意名字。 export default后面不要加声明语句(var,let),可以是变量名或者函数/对象 export * from ‘./x’会忽略x的default方法
exportexport var a = ‘xxx’;
var a = ‘xxx’; export {a}
export default var a = ‘xxx’
export {a} from./xx’
export * from./xx'
importimport {a} from./xx’
import * as obj from./xx’
import a from./xx'
  • 原型链就是通过将子类构造函数的原型作为父类构造函数的实例,这样就连通了子类-子类原型-父类,原型链的特点就是逐层查找,从子类开始一直往上直到所有对象的原型Object.prototype,找到属性方法之后就会停止查找,所以下层的属性方法会覆盖上层。原型中属性的改变会反应到所有的实例上