变量命令
var和let
- let只在其块级作用域内有效,而var却全局有效。
- 区块中有let和const,则这个区块暂时性死区;即区块会对这些命令声明的变量一开始就形成封闭作用域,只要在声明前使用这些变量,就会报错(相反var不会)
const命令只保证变量指向地址不变,不能保证数据不变。
|
|
字符
字符遍历。(好像用[]
也可以遍历)
es5: for (var i=0; i
es6: for (let i=0 of str) {alert(i);}
字符函数
es6多了:
includes:返回布尔,表示十分找到参数字符串。第二个参数,表示开始搜索的位置
startsWith:返回布尔,表示参数字符串是否在源字符串的头部。第二个参数,表示开始搜索的位置
endsWith:返回布尔,表示参数字符串是否在源字符串的尾部。第二个参数,表示开始搜索的位置
repeat:返回一个新字符串,表示将原字符串重复n次
模板字符串,使用反引号(`这个符号)
普通字符: dd'\n'asdf
这里有换行
多行字符: 无需连接符,所有的空格、回车和缩进都会被保留在输出中。
变量嵌入:hello ${name} are ${time}?
name和time是定义的变量。如果括号里面的不是字符串,则按一般规则转换为字符,如调用toString方法
函数调用:function tag(s, v, d); taghello ${a+b} ${a}
;
String的raw函数
正则
es5中String对象的方法,search、match、replace、split支持JS正则
类型扩展
数值
Number增加成员isFinite,isNaN.
es6把es5中的全局函数parseInt,parseFloat移植到了Number对象上。
Number成员,isSafeInteger范围在2的53次方
isFinite()
和isNaN()
在es5是全局,es6在Math
上又加了相同的函数;
区别在于:全局的方法先调用Number把非数值转换成数值,再进行判断。
而新方法只对数值有效,非数值一律false
Math对象,es6新增17个方法。它的方法都是静态方法:Math.abs(s)
Math.trunc(4.2)//除去一个数的小数部分,返回整数
Math.sign();//判断一个数是正数(re:+1)、负数(re:-1)、还是零(re:0)、其它(re:NaN)
Math.cbrt();//计算一个数的立方根
对数、指数、平方等就不写了。
Array数组
from函数;伪组数转换:
let arraylike = {
0: 'a', 1: 'b', length: 2//这个属性必须有
};//注意这是个对象,并不是数组
es5中转换数组:[].slice.call(arraylike);或这么写Array.prototype.slice.call(arraylike);//[‘a’, ‘b’];
es6中转换数组:Array.from(arraylike);
//只要部署了Iterator接口的数据结构,和ES6的Set、Map。都可以转换。
//如果参数是数组,则返回新数组
//from还可以接受第二个参数,用来对每个元素进行处理,将处理后的值放入返回的数组中
function test(a,b,c,d)
{
var arg = Array.prototype.slice.call(arguments,1);
alert(arg);
}
test("a","b","c","d"); //b,c,d
Array.of
Array.of(2, 3, 4)//[2, 3, 4]
find返回的是第一个符合条件的元素: [1, 4, -5, 10].find((n) => n < 0)//-5 [1, 5].find(function(value, index, arr) { return value > 9;})//10
fill填充数组
entries()//返回键值对遍历器,keys()//返回值遍历器,values()//返回键遍历器
for (let index of ['a', 'b'].keys()) {}
for (let [index, elem] of ['a', 'b'].entries()) {}
//es6有遍历器对象
includes()//返回一个布尔值,表示某个数组是否包含给定值。 [1, 2, 3].includes(2);//true
函数(函数参数默认压栈顺序,从左到右)
函数参数的默认值
es5
function log (x, y) { y = y || 'world';}
es6
function log(x, y = 'world') {}
解构和默认参数
function foo({x, y = 5}) { console.log(x, y);} foo({})//undefined, 5 foo({x:1})//1, 5 foo({x:1, y:2})//1, 2 function m1({x=0, y=0} = {}) {} function m2({x, y} = {x:0, y:0}) {} //上面俩个函数,在m({x:3})、m({})这种类型时,就不同 //它们顺序是先赋值参数,再执行构里面的默认值 //默认参数位置不用在尾部。但是调用要用undefined参数,如: f(undefined, 1) //使用默认参数会相应减少length属性的值。(function(a, b, c = 5){}).length //2
rest参数和扩展运算符
|
|
扩展运算符(…)
- 当作参数声明时,它如同c的自定义长度参数,允许多个参数传入,而它就是个数组
- 当作为参数使用,定义时,它就是个数组的解构符。
扩展运算符也是加三个点(…),好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列
|
|
function有name属性(浏览器支持,es6才标准)
箭头函数
箭头函数内部没有自己的this,导致内部的this就是外层代码块的this。
除此外,arguments、super、new.target也是指向外层函数对应的。
尾调用优化(严格模式生效)
js的函数调用会形成”调用帧”,以记录每个调用函数的信息和内部变量。
如上,这种当执行到return,就会销毁f的调用帧,只使用g的调用帧。这种”尾调用”大大的节省了内存
注意:内层函数用到外层函数内部变量,还是无法尾调用(如: g函数内部使用f的m变量)
尾调用的尾递归(严格模式生效)
递归耗内存是因为保存太多的调用帧,尾调用就只有一个调用帧,所以永远不会发生”栈溢出”错误
对象扩展
属性简洁写法
|
|
属性(属性多了 set get描述符,可以像c#那样使用)
|
|
属性名表达式(使用[]来实现)
|
|
name的俩种特殊情况:
|
|
Object.is()
除了个别(NaN、+0、-0)和===
不一样,其它都一致。
Object。assign()
它会从左向右,依次把元素叠加到第一个参数上,并返回第一个参数(注意assign只是一级属性复制,比浅拷贝多深拷贝了一层而已)
|
|
属性的可枚举
es5有三个操作会忽略enumerable
为false
的属性。
for...in
循环:只遍历对象自身的和继承的可枚举的属性Object.keys()
:返回对象自身的所有可枚举的属性的键名JSON.stringify()
:只串行化对象自身的可枚举的属性
es6多个assign
proto
书上建议使用Object.setPrototypeOf()
(写操作)、Object.getPrototypeOf()
(读操作)、Object.create()
(生成操作)代替 __proto__
的直接操作,因为只有浏览器广泛支持,其它环境不一定。
|
|
Object.keys 和values相对
|
|
Object.values 和keys相对
|
|
Object.entries
|
|
Symbol
从字面上理解,是为了属性引入新方法,同时避免重名的情况;保证属性名独一无二。
|
|
Symbol属性函数
Object.getOwnPropertySymbols
方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol
值。
Symbol.for()
接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值
Symbol.keyFor()
返回一个已登记的Symbol
类型的key;
换句话说,它对Symbol.for
返回成功的是能返回的。
对新建的Symbol(<string>)
类型是无法返回的。
Proxy(属于元编程)
|
|
总的来说有点像c++的操作符函数,但比操作符函数高级
支持一下特性:
get(target, propKey, receiver)
拦截对象属性读取set(target, propKey, value, receiver)
拦截对象属性的设置 返回一个布尔值。has(target, propKey)
拦截propKey in proxy的操作,以及对象的hasOwnProperty方法 返回一个布尔值。deleteProperty(target, propKey)
拦截delete proxy[propKey]的操作 返回一个布尔值。ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。preventExtensions(target)
拦截Object.preventExtensions(proxy),返回一个布尔值。getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy),返回一个对象。isExtensible(target)
拦截Object.isExtensible(proxy),返回一个布尔值。setPrototypeOf(target, proto)
拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。apply(target, object, args)
拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。construct(target, args)
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。
这个确实太多了,也没咋记,具体看书吧!
二进制数组
网络传输和图片视频文件解析等var type = new ArrayBuffer(32);
开辟二进制内存
访问二进制内存,不同数据有多种读取方式,叫”视图”
有俩种视图:
- TypeArray视图, 整体是同一数据类型
Int8Array Uint8Array....16 32
Dataview视图
TypeArray视图
构造函数参数有3个:
第一个必需,接收ArrayBuffer
第二个可选,视图开始的字节序号,必需保证字节合理性不然异常
第三个可选,视图包含的数据个数,必需保证字节合理性不然异常成员
BYTES_PER_ELEMENT
表示类型所占字节数
成员byteLength
获取占内存长度
成员length
获取数组含多少个成员12var b = new ArrayBuffer(8);var v1 = new Int32Array(b);DataView视图
get方式一轮次获
set方式设置
俩函数都有字节序的设置
Set/Map
和stl不同,他们并不会排序
map的键支持对象
和Array连用
Iterator接口
主要用于es6的for...of
语句
此语句的遍历依靠next()
返回一个对象{value:…, done:…} false表示遍历结束
es6中只要部署了Symbol.iterator
接口,就可以遍历(数组默认部署)
当对象也要使用for of语句遍历时,加个接口就好(接口是个函数,返回带有next的对象)
还可以用于解构
由于...
运算符也是用Iterator
接口,所以
字符串也可以用next遍历
js原有的for…in只能读取属性值,后面的for…of才能读取键值如同forEach
Generator函数
|
|
yield不能用于一般函数即使临时函数也不行
next方法参数
next
可以带一个参数,该参数会被作为上一条yield语句的返回值
配合Iterator接口
以前要封装接口,必须让函数返回带next函数标准返回对象的对象
现在如下便可
Generator.prototype.return()强制返回,不继续执行
yield*的使用
Generator内部不能调用Generator函数
Genrator中的return
Generator中的this
和普通函数的this差不多,但是不能和new使用,当构造函数使用得变通
Generator使用Trick
以前切换状态必须要个高层变量来保存,切换时就false,true的换。
现在可以直接:
Promise
状态对象,用来传递异步消息
Promise.then
由于then
返回的是一个promise
对象,所以可以连续then
Promise.catch和普通一样
Promise.all
all
参数是个Promise数组,返回一个Promise
只有当数组里面的Promise全改变状态,才会改变此Promise状态
Promise.race同上调用,数组中一个改变,就跟着改变
Class
类的方法之间不需要逗号分隔,加了会报错typeof ClassName
是function
即类数据类型是函数
构造函数也只是prototype.constructor
的子函数
类内部所有定义的属性都是不可枚举的
let d = new class {...}()
这样写法是允许的
继承
子类构造函数必须调用super
子类没有自己的this对象,是继承父类的this,所以必须supper构造父类
因此super
之前的this调用都是错误的
给类添加私有方法Trick
通过symbol导致第三方获取不到函数名,所以称为私有方法
set和get属性拦截
class和generator
|
|
静态函数
这个和es5直接定义函数的属性相似,区别在于父类的静态方法可以被子类继承es6规定static只能静态方法,不能静态变量属性
new.target
这个属性专门用在构造函数中(es5的函数构造,es6的constructor函数里面使用)
它用来判断new
的对象名(这样就算是继承也能知道new的对象是哪个)
Class的继承
子类允许使用super
关键字来访问父类函数
尽量不用super访问变量,我不知道为何会访问到prototype上去,赋值就可以,读却不行
Module
模块加载
CommonJS加载方式let {stat, exists} = require('fs');
es6方式:import {stat, exists} from 'fs'
前者整体加载fs模块(加载所有的fs方法),然后在使用时用到3个方法。即”运行时加载”
后者通过命令显式指定输出代码,输入时也用静态命令方式。实际只加载了fs的3个方法,其它未加载。即”编译时加载”
后者效率高
导入导出命令
export
和import
对接的接口名必须相同
如果不同,可以用此来import {last as surn} from './profile'
上面是逐一加载方式,接下来是整体加载方式
import * as pro from './profile'
pro.last
默认导出
由于上面的介绍必须知道导入和导出的接口名才能继续。所以有了default
|
|
导出继承
export * from 'circle'
从circle导出所有
加载
浏览器加载
|
|
这俩个都是异步加载defer
与async
的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer
是“渲染完再执行”,async
是“下载完就执行”。另外,如果有多个defer
脚本,会按照它们在页面出现的顺序加载,而多个async
脚本是不能保证加载顺序的。
es6模块和CommonJS模块差异
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
值拷贝:
Node加载
node有自己的commonjs模块格式,所以建议node和es6的加载方案各自分开
node使用es6加载时会依次寻找脚本
循环加载
a脚本依赖b脚本,b脚本依赖c脚本,c脚本依赖a脚本;会造成递归加载使得程序无法进行
CommonJS的循环加载时,返回的是当前已经执行的部分的值,而不是代码全部执行后的值,两者可能会有差异。所以,输入变量的时候,必须非常小心。
ES6的循环加载时,返回的是引用,所以只有执行到了才会有数据。
代码风格
let
const
优于let
:提醒阅读/符合函数式编程思想/js编译器会进行优化
字符串
静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号
优先使用解构赋值
数组解构
函数参数是对象,优先使用解构(注意当对参数无修改才如此使用,因为解构是赋值过程)
对象定义不得随便增加新属性,如果要增加属性,要使用Object.assign
扩展运算符拷贝数组
const itemscopy = […items];
使用箭头函数取代bind,放弃self
由于箭头函数的this
指向定义处,所以放弃以前的bind
和self
写法