var
声明初始化变量, 声明能够提高,但初始化不能够提高。html
let
const
JS引擎在扫描代码发现变量声明时,要么将它们提高至做用域顶部(
var
声明),要么将声明放到TDZ(临时死区)中(let
和const
声明)。访问TDZ中的变量会触发运行时错误。只有执行过变量声明语句后,变量才会从TDZ中移出,而后能够正常访问。node
第一种状况:es6
if(condition) {
console.log(typeof value); //引用错误!
let value = "blue"; //TDZ
}
复制代码
第二种状况:web
console.log(typeof value); //'undefined'-->这里不报错,只有变量在TDZ中才会报错
if(condition) {
let value = "blue";
}
复制代码
当即调用(IIFE)正则表达式
let
和const
之因此能够在运用在for-in和for-of循环中,是由于每次迭代会建立一个新的绑定(const在for循环中会报错)。编程
var
可能会在无心中覆盖一个已有的全局属性,let
或const
会在全局做用域下建立一个新的绑定,但该绑定不会添加为全局对象的属性。换句话说,使用let
或const
不能覆盖全局变量,而只能遮蔽它。若是不是为全局对象建立属性,使用let
和const
要安全得多。json
注:若是但愿在全局对象下定义变量,仍然可使用var。这种状况常见于在浏览器中跨frame或跨window访问代码。数组
名词解释:promise
在UTF-16中,前2^16个码位均以16位的编码单元表示,这个范围被称做基本多文种平面浏览器
这个方法尽管有效,可是当统计长字符串中的码位数量时,运动效率很低。所以,你也可使用字符串迭代器解决效率低的问题,整体而言,只要有可能就尝试着减少码位计算的开销。
检测u修饰符支持:
function hasRegExpU() {
try {
var pattern = new RegExp('.', 'u');
return true;
} catch (ex) {
return false;
}
}
复制代码
当执行操做时, y修饰符会把上次匹配后面一个字符的索引保存到
lastIndexOf
中;若是该操做匹配的结果为空,则lastIndexOf
会被重置为0。g修饰符的行为相似。
1. 只有调用exec()和test()这些正则表达式对象的方法时才会涉及lastIndex属性;
2. 调用字符串的方法,例如match(),则不会触发粘滞行为。
复制代码
var re1 = /ab/i;
re2 = new RegExp(re1); //没有修饰符复制
re3 = new RegExp(re1, "g"); //有修饰符(ES6)
复制代码
es5方法获取正则表达式的修饰符:
function getFlags(re) {
var text = re.toString();
return text.substring(text.lastIndexOf('/' + 1, text.length);
}
复制代码
多行字符串
基本的字符串格式化(字符串占位符)
HTML转义
function passthru(literals, ...substitutions) {
//返回一个字符串
let result = "";
//根据substitutions的数量来肯定循环的执行次数
for(let i=0; i<substitutions.length; i++){
result += literals;
result += substitutions[i]
console.log(literals[i])
console.log(substitutions[i])
}
//合并最后一个literal
result += literals[literals.length - 1];
return result;
}
let count = 10;
price = 0.25;
message = passthru`${count} items cost $${(count * price).toFixed(2)}`;
console.log(message)
复制代码
String.raw`assda\\naadasd`
//代码模拟(略)
复制代码
ES5默认参数值
下面函数存在什么问题 ???
function makeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback || function() {};
}
复制代码
假如timeout
传入值0,这个值是合法的,可是也会被视为一个假值,并最终将timeout
赋值为2000。在这种状况下,更安全的选择是经过typeof
检查参数类型,以下:
function makeRequest(url, timeout, callback) {
timeout = (typeof timeout !== 'undefined') ? timeout :2000;
callback = (typeof callback !== 'undefined') ? callback : function() {};
}
复制代码
ES5默认参数值
function makeRequest(url, timeout = 2000, callback) {
//函数的其他部分
}
//特别注意:此时 null 是一个合法值,因此不会使用 timeout 默认值,即 timeout = null
makeRequest('/foo', null, function(body){
doSomething(body);
})
复制代码
arguments
的影响**非严格模式:参数变化,arguments对象随之改变;
严格模式:不管参数如何变化,arguments对象再也不随之改变;
非严格模式/严格模式:不管参数如何变化,arguments对象再也不随之改变;
注: 在引用参数默认值的时候,只容许引用前面参数的值,即先定义的参数不能访问后定义的参数。这能够用默认参数的临时死区来解释。以下:
function add(first = second, second) {
return first + second;
}
console.log(add(1, 1)); //2
console.log(add(undefined, 1)) //抛出错误
//解释原理:
//add(1, 1)
let first = 1;
let second = 1;
//add(undefined, 1)
let first = second;
let second = 1; //处于临时死区
复制代码
setter
之中(由于对象字面量setter的参数有且只有一个,而在不定参数的定义中,参数的数量能够无限多)不管是否使用不定参数,arguments对象老是包含全部传入函数的参数。
let value = [25, 50, 75, 100];
//es5
console.log(Math.max.apply(Math, values); //100
//es6
console.log(Math.max(...values)); //100
复制代码
两个有关函数名称的特例:
bind()
函数建立的函数,其名称将带有“bound”前缀;Function
构造函数建立的函数,其名称将是“anonymous”.var doSomething = function() {
//空函数
}
console.log(doSomething.bind().name); //'bound doSomething'
console.log((new Function()).name); //'anonymous(匿名)'
复制代码
切记: 函数name属性的值不必定引用同名变量,它只是协助调试用的额外信息,因此不能使用name属性的值来获取对于函数的引用。
JS函数有两个不一样的内部方法:[[call]] 和 [[Construct]]。
this
绑定到实例上(具备 [[Construct]] 方法的函数被统称为构造函数,箭头函数没有 [[Construct]] 方法 );new
关键字调用函数,则执行 [[call]] 函数,从而直接执行代码中的函数体;为了解决判断函数是否经过new关键字调用的问题,new.target
横空出世 (instance of ---> new.target)
在函数外使用
new.target
是一个语法错误。
箭头函数与传统的JS函数不一样之处主要有如下几个方面:
this
、super
、arguments
和new.target
绑定;new
关键字调用;this
的绑定;arguments
对象建立一个空函数:
let doNothing = () => {};
复制代码
返回一个对象字面量
let getTempItem = id => ({ id: id, name: "Temp"});
复制代码
建立当即执行的函数
let person = ((name) => {
return {
getName: function() {
return name;
}
}
})("xszi")
console.log(person.getName()); //xszi
复制代码
箭头函数没有this
绑定
let PageHandler = {
id: '123456',
init: function() {
document.addEventListener("click", function(event){
this.doSomething(event.type); //抛出错误
}, false)
},
doSomething: function(type) {
console.log("handling " + type + "for" + this.id)
}
}
复制代码
使用bind()
方法将函数的this
绑定到PageHandler
,修正报错:
let PageHandler = {
id: '123456',
init: function() {
document.addEventListener("click", (function(event){
this.doSomething(event.type); //不报错
}).bind(this), false)
},
doSomething: function(type) {
console.log("handling " + type + "for" + this.id)
}
}
复制代码
使用箭头函数修正:
let PageHandler = {
id: '123456',
init: function() {
document.addEventListener("click",
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log("handling " + type + "for" + this.id)
}
}
复制代码
- 箭头函数没有
prototype
属性,它的设计初衷是 即用即弃, 不能用来定义新的类型。- 箭头函数的中
this
取决于该函数外部非箭头函数的this
值,不能经过call(), apply()
或bind()
方法来改变this
的值。
箭头函数没有arguments
绑定
始终访问外围函数的
arguments
对象
如何利用尾调用优化
function factorial(n) {
if ( n<=1 ) {
return 1;
}
}else{
//引擎没法自动优化,必须在返回后执行乘法操做
return n * factorial(n-1);
//随调用栈尺寸的增大,存在栈溢出的风险
}
复制代码
function factorial(n, p = 1) {
if ( n<=1 ) {
return 1 * p;
}
}else{
let result = n * p;
//引擎可自动优化
return factorial(n-1, result);
//不建立新的栈帧,而是消除并重用当前栈帧
}
复制代码
脚本开始执行时存在于JS执行环境中的对象,全部标准对象都是内建对象。
var person = {
name: 'xszi',
sayName: function() {
console.log(this.name);
}
}
var person = {
name: 'xszi',
sayName() {
console.log(this.name);
}
}
//二者惟一的区别是,简写方式可使用super关键字
复制代码
在对象字面量中使用方括号表示的该属性名称是可计算的,它的内容将被求值并被最终转化为一个字符串。
以下:
var suffix = ' name';
var person = {
['first' + suffix]: 'xszi',
['last' + suffix]: 'wang'
};
console.log(person['first name']); //xszi
console.log(person['last name']) // wang
复制代码
ECMAScript
其中一个设计目标是:再也不建立新的全局函数,也不在Object.prototype
上建立新的方法。
Object.is()
大多数状况下,Object.is()
与'==='运行结果相同,惟一区别在于 +0 和 -0 识别为不相等,而且NaN
与NaN
等价。
Object.assign()
mixin()
方法使用赋值操做符(assignment operator)= 来复制相关属性,却不能复制 访问器属性 到接受对象中,所以最终添加的方法弃用mixin
而改用assign
做为方法名。
Object.assign()
方法能够接受任意数量的源对象,并按指定的的顺序将属性赋值到接收对象中。因此若是多个源对象具备同名属性,则排位靠后的源对象会覆盖排位靠前的。
访问器属性: Object.assign()
方法不能将提供者的访问器属性赋值到接收对象中。因为 Object.assign()
方法执行了赋值操做,所以提供者的访问器属性最终会转变为接受对象中的一个数据属性。
eg:
var receiver = {};
supplier = {
get name() {
return 'file.js'
}
};
Object.assign(receiver, supplier);
var descriptor = Object.getOwnPropertyDescriptor(receiver, 'name');
console.log(descriptor.value); // 'file.js'
console.log(descriptor.get); // undefined
复制代码
自有属性枚举顺序的基本规则是:
symbol
按照他们被加入的顺序排序。Object.setPrototypeOf(targetObject, protoObject);
复制代码
Super
引用
Super
引用至关于指向对象原型的指针,实际上也就是Object.getPrototypeOf(this)
, **必需要在使用简写方法的对象中使用Super
**引用。
Super
引用不是动态变化的,它老是指向正确的对象。
ES6正式将方法定义为一个函数,它会有一个内部的 [[HomeObject]] 属性来容纳这个方法从属的对象。
Super
的因此引用都经过 [[HomeObject]] 属性来肯定后续的运行过程:
Object.getPrototypeOf()
方法来检索原型的引用;this
绑定而且调用相应的方法。let node = {
type: "Indetifier",
name: "foo"
}
let {type, name} = node;
console.log(type); // Indetifier
console.log(name); // foo
复制代码
不要忘记初始化程序(也就是符号右边的值)
var {type, name}; //报错,使用let和const一样报错
// 除使用解构外,使用var, let不强制要求提供初始化程序, 可是const必定须要;
复制代码
let node = {
type: "Indetifier",
name: "foo"
}
type = 'Literal', name = 5;
//使用解构语法为多个变量赋值
({type, name} = node); //须要使用()包裹解构复制语句,{}是一个代码块,不能放在左边
console.log(type); // Indetifier
console.log(name); // foo
复制代码
let node = {
type: "Indetifier",
name: "foo"
}
let { type: localType, name: localName } = node;
console.log(localType); // Indetifier
console.log(localName); // foo
复制代码
type: localType
语法的含义是读取名为type
的属性并将其只存储在变量localType
中。
数组解构也可用于赋值上下文,但不须要用小括号包裹表达式,这一点与对象解构的的约定不一样。
let colors = ['red', 'green', 'blue'], firstColor = 'black', secondColor = 'purple';
[firstColor, secondColor] = colors;
console.log(firstColor); // 'red'
console.log(secondColor); // 'green'
复制代码
交换值
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a); //2
console.log(b); //1
复制代码
let colors = ['red', ['green', 'lightgreen'], 'blue'];
let [firstColor, [secondColor]] = colors;
console.log(firstColor); //red
console.log(secondColor); //green
复制代码
function setCookie(name, value, options) {
options = options || {};
let secure = options.secure,
path = options.path,
domian= options.domain,
expires = options.expires;
//设置cookie代码
}
// 第三个参数映射到options中
setCookie('type', 'js', {
secure: true,
expires: 60000
})
复制代码
上面函数存在一个问题:仅查看函数的声明部分,没法辨识函数的预期参数,必须经过阅读函数体才能够肯定全部参数的状况。可使用 解构参数 来优化:
function setCookie(name, value, {secure, path, domain, expires}}) {
//设置cookie代码
}
setCookie('type', 'js',{
secure: true,
expires: 60000
})
复制代码
Symbol
和Symbol
属性在
Symbol
出现以前,人们一直经过属性名来访问全部属性,不管属性名由什么元素构成,所有经过一个字符串类型的名称来访问;私有名称原来是为了让开发者们建立非字符串名称而设计的,可是通常的技术没法检测这些属性的私有名称。
经过Symbol
能够为属性添加非字符串名称,可是其隐私性就被打破了。
Symbol
、Symbol
共享体系let firstName = Symbol();
let person = {};
person[firstName] = 'xszi';
console.log(person[firstName]); //xszi
复制代码
因为Symbol
是原始值,所以调用new Symbol()
会致使程序抛出错误。
Symbol
函数接受一个可选参数,其可让你添加一段文本描述即将建立的Symbol
,这段描述 不可用于属性访问。该描述被存储在内部的 [[Description]] 属性中,只有当调用Symbol
的toString()
方法时才能够读取这个属性。
Symbol
共享体系有时咱们可能但愿在不一样代码中共享同一个
Symbol
(在很大的代码库中或跨文件追踪Symbol
很是困难),ES6提供了一个能够全局访问的全局Symbol
注册表,即便用Symbol.for()
方法。
let uid = Symbol.for('uid');
let object = {};
object[uid] = '12345';
console.log(object[uid]); //'12345'
console.log(uid); // 'Symbol(uid)'
复制代码
实现原理: Symbol.for()
方法首先在全局Symbol
注册表中搜索键为‘uid’的Symbol
是否存在,若是存在,直接返回已有的Symbol
;不然,建立一个新的Symbol
,并使用这个键在Symbol
全局注册表中注册,随即返回新建立的Symbol
。
可使用Symbol.keyFor()
方法在Symbol
全局注册表中检索与Symbol
有关的键。
Symbol
全局注册表是一个相似全局做用域的共享环境,也就是说你不能假设目前环境中存在哪些键。当使用第三方组件时,尽可能使用Symbol
键的命名空间减小命名冲突。如 jQuery.
console.log()
会调用Symbol
的String()
方法desc = String(uid);
desc = uid + ''; //报错,不能转为字符串类型
desc = uid / 2; //报错,不能转为数字类型
复制代码
Object.keys()
返回可枚举属性Object.getOwnPropertyNames()
不考虑可枚举性,一概返回Object.getOwnProperty-Symbols()
ES6用来检索对象中的Symbol属性全部对象一开始没有本身独有的属性,可是对象能够从原型链中继承Symbol属性。
well-know Symbol
暴露内部操做仍是不怎么理解,找到一个使用Symbol
的实际场景才能更好理解!
Set 和 Map 主要的应用场景在于 数据重组 和 数据储存
Set 是一种叫作集合的数据结构,Map 是一种叫作字典的数据结构
//set
var set = Object.create(null);
set.foo = true;
//检查属性是否存在
if(set.foo){
//要执行的代码
}
复制代码
//map
var map = Object.create(null);
map.foo = "bar";
//获取已存值
var value = map.foo;
console.log(value);
复制代码
通常来讲,Set集合常被用于检查对象中是否存在某个键名,而Map集合常被用来获取已存的信息。
全部对象的属性名必须是字符串类型,必须确保每一个键名都是字符串类型且在对象中是惟一的
- 有序
- 不重复
+0和-0在Set中被认为是相等的。
Set构造函数能够接受全部可迭代对象做为参数
复制代码
forEach遍历Set,回调函数中value和key的值相等,个人理解: Set集合中的元素都是不重复的,因此能够把值做为键,即“以值为键”。以下:
let set = new Set([1, 2]);
set.forEach(function(value, key, ownerSet)){
console.log(key + " " + value);
console.log(ownerSet === set);
});
复制代码
在回调函数中使用this引用
let set = new Set([1, 2]);
let processor = {
output(value) {
console.log(value);
},
process(dataSet) {
dataSet.forEach(function(value){
this.output(value);
}, this);
}
};
processor.process(set);
复制代码
箭头函数this
let set = new Set([1, 2]);
let processor = {
output(value) {
console.log(value);
},
process(dataSet) {
dataSet.forEach(value => this.output(value));
}
};
processor.process(set);
复制代码
let set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
array = [...set];
console.log(array); //[1, 2, 3, 4, 5]
复制代码
function eliminbateDuplicates(items){
return [...new Set(items)]
}
let numbers = [1, 2, 3, 3, 3, 4, 5];
noDuplicates = eliminateDuplicates(numbers);
console.log(noDuolicates); //[1, 2, 3, 4, 5]
复制代码
解决Set集合的强引用致使的内存泄漏问题
Weak Set集合只存储对象的弱引用,而且不能够存储原始值;集合中的弱引用若是是对象惟一的引用,则会被回收并释放相应内存。
Weak Set集合的方法:add, has,delete
差别 | Set | Weak Set |
最大区别 | 保存对象值的强引用 | 保存对象值的弱引用 |
方法传入非对象参数 | 正常 | 报错 |
可迭代性 | 可迭代 | 不可迭代 |
支持forEach方法 | 支持 | 不支持 |
支持size属性 | 支持 | 不支持 |
- 有序
- 键值对
复制代码
在对象中,没法用对象做为对象属性的键名;可是Map集合中,却能够这样作。
let map = new Map(),
key1 = {},
key2 = {};
map.set(key1, 5);
map.set(key2, 42);
console.log(map.get(key1)); //5
console.log(map.get(key2)); //42
复制代码
以上代码分别用对象 key1 和 key2 做为两个键名在Map集合里存储了不一样的值。这些键名不会强制转换成其余形式,因此这两个对象在集合中是独立存在的,也就是说,不须要修改对象自己就能够为其添加一些附加信息。
Map集合的方法:set,get,has(key),delete(key),clear,forEach,size(属性)
Map集合初始化过程当中,能够接受任意数据类型的键名,为了确保它们在被存储到Map集合中以前不会被强制转换为其余数据类型,于是只能将它们放在数组中,由于这是惟一一种能够准确地呈现键名类型的方式。
无序 键值对
- 弱引用Map集合,集合中键名必须是一个对象,若是使用非对象键名会报错;
- 键名对于的值若是是一个对象,则保存的是对象的强引用,不会触发垃圾回收机制。
复制代码
let map = new WeakMap(),
element = document.querySelector('.element');
map.set(element, "Original");
let value = map.get(element);
console.log(value); //"Original"
//移除element元素
element.parentNode.removeChild(element);
element = null;
//此时 Weak Map集合为空,数据被同步清除
复制代码
set, get, has, delete
私有对象数据
存储对象实例的私有数据是Weak Map的另一个应用:
var Person = (function(){
var privateData = {},
privateData = 0;
function Person(name){
Object.defineProperty(this, "_id", { value: privateId++ });
privateData[this._id] = {
name: name
};
}
Person.prototype.getName = function() {
return privateData[this._id].name;
}
return Person;
}());
复制代码
上面这种方法没法获知对象实例什么时候被销毁,不主动管理的话,privateData中的数据就永远不会消失,须要使用Weak Map来解决这个问题。
let Person = (function(){
let privateData = new WeakMap(),
privateData = 0;
function Person(name){
privateData.set(this, {name: name});
}
Person.prototype.getName = function() {
return privateData.get(this).name;
}
return Person;
}());
复制代码
当你要在Weak Map集合与普通的Map集合之间作出选择时,须要考虑的主要问题是,是否只用对象做为集合的键名。
迭代器的出现旨在消除循环复杂性并减小循环中的错误。
迭代器是一种特殊对象,他具备一些专门为迭代过程设计的专有端口,全部的迭代器对象都有一个next()
方法,每次调用都返回一个结果对象。
ES5语法 实现一个迭代器
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
}
}
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); //"{ value: 1, done: false}"
console.log(iterator.next()); //"{ value: 2, done: false}"
console.log(iterator.next()); //"{ value: 3, done: false}"
console.log(iterator.next()); //"{ value: undefined, done: true}"
复制代码
生成器是一种返回迭代器的函数,经过function
关键字后的星号(*)来表示,函数中会用到新的关键字yield
。
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator.next().value); //1
console.log(iterator.next().value); //2
console.log(iterator.next().value); //3
复制代码
yield
的使用限制
yield
关键字只可在生成器内部使用,在其余地方使用会致使程序抛出语法错误,即便在生成器内部的函数里使用也会报错,与return
关键字同样,不能穿透函数的边界。
不能用箭头函数来建立生成器
for-of
循环可迭代对象具备Symbol.iterator
属性,是一种与迭代器密切相关的对象。Symbol.iterator
经过指定的函数能够返回一个做用于附属对象的迭代器。
function isIterable(object) {
return typeof object[Symbol.iterator] === 'function';
}
console.log(isIterable([1, 2, 3])); //true
复制代码
默认状况下,开发者定义的对象都是不可迭代对象,但若是给Symbol.iterator添加一个生成器,则能够将其变为可迭代对象。
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
}
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
console.log(x);
}
1
2
3
复制代码
集合对象迭代器
字符串迭代器
NodeList迭代器
function *createIterator() {
let first = yield 1;
let second = yield first + 2; //4 + 2
yield second + 3; //5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // '{ value: 1, done: false }'
console.log(iterator.next(4)); // '{ value: 6, done: false }'
console.log(iterator.next(5)); // '{ value: 8, done: false }'
console.log(iterator.next()); // '{ value: undefined, done: true }'
复制代码
调用next()方法命令迭代器继续执行(可能提供一个值),调用throw()方法也会命令迭代器继续执行,但同时也抛出一个错误,在此以后的执行过程取决于生成器内部的代码。
展开运算符与
for-of
循环语句会直接忽略经过return语句指定的任何返回值,只要done
一变为true
就当即中止读取其余的值。
在生成器里面再委托另外两个生成器
生成器使人兴奋的特性与异步编程有关。
function run(taskDef) {
//建立一个无使用限制的迭代器
let task = taskDef();
//开始执行任务
let result = task.next();
//循环调用next() 的函数
function step() {
//若是任务未完成,则继续执行
if(!result.done){
result = task.next();
//result = task.next(result.value) 向任务执行器传递数据
step();
}
}
//开始迭代执行
step();
}
复制代码
ES6中的类与其余语言中的仍是不太同样,其语法的设计实际上借鉴了Javascript的动态性。
ES5 中的近类结构,建立一个自定义类型:
function PersonType(name) {
this.name = name;
}
PersonType.prototype.sayName = function() {
console.log(this.name);
}
var person = new PersonType('waltz');
person.sayName(); //'waltz'
console.log(person instanceof PersonType); //true
console.log(person instance of Object); //true
复制代码
class PersonClass {
//等价于PersonType的构造函数
constructor(name){
this.name = name;
}
//等价于PersonType.protoType.sayName
sayName() {
console.log(this.name);
}
}
let person = new PersonClass('waltz');
person.sayName(); //'waltz'
console.log(person instanceof PersonClass); //true
console.log(person instanceof Object); //true
console.log(typeof PersonClass); //'function'
console.log(typeof PersonClass.prototype.sayName) //'function'
复制代码
自有属性是实例中的属性,不会出如今原型上,且只能在类的构造函数或方法中建立。建议你在构造函数中建立全部的自有属性,从而只经过一处就能够控制类中的全部自有属性。
与函数不一样的是,类属性不可被赋予新值。
let
声明相似,不能被提高;真正执行声明语句以前,它们会一直存在于临时死区(TDZ)中。Object.defineProperty()
方法手工指定某个方法为不可枚举;而在类中,全部的方法都是不可枚举的。[[Construct]]
的内部方法,经过关键字new调用那些不含[[Construct]]
的方法会致使程序抛出错误。new
之外的方式调用类的构造函数会致使程序抛出错误。和函数的声明形式和表达式相似。
在js引擎中,类表达式的实现与类声明稍有不一样。对于类声明来讲,经过let定义的外部绑定与经过const定义的内部绑定具备相同的名称。而命名类表达式经过const定义名称,从而只能在类的内部使用。
在程序中。一等公民是指一个能够传入函数,能够从函数返回,而且能够赋值给变量的值。(JS函数是一等公民)
function createIbject(classDef) {
return new classDef();
}
let Obj = createObject(class {
sayHi() {
console.log('Hi!')
}
});
obj.sayHi(); //'Hi!'
复制代码
类表达式还有另外一种使用方式,经过当即调用类构造函数能够建立单例。用new
调用类表达式,紧接着经过一对小括号调用这个表达式:
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('waltz');
person.sayName(); // 'waltz'
复制代码
依照这种模式可使用类语法建立单例,而且不会再做用域中暴露类的引用。
class CustomHtmlElement() {
constructor(element){
this.element = element;
}
//建立getter
get html() {
return this.element.innerHTML;
}
//建立setter
set html(value) {
this.element.innnerHTML = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(CustomHtmlElement.prototype, "html");
console.log("get" in descriptor); //true
console.log("set" in descriptor); //true
console.log(descriptor.enumerable); //false
复制代码
//类方法
let methodName = "sayName";
class PersonClass(name) {
constructor(name) {
this.name = name;
}
[methodName]() {
console.log(this.name);
}
};
let me = new PersonClass("waltz");
me.sayName(); // 'waltz'
复制代码
//访问器属性
let propertyName = 'html';
class CustomHTMLElement)() {
constructor(element) {
this.element = element;
}
get [propertyName]() {
return this.element.innerHTML;
}
set [propertyName](value) {
this.element.innerHTML = value;
}
}
复制代码
class MyClass {
*createIterator() {
yield 1;
yield 2;
yield 3;
}
}
let instance = new MyClass();
let iterator = instance.createIterator();
复制代码
若是用对象来表示集合,又但愿经过简单的方法迭代集合中的值,那么生成器方法就派上用场了。
尽管生成器方法很实用,但若是你的类是用来表示值的 集合 的,那么为它定义一个 默认迭代器 更有用。
直接将方法添加到构造函数中来模拟静态成员是一种常见的模式。
function PersonType(name) {
this.name = name;
}
//静态方法
PersonType.create = function(name) {
return new PersonType(name);
}
//实例方法
PersonType.protoType.sayName = function() {
console.log(this.name);
};
var person = PersonType.create('waltz');
复制代码
类等价:
class PersonClass {
// 等价于PersonType构造函数
constructor(name) {
this.name = name;
}
//等价于PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
//等价于PersonType.create
static create(name) {
return new PersonClass(name);
}
}
let person = PersonClass.create('waltz');
复制代码
类中的全部方法和访问器属性均可以用static关键字来定义,惟一的限制是不能将static用于定义构造函数方法。
不可在实例中访问静态成员,必需要直接在类中访问静态成员。
ES5实现
function Rectangle(length, width) {
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function() {
return this.length * this.width;
};
function Square(length) {
Rectangle.call(this, length, length);
}
Square.prototype = Object.create(Rectangle.prototype, {
constuctor: {
value: Square,
enumerable: true,
writable: true,
configurable: true
}
});
var square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); //true
console.log(square instanceof Rectangle); true
复制代码
ES6类实现
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rectangle {
//派生类指定了构造函数则必需要调用 super()
constructor(length) {
//等价于Retangle.call(this, length, length)
super(length, length);
}
//若是不使用构造函数,则当建立新的类实例时会自动调用 super() 并传入全部参数
}
var square = new Square(3);
console.log(square.getArea()); //9
console.log(square instanceof Square); //true
console.log(square instanceof Rectangle); //true
复制代码
使用super()的小贴士:
- 只可在派生类的构造函数中使用
super()
,若是尝试在非派生类(不是用extends
声明的类)或函数中使用则会致使程序抛出错误。- 在构造函数中访问
this
以前必定要调用super()
,它负责初始化this
,若是在调用super()
以前尝试访问this
会致使程序错误。- 若是不想调用
super()
,则惟一的方法是让类的构造函数返回一个对象。
类方法遮蔽 --- 派生类中的方法总会覆盖基类中的同名方法。
静态成员继承 --- 若是基类有静态成员,那么这些静态成员在派生类中也可用。
派生自表达式的类 --- 只要表达式能够解析为一个函数而且具备[[Constructor]]属性和原型,那么就能够用extends进行派生。 extends强大的功能使得类能够继承自任意类型的表达式,从而创造更多可能性。
因为能够动态肯定使用哪一个基类,于是能够建立不一样的继承方法
let SerializationMixin = {
serialize() {
return JSON.stringify(this);
}
};
let AreaMixin = {
getArea() {
return this.length * this.width;
}
};
function mixin(...mixins) {
var base = function() {};
Object.assign(base.prototype, ...mixins);
return base;
}
class Square extends mixin(AreaMixin, SerializableMixin) {
constructor(length) {
super();
this.length = length;
this.width = length;
}
}
var x = new Square(3);
console.log(x.getArea()); //9
console.log(x.serialize()); // "{'length': 3, 'width': 3}"
//若是多个mixin对象具备相同属性,那么只有最后一个被添加的属性被保留。
复制代码
内建对象的继承
class MyArray extends Array {
//空
}
var colors = new MyArray();
colors[0] = "red";
console.log(colors.length); //1
colors.length = 0;
console.log(colors[0]); //undefined
复制代码
Symbol.species
属性
内建对象继承的一个实用之处,本来在内建对象中返回实例自身的方法将自动返回派生类的实例。
Symbol.species
是诸多内部Symbol中的一个,它被用于定义返回函数的静态访问器属性。被返回的函数是一个构造函数,每当要在实例的方法中(不是在构造函数中)建立类的实例时必须使用这个构造函数。
通常来讲,只要想在类方法中调用
this.constructor
,就应该使用Symbol.species
属性,从而让派生类重写返回类型。并且若是你正从一个已定义Symbol.species
属性的类建立派生类,那么确保使用哪一个值而不是使用构造函数。
class MyArray extends Array {
static get [Symbol.species]() {
return Array;
}
}
let items = new MyArray(1, 2, 3, 4),
subitems = items.slice(1, 3);
console.log(items instanceof MyArray); //true
console.log(subitems instanceof Array); //true
console.log(subitems instanceof MyArray); //false
复制代码
new.target
在简单状况下,new.target
等于类的构造函数。
由于类必须经过new
关键字才能调用,因此在列的构造函数中,new.target
属性永远不会是undefined
。
1.1 ES6以前建立数组的方法:
1.2 ES6:
Array.of();
做用:帮助开发者们规避经过Array构造函数建立数组是的怪异行为,由于,Array构造函数表现的与传入的的参数类型及数量有些不符;
function createArray(arrayCreator, value){
return arrayCreator(value);
}
let items = createArray(Array.of, value)
复制代码
Array.from();
ES5方法将类数组转化为真正的数组:
function makeArray(arrayLike) {
var result = [];
for(var i=0, len = arrayLike.length; i<len; i++) {
result.push(arrayLike[i]);
}
return result;
}
function doSomething() {
var args = makeArray(arguments);
//使用args
}
复制代码
改进方法:
function makeArray(arrayLike) {
return Array.prototype.slice.call(arrayLike);
}
function doSomething() {
var args = makeArray(arguments);
//使用args
}
复制代码
ES6-Array.from():
function doSomething() {
var args = Array.from(arguments);
//使用args
}
复制代码
1.3 映射转换
若是想要进一步转化数组,能够提供一个映射函数做为Array.from()
的第二个参数,这个函数用来将类数组对象中的每个值转换成其余形式,最后将这些结果储存在结果数组的相应索引中。
function translate() {
return Array.from(arguments, (value) => value + 1);
}
let numbers = translate(1, 2, 3);
console.log(numbers); //2, 3, 4
复制代码
也能够传入第三个参数来表示映射函数的this值
let helper = {
diff: 1,
add(value) {
return value + this.diff;
}
};
function translate() {
return Array.from(arguments, helper.add, helper);
}
let numbers = translate(1, 2, 3);
console.log(numbers); //2, 3, 4
复制代码
用Array.from()
转换可迭代对象
let numbers = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
};
let numbers = Array.from(numbers, (value) => value + 1);
console.log(numbers2); //2, 3, 4
复制代码
若是一个对象既是类数组又是可迭代的,那么
Array.from()
方法会根据迭代器来决定转换那个值。
2.1 find()
方法和findIndex()
方法
一旦回调函数返回
true
,find()
方法和findIndex()
方法都会当即中止搜索数组剩余的部分。
适用于根据某个条件查找匹配的元素,若是只想查找与某个值匹配的元素,则indexOf()
方法和lastIndexOf()
方法是更好的选择。
2.2 fill()
方法
若是开始索引或结束索引为负值,那么这些值会与数组的length属性相加来做为最终的位置。
2.3copyWith()
方法
传入两个参数,一个是开始填充值的索引位置,另外一个是开始复制值的索引位置。(若是索引存在负值,也会与数组的length属性相加做为最终值)
3.1 定义
定型数组能够为JavaScript带来快速的换位运算。ES6采用定型数组做为语言的正式格式来确保更好的跨JavaScript引擎兼容性以及与JavaScript数组的互操做性。
所谓定型数组,就是将任何数字转换为一个包含数字比特的数组。
定型数组支持存储和操做如下8种不一样的数值类型:
全部与定型数组有关的操做和对象都集中在这8个数据类型上,可是在使用它们以前,须要建立一个 数组缓冲区 存储这些数据。
3.2 数组缓冲区
数组缓冲区是全部定型数组的根基,它是一段能够包含特定数量字节的内存地址。(相似
c
语言malloc()
分配内存)
let buffer = new ArrayBuffer(10); //分配10字节
console.log(buffer.byteLength); // 10
复制代码
数组缓冲区包含的实际字节数量在建立时就已肯定,能够修改缓冲区内的数据,可是不能改变缓冲区的尺寸大小。
DataView
类型是一种通用的数组缓冲区视图,其支持全部8种数值型数据类型。
let buffer = new ArrayBuffer(10),
view = new DataView(buffer);
复制代码
能够基于同一个数组缓冲区建立多个view
, 于是能够为应用申请一整块独立的内存地址,而不是当须要空间时再动态分配。
获取读取试图信息
读写视图信息
let buffer = new ArrayBuffer(2),
view = new DataView(buffer);
view.setInt(0, 5);
view.setInt(1, -1);
console.log(view.getInt16(0)); // 1535
console.log(view.getInt8(0)); //5
console.log(view.getInt8(1)); //-1
复制代码
定型数组是视图
3.3 定型数组与普通数组的类似之处
能够修改
length
属性来改变普通数组的大小,而定型数组的length
属性是一个不可写属性,因此不能修改定型数组的大小。
3.4 定型数组与普通数组的差异
定型数组和普通数组最重要的差异是:定型数组不是普通数组。
定型数组一样会检查数据类型的合法性,0被用于代替因此非法值。
set(): 将其它数组复制到已有的定型数组。
subarray(): 提取已有定型数组的一部分做为一个新的定型数组。
JavaScript既能够像事件和回调函数同样指定稍后执行的代码,也能够明确指示代码是否成功执行。
JavaScript引擎一次只能执行一个代码块,因此须要跟踪即将运行的代码,那些代码被放在一个任务队列中,每当一段代码准备执行时,都会被添加到任务队列。每当JavaScript引擎中的一段代码结束执行,事件循环(event loop) 会执行队列中的下一个任务,它是JavaScript引擎中的一段程序,负责监督代码执行并管理任务队列。
事件模型--->回调模式--->Promise
Promise至关于异步操做结果的占位符,它不会去订阅一个事件,也不会传递一个回调函数给目标函数,而是让函数返回一个Promise对象。like:
// readFile承诺将在将来的某个时刻完成
let promise = readFile("example.txt");
复制代码
操做完成后,Promise会进入两个状态:
Fulfilled
Promise异步操做成功完成;Rejected
因为程序错误或一些其余缘由,Promise异步操做未能成功完成。内部属性[[PromiseState]]
被用来表示Promise
的三种状态:"pending"、"fulfilled"、"rejected"。这个属性不暴露在Promise
对象上,因此不能以编程的方式检测Promise
的状态,只有当Promise
的状态改变时,经过then()
方法采起特定的行动。
若是一个对象实现了上述的
then()
方法,那这个对象咱们称之为thenable
对象。全部的Promise都是thenable
对象,但并不是全部thenable
对象都是Promise
。
then方法
catch方法(至关于只给其传入拒绝处理程序的then()方法)
// 拒绝
promise.catch(function(err)) {
console.error(err.message);
});
复制代码
与下面调用相同
promise.then(null, function(err)){
// 拒绝
console.error(error.message);
});
复制代码
Promise比事件和回调函数更好用
若是一个Promise处于已处理状态,在这以后添加到任务队列中的处理程序仍将进行。
Promise的执行器会当即执行,而后才执行后续流程中的代码:
let promise = new Promise(function(resolve, reject){
console.log("Promise");
resolve();
})
console.log("Hi!");
//Promise
//Hi!
复制代码
完成处理程序和拒绝处理程序老是在 执行器 完成后被添加到任务队列的末尾。
使用Promise.resolve()
使用Promise.reject()
若是向
Promise.resolve()
方法或Promise.reject()
方法传入一个Promise
, 那么这个Promise
会被直接返回。
非Promise
的Thenable
对象
Promise.resolve()
方法和Promise.reject()
方法均可以接受非Promise
的Thenable
对象做为参数。若是传入一个非Promise
的Thenable
对象,则这些方法会建立一个新的Promise
,并在then()
函数中被调用。
非Promise
的Thenable
对象: 拥有then()
方法而且接受resolve
和reject
这两个参数的普通对象。
若是不肯定某个对象是否是Promise
对象,那么能够根据预期的结果将其传入Promise.resolve()
方法中或Promise.object()
方法中,若是它是Promise
对象,则不会有任何变化。
每一个执行器都隐含一个try-catch
块,因此错误会被捕获并传入拒绝处理程序。
Promise
拒绝处理有关Promise
的其中一个 最具争议 的问题是,若是在没有拒绝处理程序的状况下拒绝一个Promise
,那么不会提示失败信息。
6.1 Node.js环境的拒绝处理
unhandledRejection
在一个事件循环中,当Promise
被拒绝,而且没有提供拒绝处理程序时,触发该事件。
let rejected;
process.on("unhandledRejection", function(reason, promise){
console.log(reason.message); // "Explosion!"
console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("Explosion!"));
复制代码
rejectionHandled
在一个事件循环以后,当Promise
被拒绝时,若拒绝处理程序被调用,触发该事件。
let rejected;
process.on("rejectionHandled", function(promise){
console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("Explosion!"));
//等待添加拒绝处理程序
setTimeout(function(){
rejected.catch(function(value){
console.log(value.message); // "Explosion!"
});
}, 1000);
复制代码
6.2 浏览器环境 的拒绝处理
unhandledRejection
(描述与Node.js相同)
rejectionHandled
浏览器中的实现与
Node.js
中的几乎彻底相同,两者都是用一样的方法将Promise
及其拒绝值存储在Map集合中,而后再进行检索。惟一的区别是,在事件处理程序中检索信息的位置不一样。
Promise
每次调用
then()
方法或catch()
方法时实际上建立并返回了另外一个Promise
,只有当第一个Promise
完成或被拒绝后,第二个才会被解决。
务必在
Promise
链的末尾留有一个拒绝处理程序以确保可以正确处理全部可能发生的错误。
拒绝处理程序中返回的值仍可用在下一个
Promise
的完成处理程序中,在必要时,即便其中一个Promise
失败也能恢复整条链的执行。
Promise
中返回Promise
在完成或拒绝处理程序中返回Thenable
对象不会改变Promise
执行器的执行动机,先定义的Promise
的执行器先执行,后定义的后执行。
Promise
Promise.All()
方法Promise.race()
方法Promise
继承Promise与其余内建类型同样,也能够做为基类派生其余类,因此你能够定义本身的Promise
变量来扩展内建Promise
的功能。
Promise
的异步任务执行let fs = require("fs");
function run(taskDef) {
//建立迭代器
let task = taskDef();
//开始执行任务
let result = task.next();
//递归函数遍历
(function step() {
//若是有更多任务要作
if(!result.done) {
//用一个Promise来解决会简化问题
let promise = Promise.resolve(result.value);
promise.then(function(value) {
result = task.next(value);
step();
}).catch(function(error){
result = task.throw(error);
step();
})
}
}());
}
//定义一个可用于任务执行器的函数
function readFile(filename) {
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, contents){
if(err){
reject(err);
}else{
resolve(contents);
}
});
});
}
//执行一个任务
run(function*(){
let contents = yield readFile("config.json");
doSomethingWith(contents);
console.log("done");
})
复制代码
ES2017 await
代理
(Proxy)
是一种能够拦截并改变底层JavaScript引擎操做的包装器,在新语言中经过它暴露内部运做的对象。
调用 new Proxy()
可建立代替其余目标对象的代理,它虚拟化了目标,因此两者看起来功能一致。
代理能够拦截 JavaScript
引擎内部目标的底层对象操做,这些底层操做被拦截后会触发响应特定操做的陷阱函数。
反射API
以Reflect
对象的形式出现,对象中方法的默认特性与相同的底层操做一致,而代理能够覆写这些操做,每一个代理陷阱对应一个命名和参数都相同的Reflect
方法。
set
陷阱验证属性 / 用get
陷阱验证对象解构(Object Shape)
set
代理陷阱能够拦截写入属性的操做,get
代理陷阱能够拦截读取属性的操做。
let target = {
name: "target"
}
let proxy = new Proxy(target, {
set(trapTarget, key, value, receiver) {
//忽略不但愿受到影响的已有属性
if(!trapTarget.hasOwnProperty(key)) {
if(isNaN(value)) {
throw new TypeError("属性必须是数字");
}
}
//添加属性
return Reflect.set(trapTarget, key, value, receiver);
}
});
//添加一个新属性
proxy.count = 1;
console.log(proxy.count); //1
console.log(target.count); //1
//因为目标已有name属性于是能够给它赋值
proxy.name = "proxy";
console.log(proxy.name); //"proxy"
console.log(target.name); //"proxy"
复制代码
let proxy = new Proxy({},{
get(trapTarget, key, receiver) {
if (!(key in receiver)) {
throw new TypeError("属性" + key + "不存在");
}
return Reflect.get(trapTarget, key, receiver);
}
});
//添加一个属性,程序仍正常运行
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
//若是属性不存在,则抛出错误
console.log(proxy.nme); // 抛出错误
复制代码
has
陷阱隐藏已有属性deleteProperty
陷阱防止删除属性原型代理陷阱有一些限制:
getPrototypeOf
陷阱必须返回对象或null
,只要返回值必将致使运行时错误,返回值检查能够确保Object.getPropertyOf()
返回的老是预期的值;setPropertyOf
陷阱中,若是操做失败则返回的必定是false
,此时Object.setPrototypeOf()
会抛出错误,若是setPrototypeOf
返回了任何不是false
的值,那么Object.setPrototypeOf()
便假设操做成功。Object.getPrototypeOf()
和Object.setPrototypeOf()
是高级操做,建立伊始便给开发者使用的;而Reflect.getPrototypeOf()
和Reflect.setPrototypeOf()
方法则是底层操做,其赋予开发者能够访问以前只在内部操做的[[GetPrototypeOf]]
和[[SetPrototypeOf]]
的权限。
Object.setPrototypeOf()
和Reflect.setPrototypeOf()
之间在返回值上有微妙的差别,前者返回传入的对象,后者返回布尔值。
preventExtensions
(阻止扩展)isExtensible
(判断是否可扩展)相比高级功能方法而言,底层的具备更严格的错误检查。
defineProperty
(定义属性)getOwnPropertyDescriptor
(获取属性)给Object.defineProperty()添加限制
若是让陷阱返回
true
而且不调用Reflect.defineProperty()
方法,则可让Object.definePropperty()
方法静默失效,这既消除了错误又不会真正定义属性。
描述符对象限制
当
defineProperty
陷阱被调用时,descriptor
对象有value
属性却没有name
属性,这是由于descriptor
不是实际传入Object.defineProperty()
方法的第三个参数的引用,而是一个只包含那些被容许使用的属性的新对象。Reflect.defineProperty()
方法一样也忽略了描述符上的全部非标准属性。
ownKeys
陷阱
ownKeys
陷阱经过Reflect.ownKeys()
方法实现默认的行为,返回的数组中包含全部自有属性的键名,字符串类型和Symbol
类型的都包含在内。
Object.getOwnPropertyNames()
方法和Object.keys()
方法返回的结果将Symbol
类型的属性名排除在外。Object.getOwnPropertySymbols()
方法返回的结果将字符串类型的属性名排除在外。Object.assign()
方法支持字符串和Symbol
两种类型。apply
和construct
陷阱全部的代理陷阱中,只有
apply
和construct
的代理目标是一个函数。
new
调用构造函数能够经过检查new target
的值来肯定函数是不是经过new
来调用的。
假设Numbers()
函数定义在你没法修改的代码中,你知道代码依赖new target
,但愿函数避免检查却仍想调用函数。在这种状况下,用new
调用时的行为已被设定,因此你只能使用apply
陷阱。
length
的值length
的值来删除元素想要建立使用代理的类,最简单的方法是像往常同样定义类,而后在构造函数中返回一个代理,那样的话,当类实例化时返回的对象是代理而不是实例(构造函数中的this
是该实例)。
将代理用做原型
虽然从类构造函数返回代理很容易,但这也意味着每建立一个实例都要建立一个新代理。然而有一种方法可让全部的实例共享一个代理:将代理用做原型。
模块是自动运行在严格模式下而且没有办法退出运行的
Javascript
代码。
注:在模块的顶部,this
的值是undefined
;模块不支持HTML风格的代码注释。
绑定的微妙怪异之处
export var name = "xszi";
export function setName(newName) {
name = newName;
}
//导入以后
import { name, setName } from "./example.js";
console.log(name); //xszi
setName("waltz");
console.log(name); //waltz
name = "hahha"; //抛出错误
复制代码
导入和导出重命名
模块的默认值
只能为每一个模块设置一个默认的导出值,导出时屡次使用default
关键字是一个语法错误。
用逗号将默认的本地名称与大括号包裹的非默认值分隔开,请记住,在import
语句中,默认值必须排在非默认值以前。
从新导出一个绑定
无绑定导入
即便没有任何导出或导入的操做,这也是一个有效的模块。
无绑定导入最有可能被应用与建立Pilyfill
和Shim
Shim
: 是一个库,它将一个新的API引入到一个旧的环境中,并且仅靠旧环境中已有的手段实现。
Polyfill
: 一个用在浏览器API上的Shim
,咱们一般的作法是先检查当前浏览器是否支持某个API,若是不支持的话就加载对用的polyfill
。
把旧的浏览器想一想成一面有裂缝的墙,这些
polyfill
会帮助咱们把这面墙的裂缝填平。
在web浏览器中使用模块
//加载一个JavaScript模块文件
<script type="module" src="module.js"></script>
复制代码
//内联引入模块
<script type="module">
import { sum } from "./example.js";
let result = sum(1, 2)
</script>
复制代码
模块与脚本不一样,它是独一无二的,能够经过import
关键字来指明其所依赖的其余文件,而且这些文件必须被加载进该模块才能正确执行。为了支持该功能,<script type="module">
执行时自动应用defer
属性。
每一个模块均可以从一个或多个其余的模块导入,这会使问题复杂化。所以,首先解析模块以识别全部导入语句;而后,每一个导入语句都触发一次获取过程(从网络或从缓存),而且在全部导入资源都被加载和执行后才会执行当前模块。
//没法保证这两个哪一个先执行
<script type="module" async src="module1.js"></script>
<script type="module" async src="module2.js"></script>
复制代码
将模块做为Worker加载
Worker
能够在网页上下文以外执行JavaScript
代码。
//按照脚本的方式加载script.js
let worker = new Worker("script.js");
复制代码
//按照模块的方式加载module.js
let worker = new Worker("module.js", {type: "module"});
复制代码
IEEE 754
只能准确的表示-2^53 ~ 2^53之间的整数:
var inside = Number.MAX_SAFE_INTEGER,
outside = inside + 1;
console.log(Number.isInteger(inside)); //true
console.log(Number.isSafeInteger(inside)); //true
console.log(Number.isInteger(outside)); //true
console.log(Number.isSafeInteger(outside)); //false
复制代码
Math
方法提升一般的数学计算的速度
Unicode
标识符_ptoto_
属性实际上,_proto_
是Object.getPrototypeOf()
方法和Object.setPrototypeOf()
方法的早期实现。
5 ** 2 == Math.pow(5, 2); //true
复制代码
求幂运算符在JavaScript全部二进制运算符中具备最高的优先级(一元运算符的优先级高于**)
奇怪之处:
includes()
方法认为+0和-0是相等的,Object.is()
方法会将+0和-0识别为不一样的值。
ECMAScript 2016
规定在参数被解构或有默认参数的函数中禁止使用"use strict"
指令。只有参数为不包含解构或默认值的简单参数列表才能够在函数体中使用"use strict"
。