1.Class的基本语法
<1>定义相关:
1.可以在看作是语法糖:类的数据类型就是函数,类本身就指向构造函数,使用的时候,同构造函数,使用new命令。
2.类的方法,除了constructor(这个=原型的constructor,指向其本身),其他都定义在prototype对象上,所以新方法可以通过Object.assigh(对象原型,方法)。
3.类的内部定义的所有方法都是不可枚举,如果采用ES5构造函数的写法,就变成可以枚举的了。
<2>类和模块内部默认使用严格模式
<3>constructor方法:类的默认方法,通过new命令生成对象实例的时候自动调用,每个类必须要有,如果没有显示定义,一个空的constructor方法会被默认添加。
<4>类的实例对象:类必须要new调用,实例的属性除非显示定义在其本身,即this对象上,否则都是定义在原型上。
所有实例共享一个原型对象,所以可以通过实力的__proto__属性为类添加方法
<5>Class表达式:const Myclass=class Me{},这里的类的名字是Myclass,Me只能在Class内部代码可用,指代当前类。如果内部没用到,可以省略。采用表达式,可以写立即执行的Class,eg:let person=new class{}(参数)
<6>类不存在变量提升
<7>私有方法:ES6本身不提供,可以在方法前加下划线代表这是和内部使用的私有方法,但其实外部依旧可以使用。
可以将私有方法移除,内部调用该方法call。或者利用Symbol的唯一性将私有方法的名字命名为一个Symbol值.
有一个提案,在私有属性前面加#代表,私有和公有可以同名
<8>this:Class内的this默认指向类的实例,但是如果将Class内的方法单独取出来使用,this将会指向运行时 所在的环境。要想绑定this,可以bind,箭头函数,Proxy
<9>Class的getter和setter函数:get/set 属性名(){},该函数能拦截该属性的存取行为
<10>静态方法:类中定义的方法都会被实例继承,但是如果在一个方法前加static,表示该方法不会被实例继承,而是直接通过类调用,称为静态方法。
相关:
1.父类的静态方法可被子类继承
2.静态属性指的是Class本身的属性,只可以是Class.propname来定义更改,不可以是在Class内同方法一样定义。因为ES6明确定义Class内只有静态方法,没有静态属性。
<11>实例属性:Class的实例属性可以用等式写入类的定义中,以前是写在constructor里面。如果该属性在constructor里已定义,允许在Class内直接列出(没有任何赋值定义的操作);
新写法中可以在实例属性前面加static就可以是静态属性
<12>new.target:通过new调用的Class,在Class内部调用new.target会返回当前Class,子类继承父类时new.target返回子类
2.Class的继承
<1>概念:通过extends实现继承,子类中的super代表父类的构造函数(可以看作父类的this),因为子类没有自己的this对象,而是继承父类的,如果不调用super方法,就得不到父类的this对象。
<2>Object.getPrototyoeOf(子类)可以获取他的父类
<3>super关键字:
1.作为函数使用:代表父类的构造函数,ES6要求,子类的构造函数必须执行一次super函数。但是super被的this指的是子类。只能用在子类的构造函数中。
2.作为对象使用:在普通方法中使用指向父类的原型对象(定义在父类实例上的方法或者实例是无法通过super调用),在静态方法中指向父类。通过super调用父类的方法时,super绑定子类的this
<4>类的 prototype 属性和__proto__属性
1.子类的__proto__属性表示构造函数的继承,总是指向父类。
2.子类 prototype 属性的__proto__属性表示方法的继承,总是指向父类的
prototype 属性
<5>extends的继承目标:extends关键字后面可以跟很多种类型的值,如果是class继承的话,父类可以是任意函数
<6>ES6可以定义子类继承原生的构造函数。
3.修饰器
<1>类的修饰:修饰器对类的行为改变发生在编译时
@修饰器名字(参数)------默认为目标类,如果在修饰器外封装有函数,可以按需传参
class 类的名字
<2>方法的修饰:修饰类里的方法,写法同上,可传参为三个,目标类,修饰的属性名(方法名),该属性的描述对象
如果一个方法有多个修饰器,那么会优先按序执行所有修饰器的外层,然后再反序执行所有的内层
<3>修饰器只能用于修饰了类和类的方法,不能用于函数,因为函数会提升,而类不会
<4>core-decorators.js:第三方模块,提供了几种常见修饰器
1.@autobind:使得方法中的 this 对象绑定原始对象
2.@readonly:使得属性和方法不可写
3.@override:检查子类的方法是否正确覆盖了父类的同名方法,如果不正确(子类方法写的不对或者找不到父类相应的方法)会报错。
4.@deprecate(别名@deprcated):在控制台显示一条警告,表示该方法将废除。
5.@suppressWarnings:器抑制 decorated 修饰器导致的 console. warn()调用,但
异步代码发出的调用除外
<5>使用修饰器实现自动发布:发布订阅库:Postal.js
<6>修饰器可以实现Mixin模式,用Object.assign(类.prototype,一系列方法)。
不过这样会改写prototype对象
<7>Trait:一种修饰器,类似Minxin,但是提供了更多功能,比如同名方法的冲突等等,以第三方模块traits-decorator为例
@traits(参数为要混入的类或者对象),如果混入的具有同名方法,会报错,需要:
1.可能存在同名方法,将其排除掉写为:类名::excludes(同名方法的名字)
2.将其改名写为:类名::alias(同名方法的名字:新名字)
3.Module的语法
<1>export:输出函数、变量、类,写法:
(1)export var 变量名=xxx; 多个就写多个export
(2)expory {变量名} 多个{}里逗号隔开
此外可以用as重命名(对外的接口),接口取到模块内实时的值,而CommonJS模块输出的是值的缓存
export只要处在模块顶层就可以,位于块级作用域内会报错,import也是。
<2>import:加载,写法:import {变量或类或对象} from '地址或模块名(如果直接模块名,则配置文件内要说明位置)'
import必须接受一个对象({}),在里面指定导入的变量名,如果要重命名,也是用as
import具有提升效果,会提升到整个模块的头部并首先执行。
<3>模块的整体加载:import * as xx from ww 可以用*表示整体加载,即所有的输出值都在*,这里as成xx
<4>export default命令:export default fn,这样用户在import的时候就可以为export的东西指定任意名字,但import的时候后面只能跟名字,不能再跟{}。一个模块只能有一个默认输出(只能对应一个方法)
<5>export和import的复合写法
<6>模块继承
<7>跨模块常量:这些常量写在一个专门的constants目录里,然后合并再index.js里
<8>import():提案:参数为要加在模块的位置,实现动态加载,类似Node的require,但是是异步加载
4.Module的加载实现
<1>传统方法:异步加载脚本script标签加defer或async。前者要等整个页面加载完才会执行,后者一旦下载完渲染引擎会中断,执行完这个脚本再渲染。多个的话,defer按序,async无序
<2>加载规则:浏览器加载ES6模块也用script标签,type=“module”,其功能等同defer
外部需要注意:
1.代码在模块作用域之中运行,不是在全局,模块内的顶层变量是外部不可见的
2.模块脚本采用严格模式
3.模块中可以使用import和export
4.模块中顶层的this返回undefined,而不是指向window,即是在顶层使用this无意义
5.同一个模块如果加载多次,只执行一次
<3>ES6模块与CommonJS模块的差异
1.CommonJS模块输出的是一个值的复制,ES6模块输出的是值的引用
2.CommonJS模块是运行时加载,ES6模块是编译时输出接口
1的详解:CommonJS一旦输出一个值,模块内的变化将无法影响到该值(eg:模块内的改写该值函数输出,执行后也不会改写该值),要想能改写,除非把该值写成一个函数
<4>Node加载
概述:node有自己的CommonJS模块格式,与ES6不兼容,处理ES6比较麻烦,所以现在还是分开用各自的加载方案。在静态分析阶段只要有import或export语句,node就认为是ES6,否则是CommonJS
1.import加载CommonJS模块:Node的CommonJS模块输出都定义在module.exports属性上,所以Node环境中,用import加载CommonJS模块,Node会自动将module.exports属性当作export default。如果采用整体输入的写法(import * as xxx),那么用xxx.default来获取module.exports
2.require加载ES6模块:所有输出的接口会变成输入对象的属性,而且由于缓存机制,export后的重新赋值不会在模块外反映出来。
<5>循环加载
<6>ES6转码:ES6 module transpiler 和 SystemJS
5.编程风格
<1>let完全替代var且没有副作用,能用const尽量用,尤其在全局环境
<2>静态字符串一律用单引号或反引号,不用双引号。动态用反引号(const b = `foo${a}bar` //a为变量)
<3>使用数组成员对变量赋值时,优先使用解构赋值const [first , second]= arr;
函数参数如果是对象的成员时,优先使用解构赋值
函数如果返回多个值,优先用对象的解构赋值而不是数组,便于以后添加以及更改顺序
<4>对象:单行定义的对象,最后一个不以逗号结尾,多行的以逗号结尾;
对象尽量静态化,一旦定义不要随便添加新属性,如果实在要加,要使用Object.assign
动态属性名,在创建对象的时候使用属性表达式定义。
<5>数组:使用...数组,来复制数组,使用Array.from方法将类似数组的对象转为数组。
<6>函数:立即执行的函数可以写成箭头函数的形式,尽量用箭头函数
简单的、单行的、不会复用的函数用箭头;行数较多,比较复杂的还是用传统
所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。
eg:function divide(a , b , { option= false} = {}) { }
函数体内不用argument变量,用...代替,前者是类数组对象,后者是真正的数组
<7>Map结构:注意区分Object和Map,只有模拟实体对象时才使用 Object
<8>Class:用Class取代prototype操作,使用extends实现继承
<9>模块:使用import取代require,使用export取代module.exports(不要同时使用export和export default)
如果模块默认输出一个函数,函数名首字母应该小写,如果默认输出一个对象,对象首字母应该大写
<10>ESLint使用:语法规则和代码风格检查工具
6.读懂ECMAScript规格
需要再回顾
7.ArrayBuffer
<1>二进制数组:和操作系统的原生接口进行二进制通信,该数组由ArrayBuffer对象(代表储存二进制数据的一段内存
),TypedArray 视图(读写简单类型的二进制数据),DataView 视图(读写类型的二进制数据)
<2>ArrayBuffer对象:也是一个构造函数,可分配一段可以存放数据的连续内存区域
var buf=new ArrayBuffer(32); //生成一段32字节的内存区域,字节默认都是0
var dataView=new DataView(buf); //创建DataView视图,需提供ArrayBuffer对象实例作为参数
dataView.getUint8(0)//以不带符号的8位整数格式读取第一个元素
// TypedArray视图不是一个构造函数,而是一组,所以它的用法,如下,此外它还可以接受普通数组作为参数
var xl =new Int32Array (buf); //此例是创建32位带符号整数,如果有创建多个视图,一个视图修改底层内存会影响另一个
xl[0] = 1 ;
<3>ArrayBuffer的函数:
ArrayBuffer.prototype.byteLength //返回所分配内存区域的字节长度
ArrayBuffer.prototype.slice() //允许内存区域的一部分复制生成一个新的ArrayBuffer对象,参数1,2代表开始(含开始)和结束(不含结束)的位置,无第2参数则到对象结尾
ArrayBuffer.isView //返回Boolean,表示参数是否为ArrayBuffer的视图实例
<4>TypedArray视图:9种,都是构造函数(参数1为ArrayBuffer对象,2为开始处,3为结束处),像普通数组,区别:成员都是同一类型,成员连续不会有空位,成员默认值为0。
TypedArray的构造函数可以不通过ArrayBuffer,而是直接分配内存生成
TypedArray的构造函数可以接受另一个的TypedArray的实例作为参数。新数组只是复制了参数数组的值,对应的底层内存不一样,新数组会开辟一段新内存储存数据。
<5>数组方法:普通数组的操作方法合属性对TypedArray数组完全适用,但没有concat方法
<6>字节序:需要结合实际再了解,另外BYTES_PER_ELEMENT属性表示数据类型占据的字节数
<7>ArrayBuffer与字符串的互相转换:前提是字符串的编码方法是确定的。
<8>不同的视图类型能容纳的数值范围是确定的,如果超出,就会出现溢出,TypedArray的处理就是抛出溢出的位,其他的有不同
<9>TypedArray.prototype的属性和方法:
buffer:返回整段内存区域对应的ArrayBuffer对象
bytelength:占据的内存长度,单位为字节
byteOffset:返回TypedArray数组从ArrayBuffer对象的哪个字节开始
length:有多少个成员
set():复制数组(内存的复制),参数1为复制的目标,2为从哪一个成员开始复制
subarray():对TypedArray数组的一部分再建立一个新的视图,参数1为开始处,2为结束处
slice():返回一个指定位置的新的TypedArray实例,参数代表位置,可为负数,即逆向计数
<10>TypedArray数组的所有构造函数都有of和from方法,前者将参数转为一个TypedArray实例,后者接受一个可遍历的数据结构作为参数,返回一个基于此结构的TypedArray实例,接受函数作为第二个参数,功能类似map。
<11>dataView视图:用域处理网络设备传来的数据,可以自行设定大端字节或小端字节,存取各有8个方法。取的参数代表读取位置,存的参数代表存的位置,数据,大小端设定(true为小段,false和undefined为大端)