ES6入门 笔记

const命令:

    声明一个只读常量,一旦声明,声明的值就不能改变

    声明的常量和let一样,不可重复声明

本质:并不是变量的值不得改动,而是变量指向的那个内存地址不得改动

const只能保证这个指针固定,它指向的数据结构变不变是无法控制的

对象冻结:object.freeze方法

ES5只有两种声明变量的方法:var 和 function

ES6一共六种声明变量的方法:

let,const,import,class,var,function

var,function声明全局变量,依旧是顶层对象的属性

let,const,classes命令声明的全局变量不属于顶层对象的属性

var a = 1;

window.a;

是正确的

let b = 2

window.b

会报错

现在想要获取顶层变量一般都使用this去获取,但是是有局限性的

1.全局环境中this获取顶层变量,但是在node和es6的环境中,this只能返回当前模块

2.函数中的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象,但是在严格环境下会报未定义的错误

        ----------------这句不是很理解

3.new function('return this')总是返回全局对象,但浏览器用了CSP(Content Security Policy,内容安全策略),那么eval,new Function 这些方法都可能无法使用


解构赋值虽然方便,但是解析并不容易,es6规则:

只要有可能导致解构的歧义,就不能使用圆括号

解构的用途

1.交换变量

let x = 1;

let y = 2;

[x,y] = [y,x];

2.从函数返回多个值

函数只能返回多个值,要想返回很多值,只能将它们放在数组或者对象里返回

3.函数参数的定义

4.提取json数据

5.遍历map解构

const map = new Map();

map.set('first', 'hello');

map.set('second', 'world');

for (let [key, value] of map) {

console.log(key + " is " + value);

}

6.输入模块的指定方法

加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常 清晰。

const { SourceMapConsumer, SourceNode } = require("source-map");


1.es6加强了对Unicode的支持,拓展了字符串对象

codePointAt()

能够正确处理4个字节存储的字节,返回一个字节的码点

codePointAt 方法返回的是码点的十进制值,如果想要十六进制的 值,可以使用 toString 方法转换一下。

let s = 'a' ;

s.codePointAt(0).toString(16) // "20bb7" s.codePointAt(2).toString(16) // "61"

normalize用来将字符的不同表示方式统一为同样的样式

'\u01D1'.normalize() === '\u004F\u030C'.normalize() // true

es6提供了判断一个字符串中是否包含其他字符串的更多方法:

includes(),startsWith(),endWith()

repeat() 返回一个新字符

string.repeat(3);

padStart(),padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度, 会在头部或尾部补全。 padStart() 用于头部补全, padEnd() 用于尾 部补全。

'x'.padStart(5, 'ab') // 'ababx' 'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'


RegExp构造函数:

match(),replace().search(),split()

ES6 对正则表达式添加了 u 修饰符,含义为“Unicode模式”,用来正确 处理大于 \uFFFF 的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。

/^\uD83D/u.test('\uD83D\uDC2A') // false

 /^\uD83D/.test('\uD83D\uDC2A') // true

除了 u 修饰符,ES6 还为正则表达式添加了 y 修饰符,叫做“粘 连”(sticky)修饰符。 y 修饰符的作用与 g 修饰符类似,也是全局匹配,后一次匹配都从上 一次匹配成功的下一个位置开始。不同之处在于, g 修饰符只要剩余位 置中存在匹配就可,而 y 修饰符确保匹配必须从剩余的第一个位置开 始,这也就是“粘连”的涵义。

var s = 'aaa_aa_a'; var r1 = /a+/g; var r2 = /a+/y;

r1.exec(s) // ["aaa"] r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"] r2.exec(s) // null

lastIndex属性:

lastIndex 属性指定每次搜索的开始位置

const REGEX = /a/y;

REGEX.lastIndex = 2;

REGEX.exec('xaya')   // null

REGEX.lastIndex = 3;

const match = REGEX.exec('xaxa');

match.index;

REGEX.lastIndex

mathc用法:

单单一个 y 修饰符对 match 方法,只能返回第一个匹配,必须 与 g 修饰符联用,才能返回所有匹配。

'a1a2a3'.match(/a\d/y) // ["a1"]

'a1a2a3'.match(/a\d/gy) // ["a1", "a2", "a3"]

sticky属性:

与 y 修饰符相匹配,ES6 的正则对象多了 sticky 属性,表示是否设 置了 y 修饰符。

var r = /hello\d/y; 

r.sticky // true

新增flags属性,返回郑子表达式的修饰符

// ES6 的 flags 属性 

// 返回正则表达式的修饰符 /abc/ig.flags 

// 'gi'

正则表达式中,点( . )是一个特殊字符,代表任意的单个字符,但是 行终止符(line terminator character)除外。 以下四个字符属于”行终止符“。 

U+000A 换行符( \n ) 

U+000D 回车符( \r ) 

U+2028 行分隔符(line separator) 

U+2029 段分隔符(paragraph separator)

为了让.可以匹配任意单个字符

引入 /s 修饰 符,使得 . 可以匹配任意单个字符。

/foo.bar/s.test('foo\nbar') // true

这种模式成为dotAll模式,点代表一切字符

所以,正则表达式 还引入了一个 dotAll 属性,返回一个布尔值,表示该正则表达式是否 处在 dotAll 模式。

const re = /foo.bar/s;

re.dotAll

先行断言:

x只有在y前面才匹配     /x(?=y)/

只匹配百分号前的数字  /\d+(?=%)/

先行否定断言

x只有不在y前面才匹配   /x(?!y)/

只匹配不在百分号之前的数字 /\d+(?!%)/

后行断言

与先行断言正好相反

x只有在y后面才匹配      /(?<=y)x/

只匹配美元符号之后的数字   /(?<=\$)\d+/

后行否定断言:x只有不在y后面才匹配  /(?<!y)x/

只匹配不在美元符号后面的数字   /(?<!\$)\d+/

const RE_DOLLAR_PREFIX = /(?<=\$)foo/g; 

'$foo %foo foo'.replace(RE_DOLLAR_PREFIX, 'bar'); // '$bar %foo foo'

上面代码中,只有在美元符号后面的 foo 才会被替换

首先,”后行断言“的组匹配,与正常情况下结果是不一样的。

/(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"] 

/^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]

上面代码中,需要捕捉两个组匹配。没有”后行断言”时,第一个括号是 贪婪模式,第二个括号只能捕获一个字符,所以结果是 105 和 3 。 而”后行断言”时,由于执行顺序是从右到左,第二个括号是贪婪模式, 第一个括号只能捕获一个字符,所以结果是 1 和 053 。

其次,”后行断言”的反斜杠引用,也与通常的顺序相反,必须放在对应 的那个括号之前。

/(?<=(o)d\1)r/.exec('hodor') // null 

/(?<=\1d(o))r/.exec('hodor') // ["r", "o"]

上面代码中,如果后行断言的反斜杠引用( \1 )放在括号的后面,就 不会得到匹配结果,必须放在前面才可以。因为后行断言是先从左到右 扫描,发现匹配以后再回过头,从右到左完成反斜杠引用。

Unicode属性类

\p{...} 和 \P{...}

允许正则表达式匹配符合Unicode某种属性的所有字符

具名组匹配

正则表达式使用圆括号进行组匹配

允许每个匹配组都有一个名字,方便之后的查看和引用

如果要在正则表达式内部引用某个“具名组匹配”,可以使用 \k<组名 > 的写法。

const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/; 

RE_TWICE.test('abc!abc') // true 

RE_TWICE.test('abc!ab') // false

数字引用( \1 )依然有效

const RE_TWICE = /^(?<word>[a-z]+)!\1$/; 

RE_TWICE.test('abc!abc') // true 

RE_TWICE.test('abc!ab') // false

\1和\k可以同时使用


javaScript准确表示的整数范围在-2^53到2^53之间,超过这个范围,无法精确表示这个值

number.isSafeInteger()判断一个整数是否落在这个范围之内

Ingeter整数类型,只能表示整数,没有位数的限制,任何位数的整数都可以精确表示:1n+3n=4n


参数默认值位置

function f(x, y = 5, z) { return [x, y, z]; }

f() // [undefined, 5, undefined] 

f(1) // [1, 5, undefined] 

f(1, ,2) // 报错 

f(1, undefined, 2) // [1, 5, 2]

有默认值的参数都不是尾参数,无法忽略改参数,如果传入undefined,将触发改参数等于默认值,null则没有这个效果

注意指定了默认值后,函数的length属性将失真,变为0

(function(a){}).length //1

(function(b=1){}).length //0

(function(c,d,e=4){}).length//2

rest参数,用于获取函数的多余参数

function add(...values) {}

函数的length属性,也不包括rest参数

因为参数要先于方法执行,所以遇到以下情况时,会发生错误

function a(value = 070){

    'use strict';

    return value;

}

当给参数附完值之后,进入方法体中,采用严格模式,因为严格模式下八进制是不能带0的,所以报错

为了避免这种情况,采用两种方法:

1.使用全局严格模式

2.把函数抱在一个五参数的立即执行函数里面

const doSomething = (function () { 'use strict'; return function(value = 42) { return value; }; }());


name属性,返回函数名

es6允许使用箭头=>定义函数

var f = v =>v

等价于

var f = function(v){

    return f;

}

var sum = (num1,num2) => num1 + num2;

var sum = function(num1,num2){

    return num1 + num2;

}

使用箭头函数有几个使用注意点

1.this所指的对象不是使用时的对象而是定义时的对象

2.不可以当做构造函数,使用new会报错

3.不可以使用arguments对象,如果要,可以用rest对象代替

4.不可以使用yield命令,因此箭头函数不能做Generator函数


双冒号运算符:

    函数绑定运算符是并排的两个冒号,左边是对象,右边是方法,自动将左边的对象绑定到方法中

    foo::bar 等同于

    bar.bind(foo)

    foo::bar(...arguments);

    bar.apply(foo,arguments);


尾调用优化

    尾调用(tail call)某个函数的最后一步调用了另一个函数

    函数调用会在内存中形成调用记录,比如从A调用B,在A中形成一个B的调用帧(call frame),B中在调用C,会在B中形成C的调用帧,所有调用帧会形成调用栈(call stack)

es6只要使用尾递归就永远不会出现栈溢出

尾调用模式仅在严格模式下生效

蹦床函数(trampoline)

function trampoline(f){

    while(f && f instanceof Function){

        f = f()

}

return f;

}

之前函数参数中是不允许出现尾逗号的,但在新版本中允许尾逗号的出现


const a1=[1,2];

const a2 = a1;

a2[0] = 2;

a1 //[2,2]

直接赋值,获取的是指向a1的指针,改变a2会直接改变a1的值

所以需要使用变通的方法

a2 = a1.concat();

拓展运算符提供了赋值数组的方法

a2 = [...a1]

[...a2] = a1

合并数组:

es5:

[1,2].concat(more)

es6:

[1,2,...more]

在es6中Array,from会将类似数组的对象转为真正的数组

let arrayLike = {

    '0':'a',

    '1':'b',

     '2':'c'

}

let arr2 = Array.from(arrayLike);

类似数组的对象就是拥有length属性的对象,都可以通过array.from转为数组

如果浏览器不支持,则使用array.prototype.slice方法

array.from([1,2,3],(x)=>x*x)

可以使用第二个参数,对其进行处理

array.of可以将一组值转化为数组

Array() // []

Array(3) // [, , ,] 

Array(3, 11, 8) // [3, 11, 8]

上面代码中, Array 方法没有参数、一个参数、三个参数时,返回结 果都不一样。只有当参数个数不少于2个时, Array() 才会返回由参数 组成的新数组。参数个数只有一个时,实际上是指定数组的长度。

copyWithin(target,start,end);

target:替换的位置

start:从哪里替换

end:没有指定则为数组默认长度

[1,2,3,4,5].copywithin(0,3)//[4,5,3,4,5]

fill()方法,填充数组

['a','b','c'].fill(7,1,2)         //['a',7,'c']

includes返回一个布尔值,表示某个函数是否包含给定的值

[1,2,3].includes(2)

使用indexOf的缺点:

    内部严格使用相等运算符(===)进行判断,这会导致对NaN的误判

    [NaN].indexOf(NaN)     // -1


对象的拓展

    const foo = 'value'

    const bar = {foo}        // bar = {foo : 'value'}

相当于

    const bar = {foo: foo}

方法简写:

    const a = {

        method(){

            return "this is a function"

        }

}

相当于

    const a = {

        method() : function(){

            return "this is a function"

        }

    }

name:

    bind方法创造的函数,name会返回bound+name,function创造的函数,name属性返回anonymous属性

相等:

es5中相等方法只有两种,一种是(==)还有一种是严格相等(===)

但都有缺点:

        前者自动转换数据类型,后者NaN不等于自身,+0等于-0

es6增加了object.is(A,B)方法

        与(===)的行为一致,但NaN相等,+0 与 -0不相等

        +0 === -0 //true

         NaN === NaN // false

         Object.is(+0, -0) // false 

         Object.is(NaN, NaN) // true

object.assign()用于方法合并

        const target = {a :1}

        const source1 = {b:2}

        const source2 = {c:3}

        object.assign(target,source1,source3);

        target       //{a:1,b:2,c:3}

如果对象相同,后面会覆盖前面,如果该参数不是对象,会将其转换为对象,然后返回

undefined 和 null 无法转换为对象,所以会报错

const v1 = 'abc'; 

const v2 = true; 

const v3 = 10;

const obj = Object.assign({}, v1, v2, v3);

 console.log(obj); // { "0": "a", "1": "b", "2": "c" }

v1 是字符串,v2是布尔类型,v3是数字,结果只有字符串合入了目标对象,数值和布尔值都会被忽略

这是因为因为只有字符串的包装对象,会产生枚举属性

注意:

object.assign实行的是浅拷贝,就是如果源对象的某个属性是引用,那么拷贝的也是引用,也就是说引用的值改变了,那就都跟着变化

同名属性采用替换原则

Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable // false

另外,ES6 规定,所有 Class 的原型的方法都是不可枚举的。

大多数时候,我们至关心对象自身的属性,多以会用object.keys()代替

_proto_属性,用来读取或者设置当前对象的prototype对象

const obj={

    method:function(){...}

}

obj._proto_=someOtherObj;

最好使用

object.setPrototypeOf()        写操作

object.getPrototypeOf()        读操作

object.create()                          生成操作代替

object.setPrototypeOf是es6正式推荐的设置原型对象的方法

object.getPrototypeOf()获取一个对象的原型对象

super关键字

只能用在对象的方法之中,用在其他地方会报错

对象也可以用解构赋值

但是必须在参数最后一个,也是浅拷贝

克隆完整对象:

    const a = object.assign(

        object.create(object.getPrototypeof(obj))

)

对象的拓展运算符后面也可以跟表达式


NULL传导运算符

const firstName = message?.body?.user?.firstName || 'default';

只要有一个返回null,都会返回undefined


Symbol:保证每一个属性都是独一无二的,是es6引入symbol的原因

JavaScript一共有7种属性

1.null

2.undefined

3.boolean

4.string

5.number

6.object

7.symbol

let s = Symbol();

typeOf s;

symbol函数前不能使用new命令,否则会报错

symbol函数的参数只是表示对当前symbol值的描述,因此相同参数的symbol函数的返回值是不相等的

symbol不能与其他值进行运算

symbol作为属性名时,该属性属于公开属性,不是私有属性


魔术字符串:在代码中多次出现的,与代码形成强耦合的字符串或者数值

一般都会将其改为变量

symbol.for,symbol,keyFor

let s1= Symbol.for('foo')

let s2 = Symbol.for('foo')

s1 === s2   //true

Symbol和Symbol.for的区别是:

        Symbol.for会在全局创建一个供搜索的变量,可以被搜索到,这样每次调用都会是这一个值,但如果不用for,那么每次调用的都会不一样

symbol.keyfor返回的是已登记的symbol类型值的key

Symbol.for为Symbol值登记的名字,是全局环境的,可以在不同的iframe或者service worker中取到同一个值


Singleton模式指的是调用一个类,任何时候返回的都是同一个实例

Symbol.hasInstance:判断是否为该对象的实例

Symbol.isConcatSpreadable:表示该对象用于Array.prototype.concat()是否可以展开

Symbol.species

对象的symbol.species属性,指向当前对象的构造函数。

Symbol.iterator

指向该对象的默认遍历器方法


Set用法:

es6提供了新的数据结构Set

类似于数组,但是成员都是唯一的,没有重复值

const s= new Set()

[2,3,3,6,8].forEach(x=>s,add(x))

let set = new Set()

set.add({});

set.add({});

set.size  //2

由于两个空对象不相等,所以它们被视为两个值

Set实例的操作方法:

        add(value),delete(value),has(value),clear()

WeakSet结构与Set类似,也是不重复的值的集合

但WeakSet的成员只能是对象,es6中WeakSet不可遍历,WeakSet中的数据不会计入内存回收机制,所以删除实例的时候,不用考虑,也不会出现内存泄漏

  Map 键值对

        const map = new Map([['name','a'],['title','Author']]);

        map.size

        map.has('name')

        map.get('name')

       Set 和 Map都可以生成新的Map对象

        Map遍历方法:

            keys,values,forEach,entries()

        map.forEach(function(value, key, map){

                console.log("key:%s,Value:%s",key,value)

        })

        Map 转数组

        const mapDemo = new Map().set(true,7).set({foo:3},['abc'])

        [...mapDemo]    //转换成数组

        数组转Map

        Map转为对象

        strMapToObj(mapDemo)

        对象转map

        strObjToMap({yes:true},{no:false});

        Map转为Json

        strMapToJson(mapDemo);

        Map转为数组JSON

        strMapToArrayJson(mapDemo);

        Json转为Map

        jsonToStrMap('{"yes":true,"no":false}')

    WeakMap弱引用只是健名,而不是键值,键值依然是正常引用

    WeakMap只有四个方法:

        get,set,has,delete

    weakMap部署私有属性

     Proxy:用于修改某些操作的默认行为

           proxy实际上重载了点运算符,用自己的定义覆盖了语言的原始定义

            var proxy = new Proxy(target,handler);

           var proxy = new Proxy({},{

                get :function(target,property){

                        return 35;

                }

            })

let obj = object.create(proxy);

obj.time    //35

Proxy操作一览表

    get(garget,propKey,receiver):拦截器对象属性的读取

    set(target,proKey,value,receiver):拦截器属性的设置  proxy.foo = v

    has(target,proKey):拦截 prokey in proxy操作,返回一个布尔值

     deleteProperty(target,propKey):拦截 delete proxy[propKey],返回一个boolean值

       ownKeys(target):object.getOwnePropertyNames(proxy),Object,getOwnPropertySymbols(proxy),Object.keys(proxy)

        返回一个数组

has()只对in()生效

 construct()用于拦截new命令

deleteProperty()用于拦截delete操作

Reflcet 也是ES6为了操作对象而提供的新的API

Object,defineProperty(obj,name,desc)在无法定义属性时,会抛出一个错误

Reflect对象上可以拿到语言内部的方法

Proxy实现观察值模式

使用observable和observe两个函数


promise:

    就是一个容器,里面保存着某个为了才会结束的时间,从语法上说,promise是一个对象,从它可以获取异步操作的消息

    无法取消promise,一旦新建就会立即执行,无法中途取消。

    如果不设置回调函数,promise无法自己抛出错误,无法反应到外部

    当处于pending状态时,无法得知目前进展到拿一个阶段

   promise.prototype.catch方法是.then(null,regection)的别名,用于指定发生错误时的回调函数

    建议使用().then().catch().catch()

    promise.all()用于将多个promise实例,包装成一个新的promise实例

    var  p = Promise.resolve(''Hello")

    p.then(function(s){

        console.log(s);

     })

    done()总是处于回调链的尾端,保证抛出任何可能出现的错误

    finally()不管对象状态如何,都会执行


Iterator遍历器:

    Iterator接口就是为所有数据结构,提供一种统一的访问机制,即for...of

    yield*:它会调用该结构的遍历器接口

    forEach循环缺点是无法中途跳出,break和return命令都不好用

    for..in循环:数组的健名是数字,但它以字符串来循环

                            会遍历手动添加的其他健

                            在某些情况下,会无顺序的遍历健名

    for..of可以与break,continue,return配合使用

Genenrator.prototype.throw() 可以在函数体

一旦Generator执行过程中抛出错误,且没有被内部捕获,就不会在执行下去了,javascript引擎人呢我这个Generator已经运行结束了。

Generator.prototype.return() 可以返回给定的值,并且终结遍历Generator函数

如果Generator函数内部有try...fninally代码块,那么return方法会推迟到finally代码块执行完在执行

yield*

如果在Generator函数内部,调用另一个Generator函数,默认情况下是没有效果的

这个需要用到yield*,去调用Gneerator函数

如果yield表达式后面跟的是一个遍历器对象,需要在yield表达式后面加上星号,这个就被成为yield*表达式

let obj = { * myGeneratorMethod() { ··· } };

如上述所示,属性前带星号,表示是个Generator函数,等价于

let obj = { myGeneratorMethod: function* () { // ··· } };     

Generator是实现状态机的最佳结构

协程适合用于多任务运行的环境,但是运行的协诚只能有一个,其他协程都处于暂停状态。

JavaScript语言的执行环境是单线程的,如果没有异步编程,更不无法使用

function* asyncJob() { // ...其他代码 var f = yield readFile(fileA); // ...其他代码 }

在asyncJob中,当函数运行到yield时,会将执行权交给其他协程,等到执行权返回再继续执行

将参数出入一个临时函数,临时函数在传入函数体,这个临时函数就叫做thunk函数

thunkify模块

生产环境的转换器建议使用thunkify模块

co模块:用于generator函数的自动执行

处理stream:

    node提供stream模式读写数据,一次只处理数据的一部分,会释放三个事件:

        data事件:下一块数据库已经准备好了

        end事件:整个数据量处理完了

        error事件:发生错误

async函数:

        1.内置执行器

        2.更好的语义

        3.更广的适用性

        4,返回值是promise,这样就可以使用then进行下一步操作

await命令,await命令后面是一个promise对象,如果不是,会被转为一个立即resolve的promise对象

多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发

  let [foo,bar] = await Promise.all(getFoo(),getBar());

如果希望多个请求并发执行,可以使用promise.all方法

fetch方法,远程读取url

fetch(url);

async function logInOrder(urls) { // 并发读取远程URL

const textPromises = urls.map(async url =>

{ const response = await fetch(url); return response.text(); }); // 按次序输出

for (const textPromise of textPromises)

{ console.log(await textPromise); } }

虽然map方法的参数是async函数,但它是并发执行的,应为只有async函数是继发执行,外部不受影响

for awat...of

遍历异步的Iterator接口

异步Generator函数内部可以同时使用await和yield命令,await命令用于将外部操作产生的值输入函数内部,yield命令用于将函数内部的值输出

javascript的四种函数方式,普通型函数,generator函数,async函数,异步generator函数

如果是一系列按照顺序执行的异步操作比如读取文件,写入新内容,在存入硬盘,可以使用async函数

如果是一系列产生相同数据结构的异步操作,可以使用异步generator函数

constructor方法:构造方法,this关键字则代表实例对象

类和模块内部就是严格模式,所以不需要use strice指定运行模式

类必须使用new调用,否则会报错,这个是它跟普通构造函数的一个主要区别

在生产环境,我们可以使用object.getPrototyoeOf方法来获取实例对象的原型


Class表达式

const MyClass = class Me{

    getClassName(){

        return Me,name;

    }

}

上述定义了一个类,但类的名字不是Me,而是MeClass方法

在内部可以使用Me来指代当前类

不存在变量提升:

    类不存在变量提升

    new Foo();

    class Foo{};

上面代码中,Foo类使用在前,定义在后,这样会报错

因为Es6不会自动将类的声明提升到代码头部

this 的指向

   class Logger{

        printName(name = 'there'){

            this,print('hahha')

        }

        print(text){

            console.log(text)

        }

}

const logger = new Logger();

const {printName} = logger;

printName();//会报错,找不到print方法

修改方法,在方法中绑定this

thsi.printName = this.printName.bind(this);

或者使用箭头函数

this.printName = (name = 'there') =>{

    this.print('hello')

}

因为此时this指向的不是内部,而是当前环境

class的静态方法

   所有类中定义的方法都会被实例继承,但是如果前面加了static,那么这个方法就不会被实例继承,而是通过类来调用,这就成为静态方法

类的实例属性:用等号,写入类的定义中

new.target 用在构造函数之中,返回new命令作用于那个构造函数

class可以通过extends关键字实现继承

super关键字

    class A  {}

    class B extends A{

        constructor(){

            super();

        }

    }

子类B的构造函数之中的super(),代表调用父类的构造函数,这个是必须要求

在子类构造函数中,supper指向父类的原型对象,所以定义在父类实例上的方法或者属性,是无法通过super调用的

Mixin:多个对象合成一个新的对象


修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升

core-decorators.js

第三方模块,提供了几种常用的装饰器

@autobind:使得方法中的this对象,绑定原始对象

@readonly:使得属性或方法不可写

@override:检查子类的方法是否正确覆盖了父类的同名方法,如果不正确,会报错

@deprecate:在控制台显示一条信息,显示此方法已被废弃

@supressWarinings:抑制@deprecate导致的输出waring的行为,但是异步代码发出的调用除外

const Foo = {

        foo(){

                console.log('foo')    

        }

}

class MyClass{}

Object.assign(MyClass.prototype,Foo);

let obj = new MyClass();

obj.foo();

通过object.assign方法,可以将Foo方法放进myClass类中

Trait也是一种修饰器,效果与Mixin类似,但是提供了更多功能,比如防止同名方法的冲突,排除混入某些方法,为混入的方法起别名等

@traits(TFoo, TBar::alias({foo: 'aliasFoo'}))

@traits(TExample::excludes('foo','bar')::alias({baz:'exa mpleBaz'}))

以上为起别名和排除方法

Babel转码器的支持

在线转码网址:

https://babeljs.io/repl/


Modeule

在Es6之前,javascript一直没有模块这个概念,所以用的是CommonJs和AMD两种,在es6之后,有了模块功能,可以在编译时就确定模块的依赖关系,以及输入和输出的变量,CommonJs和AMD都只能在运行时确定这些东西,

// ES6模块 import { stat, exists, readFile } from 'fs';

表示只加载fs中的这三个方法,其他的都不加载

ES6自动采用严格模式

1.变量必须声明后使用

2.函数的参数不能有同名属性

3.不能使用with语句

4.不能对只读属性赋值,否则报错

5.不能使用前缀0表示八进制数,否则报错

6.不能给只读属性赋值

7.不能删除不能删除的属性

8.eval不会再外层作用域引入变量

9.arguments和eval不能重新被赋值

10.argument不会自动反应函数参数的变化

11.不能使用argument.callee

12.不能使用argument.caller

13.禁止this指向全局对象

14.不能使用 fn.caller 和 fn.arguments 获取函数调用的堆栈 

15.增加了保留字(比如 protected 、 static 和 interface )

export命令

    模块功能主要由两个命令构成,export和import.

    export命令主要规定模块的对外接口

    import用于输入其他模块提供的功能


默认情况下,浏览器试同步加载javaScript脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,在继续向下渲染,如果是外部脚本,还必须加入脚本下载的时间

如果脚本体积很大,下载和执行的时间很长,因此会造成浏览器堵塞,用户会感觉浏览器卡死了,没有任何响应,这显然不是很好的体验,所以浏览器脚本允许异步加载

<script src="path/to/myModule.js" defer></script>

<script src="path/to/myModule.js" async></script>

打开对应属性,就会异步加载

defer和async属性的区别:

    前者要等到整个页面正常渲染结束,才会执行

    后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本后,在继续渲染

    defer是渲染完就执行

    async是下载完就执行

如果有多个defer脚本,会按照它们在页面出现的顺序加载,多个async脚本是不能保证加载顺序的


模块加载规则:

    <script type="module" src="./foo.js"></script>

需要加入type="module"才可以

模块加载默认为defer模式

模块一旦使用了async属性, <script type="module">就不会按照在页面出现的顺序执行,而是模块加载完,就立刻执行

注意模块中顶层关键字this返回undefined,而不是指向window,也就是说,在顶层使用this关键字,是无意义的

同一个模块如果加载多次,将只执行一次

ES6模块和CommonJs模块差异

CommonJs模块输出的是一个值的拷贝,es6模块输出的是值的引用

CommonJs是运行时加载,ES6模块是编译时输出接口

es6不会缓存运行结果,而是动态的去被加载的模块取值,并且变量总是绑定其所在的模块

由于es6输入的模块变量,只是一个符号链接,所以这个变量是只读的,对它进行重新赋值会报错

Node加载

    Node要求es6模块采用.mjs后缀文件名

目前Node的import命令只支持加载本地模块,不支持加载远程模块

如果模块名不包含路径,那么import命令回去node_modules目录寻找这个模块

import 'baz';

import 'abc/123';

如果模块名包含路径,那么import会根据路径去找

如果脚本省略了后缀名,那么Node会依次尝试四个后缀

./foo.mjs ./foo.js ./foo.json /foo.node

如果这些脚本文件都不存在,Node就会去加载 ./foo/package.json 的 main 字段指定的脚本

./foo/package.json 不存在或者没有 main 字段,那么就会依次加 载 ./foo/index.mjs 、 ./foo/index.js 、 ./foo/index.json 、 ./foo/index.node 。如果以上四个文件还是都不存在,就会抛出错 误。

import foo from './c';

foo(); // 2

import * as bar from './c';

bar.default(); // 2

bar(); // throws, bar is not a function

上面代码中,bar本身是一个对象,不能当做函数调用,只能通过bar.default调用

CommonJS模块加载es6模块,不能使用requier命令,而要使用import函数

es6模块的所有输出接口,会成为对象的属性

循环加载处理

    CommonJS模块格式的加载原理

    CommonJS的一个模块,就是一个脚本文件,require命令第一次加载改脚本,就会执行整个脚本,然后在内存中生成一个对象

{ id: '...', exports: { ... }, loaded: true, ... }

该对象的id就是模块名,exports是暴露在外面的模块输出的各个接口,loaded属性是一个boolean,表示模块是否执行结束

a.js代码

exports.done = false;

var b = require('./b.js');

console.log('在 a.js 之中,b.done = %j', b.done);

exports.done = true;

console.log('a.js 执行完毕');

b.js代码

exports.done = false;

var a = require('./a.js');

console.log('在 b.js 之中,a.done = %j', a.done);

exports.done = true;

console.log('b.js 执行完毕');

b.js执行到第二行,就会去加载a.js,这时,就发生了循环加载,系统会去a.js模块对应对象的exports属性取值,可是因为a.js还没执行完,从exports属性只能取回已经执行的部分,而不是最后的值

export.done = false;

因此,对于b.js来说,它从a.js只输入一个变量done,值为false

然后b.js接着往下执行,等到全部执行完,再把执行权交个a.js

需要注意的是,在遇到循环加载时,因为当前已经执行的部分是部分加载后的值,所以会和后面的结果有差异,所以,输入变量的时候,必须非常小心

var a = require('a');      //安全写法

var foo = require('a').foo    //危险的写法

exports.good = function(arg){

    return a.foo('good', arg);

}

exports.bad = function(arg){

    return foo('bad', arg);

}

es6模块的循环加载

    es6处理循环加载是动态引用,如果使用import从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块引用,所以开发者需要确保取值的时候能取到值

    a.js

    import {bar} from './b.js';

    console.log('a.js');

    console.log(bar);

    export let foo = 'foo';

    b.js

     import {foo} from './a.js';

     console.log('b.js');

     console.log(foo);

      export let bar = 'bar'

结果:

       $ babel-node a.js

        b.js

        undefined

        a.js

        bar

执行a文件时,会加载b.js,b.js会去加载a,a因为执行了,所以b继续执行

输出       b.js

b之后调用a中的foo方法,因为a没有加载完,取不到foo的值,所以为undefined

b执行完,执行a,后面的值正常输出


var命令会存在变量提升效果,let命令没有这个问题

全局变量:

    在let和const之间,选择const,尤其是在全局环境,不应该设置变量,只应设置常量

    const可以提醒阅读程序的人,这个变量不应该改变

    另一个是const比较符合函数式编程思想

    JavaScript编译器对const进行优化,所以多使用const,有利于提供程序的运行效率

字符串

    const a = 'foobar';

    const b =  `foo${a}bar`

    const c = 'foobar';

    静态字符串一律使用单引号或反引号,不使用双引号

    动态字符串使用反引号


1.如果x不是正常值,中断执行

2.如果y不是正常值,中断执行

3.如果type(x)与type(y)相同,执行严格相等x===y

4.如果x是null,y是undefined,返回true

5.如果x是undefined,y是null,返回true

6.如果type(x)是数值,type(y)是字符串,返回x == ToNumber(y)结果

7.如果type(x)是字符串,type(y)是数值,返回ToNumner(x) == y的结果

8.如果type(x)是布尔值,返回toNumber(x) == y的结果

9.如果type(y)是布尔值,返回x == ToNumber(y)的结果

10.如果type(x)是字符串或者数值或者Symbol值,type(y)是对象,返回x == toPrimitive(y)的结果

11.如果 Type(x) 是对象, Type(y) 是字符串或数值 或 Symbol 值,返回 ToPrimitive(x) == y 的结果。

12.返回false


视图:ArrayBuffer对象视为内存区域,可以存放多种类型的数据

同一段内存,不同数据有不同的解读方法,这就叫做"视图"

typedArray视图,另一种是dataView视图


二进制应用

AJAX:传统上,服务器通过AJAX操作只能返回文本数据,即responseType属性默认为text

HmlHttpRequst第二版XHR2允许服务器返回二进制数据,如果明确知道返回的二进制数据类型,可以把返回类型设为arraybuffer;不知道,就设为blob

Canvas元素输出的二进制像素数据,就是typedArray数组

如果一个文件知道它的二进制数据类型,也可以将这个文件读取为ArrayBuffer对象

javascript是单线程的,web worker引入了多线程,主线程用来与用户互动,worker线程用来承担计算任务,每个线程的数据都是隔离的,通过postMessage()通信

SharedArrayBuffer

const w = new Worker('myworker.js');

主线程新建了一个worker线程,该线程与主线程之间会有一个通信渠道

主线程通过w.postMessage向worker线程发消息

w.postMessage('hi');

w.onmessage = function(ev){

    console.log(ev.data);

}

主线程先发一个消息,在监听到worker线程的回应后,就将其打印出来

onmessage = function(ev){

    console.log(ev.data);

    postMessage('hi');

}

   ES2017 引入 SharedArrayBuffer ,允许 Worker 线程与主线程共享同 一块内存。 SharedArrayBuffer 的 API 与 ArrayBuffer 一模一样, 唯一的区别是后者无法共享。

    const sharedBuffer = new SharedArrayBuffer(1024);

    w.postMessage(sharedBuffer);

    const sharedArray = new Int32Array(sharedBuffer );

worker线程从事件的data属性上面取到数据

    onmessage = function(ev){

        const sharedBuffer = ev.data

        const sharedArray = new Int32Array(sharedBuffer );

}

 SharedArrayBuffer 与 ArrayBuffer 一样,本身是无法读写的,必 须在上面建立视图,然后通过视图读写。

 Atomics对象保证所有共享内存中的操作都是原子性的,并且可以在所有线程内同步

// 主线程

const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 100000);

const ia = new Int32Array(sab);

for (let i = 0; i < ia.length; i++) {

    ia[i] = primes.next(); // 将质数放入 ia

}

线程 ia[112]++; // 错误 

Atomics.add(ia, 112, 1); // 正确

上面直接对内存进行操作是错误的,因为会被编译成好几天机器语言,而且不能保证中间会插入几条其他的机器语言。Atommics是将其命令作为一个整体,中间不可以被打断,插入其他的命令,所以避免了线程竞争,提高多线程共享内存时的操作安全

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

推荐阅读更多精彩内容

  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,559评论 0 11
  • 彩排完,天已黑
    刘凯书法阅读 4,201评论 1 3
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 124,553评论 2 7