[TOC]

数据类型

综述

· Number(数字)
· String(字符串)
· Boolean(布尔)
· Symbol(符号)(第六版新增)
· Object(对象)
· Function(函数)
· Array(数组)
· Date(日期)
· RegExp(正则表达式)
· Null(空)
· Undefined(未定义)

用var声明变量, 重复声明不改变变量的值.
如果把值赋给尚未声明的变量,该变量将被自动作为全局变量声明

JavaScript 变量均为对象。当您声明一个变量时,就创建了一个新的对象。

有两种方式可以访问对象属性: .property 或 [“property”]。

如果把数字与字符串相加,结果将成为字符串

· NaN 的数据类型是 number
· 数组(Array)的数据类型是 object
· 日期(Date)的数据类型为 object
· null 的数据类型是 object
· 未定义变量的数据类型为 undefined

switch…case 语句里使用的是 === , 类型和值都等才等
同一个switch里可以case不同的数据类型

变量声明

var、let、const
早期的JavaScript中,声明变量只能使用var关键字定义变量
通过var关键字定义的变量,其作用域只能函数级或是全局作用域,并没有块级作用域
ES6(ECMAScript 2015)对这一问题做了改善,增加了用于定义块级变量的let关键字和用于定义常量的const关键字。


var
变量声明提升
无论在作用域的任何位置声明变量,执行引擎都会在任何代码执行之前处理。
由于这个原因,所以在代码中的任意位置声明变量与在代码开头声明变量是等效的。

在JavaScript中,当变量被声明时,声明会被提升到它所在函数的顶部,并被赋予undefined值。
这就使得在函数的任意位置声明的变量存在于整个函数中,尽管在赋值之前,它的值一直为undefined

JavaScript进入作用域时, 会对代码进行两轮处理, 第一轮, 初始化变量, 第二轮, 执行代码
<单页Web引用>page28
JavaScript - 图1
JavaScript - 图2
function t() {
console.log(foo); // undefined, 但是不会抛未定义错误
var foo = ‘foo’;
console.log(foo); // foo
}
第一轮声明了变量, 第二轮再到声明的位置时才会初始化变量


声明与未声名变量的区别
未声明的变量会被添加一个全局作用域,而声明变量作用域是当前上下文
严格模式下全局变量必须显式声明
全局作用域与函数作用域
函数内部声明的变量只能在函数内部使用,函数外部声明的变量可以全局使用

let
var声明的变量是函数级的或者是全局的,而let用于声明块级作用域。

var的异同
let与var的区别主要体现在作用域上,
当在子代码块中使用中let声明的是块级变量,var声明的是全局变量
二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数。
在函数或程序顶层使用时,let与var没有什么区别
在程序或者函数的顶层,let并不会像var一样在全局对象上创造一个属性,比如:
var x = ‘global’;
let y = ‘global’;
console.log(this.x); // “global”
console.log(this.y); // undefined
~~ ~~
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let


const
const用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改

const定义常量与使用let定义变量十分相似:

  • 二者都是块级作用域
  • 都不能和它所在作用域内的其他变量或函数拥有相同的名称

两者还有以下两点区别:

  • const声明的常量必须初始化,而let声明的变量不用
  • 常量的值不能通过再赋值改变,也不能再次声明。而变量值可以修改

    作用域与闭包

    作用域

    一个函数可以访问定义在其范围内的任何变量和函数
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions
    注意是”定义”的地方


    作用域,词法作用域 :《你不知道的 javascript 上卷》 1-2 章,编译和词法作用域;
    执行上下文、活动对象 AO、变量对象 VO、作用域:《 javascript 高级程序设计》 第四章
    变量对象 ,[[scope]]:《 javascript 高级程序设计》 7.2 闭包


    [[Scope]]是一个包含了所有上层变量对象的分层链,它属于当前函数上下文,并在函数创建的时候,保存在函数中。
    在源代码中当你定义(书写)一个函数的时候(并未调用),
    js引擎根据你函数书写的位置,函数嵌套的位置,给你生成一个[[scope]], 保存所有父变量对象到其中,作为该函数的属性存在(这个属性属于函数的)
    所以叫词法作用域(静态作用域)

    执行环境EC (Execution Context) 或者叫执行上下文
    EC建立分为两个阶段:进入执行上下文和执行阶段。
    进入上下文阶段:发生在函数调用时,但是在执行具体代码之前(比如,对函数参数进行具体化之前)
    执行代码阶段:变量赋值,函数引用,执行其他代码。

    可以将EC看做是一个对象。
    EC={
    VO:{/ 函数中的arguments对象, 参数, 内部的变量以及函数声明 /},
    this:{},
    Scope:{ / VO以及所有父执行上下文中的VO /}
    }
    此时,执行上下文里的scope和之前属于函数的那个[[scope]]不是同一个,
    执行上下文里的scope,是在之前函数的[[scope]]的基础上,又新增一个当前的AO对象构成的。
    Scope = [AO].concat([[Scope]]);
    Scope是EC的属性,而[[scope]]则是函数的静态属性


    进入执行上下文时,VO的初始化过程具体如下, 注意:该过程是有先后顺序的。
    函数的形参(当进入函数执行上下文时)
    —— 变量对象的一个属性,其属性名就是形参的名字,其值就是实参的值;对于没有传递的参数,其值为undefined
    函数声明(FunctionDeclaration, FD)
    —— 变量对象的一个属性,其属性名和值都是函数对象创建出来的;如果变量对象已经包含了相同名字的属性,则替换它的值
    变量声明(var,VariableDeclaration)
    —— 变量对象的一个属性,其属性名即为变量名,其值为undefined; 如果变量名和已经声明的函数名或者函数的参数名相同,则不会影响已经存在的属性。

    当EC环境为函数时,我们访问的是AO,而不是VO
    AO是在进入函数的执行上下文时创建的,并为该对象初始化一个arguments属性
    AO = {
    arguments: {
    callee:,
    length:,
    properties-indexes: //函数传参参数值
    }
    };

    执行代码阶段时,VO中的一些属性undefined值将会确定

    EC分为三种:
    全局级别的代码 – 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。
    函数级别的代码 – 当执行一个函数时,运行函数体中的代码。
    Eval的代码 – 在Eval函数内运行的代码。代码eval的上下文与当前的调用上下文(calling context)拥有同样的作用域链.


    VO(变量对象,Variable Object)
    AO(活动对象,Active Object)
    变量对象 :就是执行环境中包含了所有变量和函数的对象。解析器在后台使用它,保存在内存中,代码无法直接访问。后台的每个执行环境都有一个表示变量的对象—-变量对象。
    活动对象: 当执行环境是函数时,将其活动对象作为变量对象。
    当某个函数被调用时,会创建一个执行环境( execution context )及相应的作用域链。
    然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象( activation object )
    作用域链:本质上是一个指向 变量对象 的指针列表,只引用 变量对象 。
    JavaScript - 图3


    在ECMAScript 中,在代码执行阶段有两个声明能修改作用域链。这就是with声明和catch语句。
    它们添加到作用域链的最前端,对象须在这些声明中出现的标识符中查找。
    如果发生其中的一个,作用域链简要的作如下修改:
    Scope = withObject|catchObject + AO|VO + [[Scope]]

ECS(执行环境栈Execution Context Stack)
ECStack管理EC的压栈和出栈
栈底总是全局上下文,栈顶是当前(活动的)执行上下文。
当在不同的执行上下文间切换(退出的而进入新的执行上下文)的时候,栈会被修改(通过压栈或者退栈的形式)
每次控制器进入一个函数(哪怕该函数被递归调用或者作为构造器),都会发生压栈的操作


参考链接
知识点整理 https://segmentfault.com/a/1190000000533094
一个完整过程的栗子 https://github.com/mqyqingfeng/Blog/issues/6
书籍中的知识点位置 https://www.v2ex.com/t/438395
with/catch/eval等细节 https://www.cnblogs.com/TomXu/archive/2012/01/18/2312463.html


复习题
function t(age) {
console.log(age); // [Function: age]
var age = 99;
console.log(age); // 99
function age() {
}
console.log(age); // 99
}
t(5);
原因见上面的VO初始化过程

a(); // undefined
b(); // undefined
function b() { var x = 5; a(); }
a(); // undefined
b(); // undefined
function a() { console.log(x); }
a(); // undefined
b(); // undefined
var x = 10;
a(); // 10
b(); // 10
函数a的作用域链不包含x=5, 因为a不是在b内创建的

闭包


闭包是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。
闭包是阻止垃圾回收器将变量从内存中移除的方法,使得在创建变量的执行环境的外面能够访问到该变量。
<单页Web应用>page49

栗子
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();

add();
add();
add();
// 计数器为 3


栗子 <单页Web应用>page51
JavaScript - 图4

对象

概念

http://www.runoob.com/jsref/jsref-tutorial.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects


对象拥有属性方法
JavaScript 是面向对象的语言,但 JavaScript 不使用类。
JavaScript 基于 prototype,而不是基于类的。
prototype是JavaScript全局构造函数。它可以构建新Javascript对象的属性和方法。
Array.prototype.myUcase=function(){ for (i=0;i this[i]=this[i].toUpperCase();
}
}

创建对象

l 对象初始化器
var obj = { property1: value_1, // property# 可以是一个标识符…
2: value_2, // 或一个数字…
[“property” +3]: value_3, // 或一个可计算的key名…
// …,
“property n”: value_n }; // 或一个字符串

l 构造器
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car(“Eagle”, “Talon TSi”, 1993);

l Object.create()

l class关键字

JavaScript 仍然基于原型
新的关键字包括 class, constructorstaticextendssuper


class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
}

class Square extends Polygon {
constructor(sideLength) {
super(sideLength, sideLength);
}
get area() {
return this.height * this.width;
}
set sideLength(newLength) {
this.height = newLength;
this.width = newLength;
}
}


super([arguments]);
// 调用 父对象/父类 的构造函数

super.functionOnParent([arguments]);
// 调用 父对象/父类 上的方法

new运算符

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new

**new** 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。**new** 关键字会进行如下的操作

创建一个空的简单JavaScript对象(即{});
链接该对象到另一个对象(即设置该对象的构造函数);
将步骤1新创建的对象作为this的上下文 ;
如果该函数没有返回对象,则返回this

1. Creates a blank, plain JavaScript object;
2. Links (sets the constructor of) this object to another object;
3. Passes the newly created object from Step 1 as the this context;
4. Returns this if the function doesn’t return its own object.


它创建了一个全新的对象。
它会被执行 [[Prototype]](也就是 proto)链接。
它使 this指向新创建的对象。。
通过 new创建的每个对象将最终被 [[Prototype]]链接到这个函数的 prototype对象上。
如果函数没有返回对象类型 Object(包含 Functoin,Array,Date,RegExg,Error),那么 new表达式中的函数调用将返回该对象引用。
function New (func) {
var res = {}
if (func.prototype !== null) {
res.proto = func.prototype
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1))
if ((typeof ret === ‘object’ || typeof ret === ‘function’) && ret !== null) {
return ret
}
return res
}

var obj = New(A, 1, 2)
// equals to
var obj = new A(1, 2);


继承与原型链

Inheritance and the prototype chain
JavaScript - 图5

JavaScript 中的函数永远有一个默认原型属性 prototype
JavaScript - 图6

遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指向 someObject 的原型。
从 ECMAScript 6 开始,[[Prototype]] 可以通过 Object.getPrototypeOf()Object.setPrototypeOf() 访问器来访问。
这个等同于 JavaScript 的非标准但许多浏览器实现的属性 proto
但它不应该与构造函数 func 的 prototype 属性func.prototype相混淆。
被构造函数创建的实例对象的 [[prototype]] 指向 func 的 prototype 属性。Object.prototype 属性表示 Object的原型对象。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

Object.prototypeproto 属性是一个访问器属性(一个getter函数和一个setter函数), 暴露了通过它访问的对象的内部[[Prototype]] (一个对象或 null)
使用proto是有争议的,也不鼓励使用它。
因为它从来没有被包括在EcmaScript语言规范中,但是现代浏览器都实现了它。
proto属性已在ECMAScript 6语言规范中标准化,用于确保Web浏览器的兼容性,因此它未来将被支持。
它已被不推荐使用, 现在更推荐使用Object.getPrototypeOf/Reflect.getPrototypeOfObject.setPrototypeOf/Reflect.setPrototypeOf
(尽管如此,设置对象的[[Prototype]]是一个缓慢的操作,如果性能是一个问题,应该避免)。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

Object.prototype.constructor
返回创建实例对象的 Object 构造函数的引用。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
对原始类型来说,如1true"test",该值只可读。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor

属性的遍历

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 键,按照加入时间升序排列。

不可变对象

**Object.freeze()** 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。

不可变对象实现原理: trie树
https://zhuanlan.zhihu.com/p/63207283

ES6语法

http://es6.ruanyifeng.com/#docs/object

Object.is()
与严格比较运算符(===)的行为基本一致
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

Object.assign()
对象合并, 第一个参数是目标对象
进行的是浅拷贝
同名属性会被替换
数组也被视作对象处理,属性名为0,1,2.. 所以同index被替换
只进行值的替换, 所以取值函数会求值后再替换
undefined和null不能作为参数
其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果(被忽略)。

可枚举性

letobj=`{ foo: 123 `};

Object.getOwnPropertyDescriptor(obj, 'foo')

// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
// }

目前,有四个操作会忽略enumerablefalse的属性。
for...in循环:只遍历对象自身的和继承的可枚举的属性。
Object.keys():返回对象自身的所有可枚举的属性的键名。
JSON.stringify():只串行化对象自身的可枚举的属性。
Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。


prototype

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf

Object.getPrototypeOf()
Object.setPrototypeOf()

super


Object.keys(),Object.values(),Object.entries()

Object.entries只输出属性名非 Symbol 值的属性。

l 遍历对象属性
let obj = { one: 1, two: 2 };
for (let [k, v] of Object.entries(obj)) {
console.log(
${JSON.stringify(k)}: ${JSON.stringify(v)}
);
}

l 将对象转为Map结构
const obj = { foo: ‘bar’, baz: 42 };
const map = new Map(Object.entries(obj));


解构赋值

http://es6.ruanyifeng.com/#docs/object#%E8%A7%A3%E6%9E%84%E8%B5%8B%E5%80%BC

浅克隆

扩展运算符的解构赋值,不能复制继承自原型对象的属性 (单纯的解构赋值可以)
const o = Object.create({ x: 1, y: 2 });
o.z = 3;

let { x, …newObj } = o;
let { y, z } = newObj;
x // 1
y // undefined
z // 3

默认值
var o = { a: 1 }
var { a = 222, c = 111 } = o // a:1, c:111


运算符

按位或~

https://segmentfault.com/a/1190000003731938

位运算 NOT 由否定号(~)表示,它是 ECMAScript 中为数不多的与二进制算术有关的运算符之一。
位运算 NOT 是三步的处理过程:
把运算数转换成 32 位数字
把二进制数转换成它的二进制反码(0->1, 1->0)
把二进制数转换成浮点数
简单的理解,对任一数值 x 进行按位非操作的结果为 -(x + 1)

~value的使用
判断数值中是否有某元素时,以前这样判断:
if(arr.indexOf(ele) > -1){…} //易读
现在可以这样判断,两者效率:
if(~arr.indexOf(ele)){…} //简洁

~~value的使用
对于浮点数,value可以代替parseInt(value),而且前者效率更高些
parseInt(-2.99) //-2
(-2.99) //-2

函数

注意

函数定义会提升
函数表达式不会提升

箭头函数

不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

函数内没有this,所以其内使用this时, 实际是定义时箭头函数外部的this. 所以不能并call/apply/bind绑定this.
箭头函数的this,总是指向定义时所在的对象,而不是运行时所在的对象。
function foo(){
setTimeout(() => {
console.log(“id:”, this.id)
}, 100);
}

foo.call({id:42});
这个例子中,箭头函数位于foo函数内部。只有foo函数运行后,它才会按照定义生成,所以foo运行时所在的对象,恰好是箭头函数定义时所在的对象
function foo() {
return () => {
return () => {
return () => {
console.log(“id:”, this.id);
};
};
};
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()();
var t2 = f().call({id: 3})();
var t3 = f()().call({id: 4});
https://github.com/ruanyf/es6tutorial/issues/150


以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:argumentssupernew.target
http://es6.ruanyifeng.com/#docs/function#%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0

其他

递归比较浪费资源
尾调用 和 尾递归 都只有一个调用帧, 相对节省内存


函数, 数组, 对象 都允许有尾逗号
function clownsEverywhere(
param1,
param2,
) { // }

clownsEverywhere(
‘foo’,
‘bar’,
);

链接

Function
l 属性
Function.arguments
Function.arity
Function.caller
Function.displayName
Function.length
Function.name
Function.prototype
l 方法
Function.prototype.apply()
Function.prototype.bind()
Function.prototype.call()
Function.prototype.isGenerator()
Function.prototype.toSource()
Function.prototype.toString()

数组

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array

注意: 作为函数参数的数组可以在函数内被修改(类似引用传递)

创建

1: 常规方式:
var myCars=new Array();
myCars[0]=”Saab”;
myCars[1]=”Volvo”;
myCars[2]=”BMW”;
2: 简洁方式:
var myCars=new Array(“Saab”,”Volvo”,”BMW”);
3: 字面:
var myCars=[“Saab”,”Volvo”,”BMW”];

可以在数组中有不同的变量类型

方法

改变原数组
reverse()
sort()
splice()
pop()
push()
shift()
unshift()

不改变原数组
concat()
jion()
slice()
map reduce filter


shift( )
注意shift()会改变arr的长度
function dropElements(arr, func) {
function dropElements(arr, func) {
var len = arr.length;
for (var iter = 0; iter < len; ++iter) {
// for (var iter = 0; iter < arr.length; ++iter) { //此写法有误
if (!func(arr[0])) {
arr.shift();
} else {
break;
}
}
return arr;
}


unshift( )
map()
返回一个新数组, 数组中的元素为原始数组元素调用函数处理后的值
var oldArray = [1, 2, 3, 4, 5];
var newArray = oldArray.map(function(val) {
return val + 3;
})

reduce()
var array = [4, 5, 6, 7, 8];
var singleVal = 0;
singleVal = array.reduce(function(prev, curr) {
return prev + curr;
}, 0);

previousVal 保存每次操作后的值
currentVal 为数组的下一个值
reduce() 的第二个参数可选, 用来设置previousVal的默认值

filter()
var oldArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var newArray = oldArray.filter(function(val) {
return val < 6;
});
callback返回为true的值都会被保留
注意: filter( )原数组不会被改变
var arr = [1, 2, 3, 3, 3, 4];

function func(arr) {
arr = arr.filter(function(val) {
return val > 2;
});
return arr;
}
func(arr);
console.log(arr); //arr不会被修改

arr = arr.filter(function(val) {
return val > 2;
});
console.log(arr); //arr会被修改


sort()
var array = [1, 12, 21, 2];
array.sort(function(a, b) {
return a - b;
});
callback返回负值则a在前
若callback为空则自动转换为字符串比较

reverse()
var array = [1, 2, 3, 4, 5, 6, 7];
var newArray = [];
newArray = array.reverse();
console.log(array);
console.log(newArray);
reverse( )会改变原array, 同时返回值也是改过后的array.

concat( )
var oldArray = [1, 2, 3];
var newArray = [];
var concatMe = [4, 5, 6];
newArray = oldArray.concat(concatMe);
不改变原数组

join( )
var joinMe = [“Split”, “me”, “into”, “an”, “array”];
var joinedString = ‘’;
joinedString = joinMe.join(‘ ‘);
var str2 = joinMe.join(“ and “);

slice(start, end)
说明: [start,end)

arrayObject .splice(start, howmany, item1, item2 …)
splice会改变原数组
如果从 arrayObject 中删除了元素,则返回的是含有被删除的元素的数组。
forEach( )

新建指定长度数组, 并填充值
Array(30).fill(4)
Array.apply(null, Array(30)).map(() => 4)
不能Array(30).map(() => 4)
使用 new Array(arrayLength) 方式构造的数组是一个稀疏数组,里面是没有任何值的,只有长度。所以这个方式构造出来的数组是无法遍历的,也就无法用 map 遍历填充值了。

遍历

使用 for-in 来访问数组
for (i in myObj.sites) { x += myObj.sites[i] + “
“; }
使用 for 循环访问数组
for (i = 0; i < myObj.sites.length; i++) { x += myObj.sites[i] + “
“; }
修改数组值
myObj.sites[1] = “Github”;
删除数组元素
delete myObj.sites[1];

关于sort()与数组乱序

来源链接: https://github.com/mqyqingfeng/Blog/issues/51

一种有问题的乱序写法
values.sort(function () {
return Math.random() - 0.5;
});

ECMAScript 只规定了效果,没有规定实现的方式,所以不同浏览器实现的方式不一样
v8 在处理 sort 方法时,当目标数组长度小于 10 时,使用插入排序;反之,使用快速排序和插入排序的混合排序

插入排序中, j一旦确定就不再继续遍历, 导致最终乱序不充分
function InsertionSort(a, from, to) {
for (var i = from + 1; i < to; i++) {
var element = a[i];
for (var j = i - 1; j >= from; j—) {
var tmp = a[j];
var order = comparefn(tmp, element);
if (order > 0) {
a[j + 1] = tmp;
} else {
break;
}
}
a[j + 1] = element;
}
};

用Fisher–Yates实现真正的乱序
// Fisher–Yates
function shuffle(a) {
for (let i = a.length; i; i—) {
let j = Math.floor(Math.random() * i);
[a[i - 1], a[j]] = [a[j], a[i - 1]];
}
return a;
}


ES6语法

http://es6.ruanyifeng.com/#docs/array

扩展运算符(spread)

三个点(...
扩展运算符内部调用的是数据结构的 Iterator 接口,
任何有 Iterator 接口的对象,都可以用扩展运算符转为真正的数组, 比如 Map 结构, Generator 函数, 字符串

console.log(…[1, 2, 3])

可以用于复制数组:
const a1 = [1, 2];
// 写法一
const a2 = […a1];
// 写法二
const […a2] = a1;


合并数组
// ES6的合并数组
[…arr1, …arr2, …arr3]

结合结构赋值
const [first, …rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]

const [first, …rest] = [];
first // undefined
rest // []

const [first, …rest] = [“foo”];
first // “foo”
rest // []

l 字符串
扩展运算符… 可以正确处理4字节Unicode
[…’hello’]
‘x\uD83D\uDE80y’.length // 4
[…’x\uD83D\uDE80y’].length // 3

let str = ‘x\uD83D\uDE80y’;

str.split(‘’).reverse().join(‘’)
// ‘y\uDE80\uD83Dx’

[…str].reverse().join(‘’)
// ‘y\uD83D\uDE80x’

Array.from()

任何有length属性的对象,都可以通过Array.from方法转为数组
Array.from({ length: 3 });
// [ undefined, undefined, undefined ]

Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from(arrayLike, x => x x);
// 等同于
Array.from(arrayLike).map(x => x
x);

Array.from的第三个参数,用来绑定this

Array.of()

用于将一组值,转换为数组。
Array.of基本上可以用来替代Array()new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

Array()的情况:
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

find() 和 findIndex()

find()的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
find()接受第二个参数,用来绑定回调函数的this对象。
function f(v){
return v > this.age;
}
let person = {name: ‘John’, age: 20};
[10, 12, 26, 15].find(f, person); // 26


findIndex()使用方法类似, 返回位置, 没找到则返回-1


这两个函数弥补了indexOf的不足:
[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0

some()

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some
some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。

includes()

数组是否包含元素
对NaN有效

fill()

[‘a’, ‘b’, ‘c’].fill(7)
// [7, 7, 7]

new Array(3).fill(7)
// [7, 7, 7]

fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
区间为左闭右开 [ a, b )

copyWithin()

它接受三个参数。
· target(必需):从该位置开始替换数据。如果为负值,表示倒数。
· start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
· end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示

entries(),keys() 和 values()

用于遍历数组元素
for (let index of [‘a’, ‘b’].keys()) {
console.log(index);
}
// 0
// 1

for (let elem of [‘a’, ‘b’].values()) {
console.log(elem);
}
// ‘a’
// ‘b’

for (let [index, elem] of [‘a’, ‘b’].entries()) {
console.log(index, elem);
}
// 0 “a”
// 1 “b”

数组的空位

ES5 :
· forEach(), filter(), reduce(), every() 和some()都会跳过空位。
· map()会跳过空位,但会保留这个值
· join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
ES6 :
ES6 明确将空位转为undefined
Array.from方法会将数组的空位,转为undefined
扩展运算符(...)也会将空位转为undefined
fill()会将空位视为正常的数组位置。
for...of循环也会遍历空位。
entries()keys()values()find()findIndex()会将空位处理成undefined

由于空位的处理规则非常不统一,所以建议避免出现空位。

数值

概述

JavaScript 只有一种数字类型。
在JavaScript中,数字不分为整数类型和浮点型类型,所有的数字都是由 浮点型类型。JavaScript采用IEEE754标准定义的64位浮点格式表示数字,它能表示最大值为±1.7976931348623157 x 10 308,最小值为±5 x 10 -324

var pi=3.14; // 使用小数点
var x=34; // 不使用小数点
var z=123e-5; // 0.00123

整数(不使用小数点或指数计数法)最多为 15 位。
小数的最大位数是 17,但是浮点运算并不总是 100% 准确:
0.2 + 0.1 = 0.30000000000000004
可以通过乘以10或除以10来计算: 0.3

浮点数计算问题详见:
https://segmentfault.com/q/1010000000670650
https://zh.wikipedia.org/wiki/IEEE_754


var y = 0377; //八进制var z = 0xFF; //十六进制

可以使用 toString() 方法 输出16进制、8进制、2进制。
var myNumber=128;myNumber.toString(16); // 返回 80myNumber.toString(8); // 返回 200myNumber.toString(2); // 返回 10000000

Infinity
无穷大, 值溢出后则为无穷大
除以0也产生无限Infinity
无穷大是一个数字, isNaN(Infinity)为false

数字可以是数字或对象
var x = 123;var y = new Number(123);
typeof(x) // 返回 Numbertypeof(y) // 返回 Object




var obj = {
16: “sixteen”
}
console.log(obj[16]);
obj[17] = “seventeen”;
obj.eighteen = 18;
console.log(obj);
delete obj[16];
console.log(obj);

JavaScript - 图7

function phoneticLookup(val) {
var result = “”;
lookup = {
“alpha”: “Adams”,
“bravo”: “Boston”,
“charlie”: “Chicago”,
“delta”: “Denver”,
“echo”: “Easy”,
“foxtrot”: “Frank”,
}

result = lookup[val];
// result = lookup.val; //返回undefined
return result;
}

// Change this value to test
ret = phoneticLookup(“charlie”);
console.log(ret);


parseInt(str,进制)
function binaryAgent(str) {
var arr = str.split(“ “).map(function(val) {
return String.fromCharCode(parseInt(val, 2));
});
return arr.join(“”);
}

function toBinaryStr(str) {
var arr = [];
for (var iter = 0; iter < str.length; ++iter) {
arr.push(str.charCodeAt(iter).toString(2));
}
return arr.join(“ “);
}

ES6语法


ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。



Number提供isFinite()isNaN()
它们与传统的全局方法isFinite()isNaN()的区别:
传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效
栗如:
Number.isNaN(‘15’) // false
isNaN(‘15’) //true


Number.isInteger()用来判断一个数值是否为整数
新增一个极小的常量Number.EPSILON。是 JavaScript 能够表示的最小精度, 2 的 -52 次方

引入了Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER这两个常量表示能够精确表示的整数值范围. 实际(-2^53 , 2^53), 开区间
Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内

数值深入

https://medium.com/angular-in-depth/javascripts-number-type-8d59199db1b6

字符串

概述


注意: 不能通过下标单独改变string的某字符.

var carname=”Volvo XC60”;var carname=’Volvo XC60’;
var character=carname[7];
var answer=”It’s alright”;var answer=”He is called ‘Johnny’”;
var answer=’He is called “Johnny”‘;
var answer=’It\’s alright’;var answer=”He is called \”Johnny\””;

.length

.indexOf()

.match()

.replace()
string.replace(searchvalue,newvalue)

searchvalue 必须。规定子字符串或要替换的模式的 RegExp 对象。
请注意,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为 RegExp 对象。
newvalue 必需。一个字符串值。规定了替换文本或生成替换文本的函数。


function convertHTML(str) {
return str.replace(/[&<>”‘]/g, function($0) {
return “&” + { “&”: “amp”, “<”: “lt”, “>”: “gt”, ‘“‘: “quot”, “‘“: “apos” }[$0] + “;”;
});
}


.toUpperCase()
.toLowerCase()
.split()
var string = “Split me into an array”;
var array = [];
array = string.split(‘ ‘);
var array2 = string.split(‘i’);
split(‘’);拆分为一个个字符

.substr(from,len)
.substring(from,to)

ES6语法

将码点用大括号括起来, 可以表示大于0xFFFF的码点
‘\u{1F680}’ === ‘\uD83D\uDE80’

共6中表示法
‘\z’ === ‘z’ // true
‘\172’ === ‘z’ // true
‘\x7A’ === ‘z’ // true
‘\u007A’ === ‘z’ // true
‘\u{7A}’ === ‘z’ // true



//ES5函数, 不能正确处理32位
s.charAt()
s.charCodeAt()
String.fromCharCode()

//ES6函数, 可以正确处理32位
s.at()
s.codePointAt()
String.fromCodePoint()


for…of循环, 可以处理32位



normalize()
‘\u01D1’.normalize() === ‘\u004F\u030C’.normalize()
normalize方法可以接受一个参数来指定normalize的方式,参数的四个可选值如下。
· NFC,默认参数,表示“标准等价合成”(Normalization Form Canonical Composition),返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。
· NFD,表示“标准等价分解”(Normalization Form Canonical Decomposition),即在标准等价的前提下,返回合成字符分解的多个简单字符。
· NFKC,表示“兼容等价合成”(Normalization Form Compatibility Composition),返回合成字符。所谓“兼容等价”指的是语义上存在等价,但视觉上不等价,比如“囍”和“喜喜”。(这只是用来举例,normalize方法不能识别中文。)
· NFKD,表示“兼容等价分解”(Normalization Form Compatibility Decomposition),即在兼容等价的前提下,返回合成字符分解的多个简单字符。

用反引号`表示字符串
可以嵌套变量, 可以换行
大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
let name = “Bob”, time = “today”;
Hello ${name},<br /> how are you ${time}?

Date对象

http://www.runoob.com/jsref/jsref-obj-date.html

以下四种方法同样可以创建 Date 对象:
var d = new Date();
var d = new Date(milliseconds);
var d = new Date(dateString);
var d = new Date(year, month, day, hours, minutes, seconds, milliseconds);

Date 对象属性

属性 描述
constructor 返回对创建此对象的 Date 函数的引用。
prototype 使您有能力向对象添加属性和方法。


Date 对象方法

方法 描述
getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 (0 ~ 6)。
getFullYear() 从 Date 对象以四位数字返回年份。
getHours() 返回 Date 对象的小时 (0 ~ 23)。
getMilliseconds() 返回 Date 对象的毫秒(0 ~ 999)。
getMinutes() 返回 Date 对象的分钟 (0 ~ 59)。
getMonth() 从 Date 对象返回月份 (0 ~ 11)。
getSeconds() 返回 Date 对象的秒数 (0 ~ 59)。
getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
getTimezoneOffset() 返回本地时间与格林威治标准时间 (GMT) 的分钟差。
getUTCDate() 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。
getUTCDay() 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。
getUTCFullYear() 根据世界时从 Date 对象返回四位数的年份。
getUTCHours() 根据世界时返回 Date 对象的小时 (0 ~ 23)。
getUTCMilliseconds() 根据世界时返回 Date 对象的毫秒(0 ~ 999)。
getUTCMinutes() 根据世界时返回 Date 对象的分钟 (0 ~ 59)。
getUTCMonth() 根据世界时从 Date 对象返回月份 (0 ~ 11)。
getUTCSeconds() 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。
getYear() 已废弃。 请使用 getFullYear() 方法代替。
parse() 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。
setDate() 设置 Date 对象中月的某一天 (1 ~ 31)。
setFullYear() 设置 Date 对象中的年份(四位数字)。
setHours() 设置 Date 对象中的小时 (0 ~ 23)。
setMilliseconds() 设置 Date 对象中的毫秒 (0 ~ 999)。
setMinutes() 设置 Date 对象中的分钟 (0 ~ 59)。
setMonth() 设置 Date 对象中月份 (0 ~ 11)。
setSeconds() 设置 Date 对象中的秒钟 (0 ~ 59)。
setTime() setTime() 方法以毫秒设置 Date 对象。
setUTCDate() 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
setUTCFullYear() 根据世界时设置 Date 对象中的年份(四位数字)。
setUTCHours() 根据世界时设置 Date 对象中的小时 (0 ~ 23)。
setUTCMilliseconds() 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。
setUTCMinutes() 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。
setUTCMonth() 根据世界时设置 Date 对象中的月份 (0 ~ 11)。
setUTCSeconds() setUTCSeconds() 方法用于根据世界时 (UTC) 设置指定时间的秒字段。
setYear() 已废弃。请使用 setFullYear() 方法代替。
toDateString() 把 Date 对象的日期部分转换为字符串。
toGMTString() 已废弃。请使用 toUTCString() 方法代替。
toISOString() 使用 ISO 标准返回字符串的日期格式。
toJSON() 以 JSON 数据格式返回日期字符串。
toLocaleDateString() 根据本地时间格式,把 Date 对象的日期部分转换为字符串。
toLocaleTimeString() 根据本地时间格式,把 Date 对象的时间部分转换为字符串。
toLocaleString() 据本地时间格式,把 Date 对象转换为字符串。
toString() 把 Date 对象转换为字符串。
toTimeString() 把 Date 对象的时间部分转换为字符串。
toUTCString() 根据世界时,把 Date 对象转换为字符串。
UTC() 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。
valueOf() 返回 Date 对象的原始值。

正则

概述


var patt=new RegExp(pattern,modifiers);
或更简单的方法
var patt=/pattern/modifiers;

modifiers:
i 不区分大小写
g 全文查找
gi 全文不区分大小写查找

返回true或false:
var patt1=new RegExp(“e”);
document.write(patt1.test(“The best things in life are free”));

返回被找到的值:
var patt1=new RegExp(“e”);
document.write(patt1.exec(“The best things in life are free”));


/正则表达式主体/修饰符(可选)

修饰符 描述
i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。


字符类别

元字符 描述
. 查找单个字符,除了换行和行结束符。
\w 查找单词字符。
\W 查找非单词字符。
\d 查找数字。
\D 查找非数字字符。
\s 查找空白字符。
\S 查找非空白字符。
\b 匹配单词边界。
\B 匹配非单词边界。
\0 查找 NULL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八进制数 xxx 规定的字符。
\xdd 查找以十六进制数 dd 规定的字符。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。


量词 描述
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。



ret = “aabb abab”.match(/(.)\1(.)\2/); //aabb
ret = “aabb abab”.match(/(.)(.)\1\2/); //abab

RegExp 对象方法

方法 描述 FF IE
compile 编译正则表达式。 1 4
exec 检索字符串中指定的值。返回找到的值,并确定其位置。 1 4
test 检索字符串中指定的值。返回 true 或 false。 1 4


支持正则表达式的 String 对象的方法

方法 描述 FF IE
search 检索与正则表达式相匹配的值。 1 4
match 找到一个或多个正则表达式的匹配。 1 4
replace 替换与正则表达式匹配的子串。 1 4
split 把字符串分割为字符串数组。 1 4


ES6语法

// 等价
var regex = new RegExp(‘xyz’, ‘i’);
var regex = new RegExp(/xyz/i);
var regex = /xyz/i;


new RegExp(/abc/ig, ‘i’).flags
// ES6允许这样写, ‘ig’被忽略, 执行新给的’i’


字符串对象共有 4 个方法,可以使用正则表达式:match()replace()search()split()
在ES6实现为调用RegExp的实例方法
· String.prototype.match 调用 RegExp.prototype[Symbol.match]
· String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
· String.prototype.search 调用 RegExp.prototype[Symbol.search]
· String.prototype.split 调用 RegExp.prototype[Symbol.split]

ES6添加u修饰符来来正确处理大于\uFFFF的 Unicode 字符
/\u{20BB7}/u.test(‘𠮷’) // true

有些 Unicode 字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K。

ES6添加u修饰符来来正确处理大于\uFFFF的 Unicode 字符
/[a-z]/i.test(‘\u212A’) // false
/[a-z]/iu.test(‘\u212A’) // true
上面代码中,不加u修饰符,就无法识别非规范的K字符。

ES6 还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。
g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。


断言
/\d+(?=%)/.exec(‘100% of US presidents have been male’) // [“100”] 先行断言
/\d+(?!%)/.exec(‘that’s all 44 of them’) // [“44”] 先行否定断言

/(?<=\$)\d+/.exec(‘Benjamin Franklin is on the $100 bill’) // [“100”] 后行断言
/(?<!\$)\d+/.exec(‘it’s is worth about €90’) // [“90”] 后行否定断言


链接

教程 @菜鸟教程
http://www.runoob.com/jsref/jsref-obj-regexp.html

正则介绍
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

正则对象
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp

一个正则工具网站:
http://regex.zjmainstay.cn/

异常处理

错误类型

http://javascript.ruanyifeng.com/grammar/error.html



自定义错误
function UserError(message) {
this.message = message || ‘默认信息’;
this.name = ‘UserError’;
}

UserError.prototype = new Error();
UserError.prototype.constructor = UserError;

ES6写法
class CustomError extends Error {
constructor (message) {
super(message)
this.name = ‘CustomError’,
// 这一步可不写,默认会保存堆栈追踪信息到自定义错误构造函数之前,
// 而如果写成 Error.captureStackTrace(this) 则自定义错误的构造函数也会被保存到堆栈追踪信息
Error.captureStackTrace(this, this.constructor)
}
}
https://segmentfault.com/a/1190000015894152


JavaScript 语言标准只提到,Error实例对象必须有message属性,表示出错时的提示信息,没有提到其他属性。
大多数 JavaScript 引擎,对Error实例还提供name和stack属性,分别表示错误的名称和错误的堆栈,但它们是非标准的,不是每种实现都有。

传递及捕获

https://www.jianshu.com/p/945502a43eaa

传递错误的几种方式

  • throw the error (making it an exception).
  • pass the error to a callback, a function provided specifically for handling errors and the results of asynchronous operations
  • pass the error to a reject Promise function
  • emit an “error” event on an EventEmitter


    function children() {
    var err = new Error(“子报错”);
    err.statusCode = 404; //附加的属性 提供上下文
    throw err;
    }

    function parent() {
    children(); //有异常抛出 函数中断执行
    }

    try {
    parent();
    } catch (error) {
    console.log(error.statusCode); //404
    console.log(error);
    }


    try catch无法捕获异步队列中抛出的异常
    对于异步函数,我们对异常的处理原则为, 异步函数里面使用自己的try catch处理异常,
    然后通过它的回调函数的参数 callback(error, value) 在上一层的调用中判断是否异步出现了异常,如果出现的话, error即第一个参数不为空

    promise内部发生异常,统一在内部try catch 处理,并设置它的状态为reject 然后通过reject回调处理,
    即promise异常处理通过reject状态处理,
    如果一个promise的reject状态没得到处理的话,会抛出一个异常

    Promise 内部的错误不会影响到 Promise 外部的代码
    http://es6.ruanyifeng.com/#docs/promise

    generator 函数体内抛出的异常能在函数体外捕获
    但是注意捕获的时间,哪个next 执行会抛出异常,就在哪次上面捕获 当然也可以 try catch 包含多个next
    yield 自带异常api Generator.prototype.throw()
    var g = function* () {
    try {
    yield console.log(‘yielding’); //从这里断开的 需要从左表达式处接受异常
    } catch(error) {
    console.log(error);
    }
    };

    var i = g();
    i.next();
    i.throw(new Error(“外部异常”)); //throw 传递的参数可以传递到generator里面



    async无论如何都会返回一个promise,如果内部有异常,那么返回一个reject的promise
    只要有一个await后面的promise是reject, 那么async就会中断,并且返回一个reject的promise
    如果想 即使 await后面是一个reject的promise,我如何还能让它往下执行, 可以用一个 try catch 将对应的await 包裹住,这样的话,就可以捕获promise未处理抛出的异常

    try…catch

    catch代码块捕获错误之后,程序不会中断,会按照正常流程继续执行下去
    try {
    throw “出错了”;
    } catch (e) {
    console.log(111);
    }
    console.log(222);
    // 111
    // 222

    即使try块或catch块里面有return, finally仍然会被执行
    首先try或catch里的return执行得到结果, 然后return之前执行finally, 等finally代码执行完毕后才返回
    如果finally里也有return, 则不会执行到刚才try或catch里的return
    var count = 0;
    function countUp() {
    try {
    return count;
    } finally {
    count++;
    }
    }

    countUp() // 0
    count // 1


    正确栗子
    var asyncFunc = function (callback) {
    process.nextTick(function () {
    var results = something;
    if (error) {
    return callback(error);
    }
    callback(null, results);
    });
    };


    错误写法
    // 若try中的callback中发生异常, 则catch中的callback还会被执行, 造成业务逻辑混乱
    try {
    req.body = JSON.parse(buf, options.reviver);
    callback();
    } catch (err) {
    err.body = buf;
    err.status = 400;
    callback(err);
    }

    // 异步调用的执行不在本轮事件循环内, 异常无法被捕获
    try {
    asyncFunc(callback);
    } catch (e) {
    // xxx
    }

    Node特有的方式

    process方式可以捕获任何异常(不管是同步代码块中的异常还是异步代码块中的异常)
    process.on(‘uncaughtException’, function (e) {
    /处理异常/
    console.log(e.message)
    });
    asyncError()
    syncError()

    最佳实践

    https://www.joyent.com/node-js/production/design/errors
    http://imweb.io/topic/5846d2069be501ba17b10a8d


    预期异常:参数不合法,前提条件不符合,通常直接throw
    非预期异常: js运行时异常,依赖库异常, 可以直接在异常上面提供一下附加属性来提供上下文


    一个函数的参数、类型、预期错误、如何捕获都应该是明确的。
    所有的erorr都使用Error对象(或者基于Error类的扩展)
    所有的error都应该提供name和message属性,并且stack也应该准确可用。
    使用标准的Error类和标准属性。使用独立的属性,添加尽可能多的附加信息,尽可能使用通用的属性名称。

    node-verror

    https://github.com/joyent/node-verror

    异步编程

    流程控制

    事件发布订阅

    const EventEmitter = require(‘events’);

    class MyStream extends EventEmitter {
    write(data) {
    this.emit(‘data’, data);
    }
    }

    const stream = new MyStream();

    stream.on(‘data’, (data) => {
    console.log(Received data: "${data}");
    });
    stream.write(‘With ES6’);

    Usage of util.inherits() is discouraged.
    https://nodejs.org/docs/latest-v10.x/api/util.html#util_util_inherits_constructor_superconstructor

    利用事件解决雪崩问题
    var proxy = new events.EventEmitter();
    var status = “ready”;
    var select = function (callback) {
    proxy.once(“selected”, callback);
    if (status === “ready”) {
    status = “pending”;
    db.select(“SQL”, function (results) {
    proxy.emit(“selected”, results);
    status = “ready”;
    });
    }
    };
    <深入浅出node.js> page77

    多异步之间的协作
    var after = function (times, callback) {
    var count = 0,
    results = {};
    return function (key, value) {
    results[key] = value;
    count++;
    if (count === times) {
    callback(results);
    }
    };
    };
    var done = after(times, render);

    var emitter = new events.Emitter();
    var done = after(times, render);
    emitter.on(“done”, done);
    emitter.on(“done”, other);
    fs.readFile(template_path, “utf8”, function (err, template) {
    emitter.emit(“done”, “template”, template);
    });
    db.query(sql, function (err, data) {
    emitter.emit(“done”, “data”, data);
    });
    l10n.get(function (err, resources) {
    emitter.emit(“done”, “resources”, resources);
    });
    <深入浅出node.js> page78

    Promise

    流程控制库


并发控制

promise-fun
https://github.com/sindresorhus/promise-fun

async
https://github.com/caolan/async



事件

https://developer.mozilla.org/en-US/docs/Web/API/Event
https://www.w3.org/TR/DOM-Level-3-Events

注册事件监听器

https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Events
https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model/Events
https://developer.mozilla.org/zh-CN/docs/Web/Guide/Events/Event_handlers
http://dafeizizhu.github.io/2013/08/02/javascript-bind-event-handler/

addEventListener

推荐使用的方法
button.addEventListener(“click”, function (evt) { … }, true); // IE9, IE9+, Chrome, Safari, Opera
button.attatchEvent(“onclick”, function () { … }); // IE9-

  1. 执行上下文是DOM元素。
  2. 返回值是false的时候不阻止默认行为,也不阻止事件传播,只能通过调用事件对象的preventDefault才能阻止默认行为,调用stopPropagation才能阻止事件传播。
  3. 事件的传播可以选择是冒泡(默认)或者是捕获的。(IE不支持事件捕获)
  4. 绑定的事件处理程序可以通过removeEventHandler或者detachEvent来删除。
  5. 多个事件处理程序的执行顺序是由绑定的顺序决定的,而且如果一个事件处理程序报错,不影响其他事件处理程序的执行。例子参考这里

更多关于addEventListener

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget

addEventListener() 方法用于向指定元素添加事件句柄。
element.addEventListener(event, function, useCapture);
第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。默认值为 false, 即冒泡传递,当值为 true 时, 事件使用捕获传递。在 冒泡 中,内部元素的事件会先被触发,然后再触发外部元素.
注意:不要使用 “on” 前缀。 例如,使用 “click” ,而不是使用 “onclick”。
removeEventListener() 方法移除事件的监听。

addEventListener() 方法添加的事件句柄不会覆盖已存在的事件句柄。
你可以向一个元素添加多个事件句柄。
你可以向同个元素添加多个同类型的事件句柄,如:两个 “click” 事件。
你可以向任何 DOM 对象添加事件监听,不仅仅是 HTML 元素。如: window 对象。

重复添加同一个监听器,监听器也只会执行一次;与之相对,就算重复添加,只要移除一次,监听器则会失效。
注意以下两者写法效果不一样, 前者添加的是同一个监听器(同一个引用), 后者不是
let f = evt => console.log(evt.key);
document.addEventListener(‘keypress’, f);
document.addEventListener(‘keypress’, f);

document.addEventListener(‘keypress’, evt => console.log(evt.key));
document.addEventListener(‘keypress’, evt => console.log(evt.key));


为了保存为一引用, 可以: 取到外部全局变量, 挂上去; 挂在元素自身上
注意属性的定义应该进行命名空间的限制,以避免发生同名属性覆盖的问题
https://blog.csdn.net/lm278858445/article/details/81707921

on-handler

var button = document.getElementById(“#id”);
button.onclick = function (evt) { evt = evt || windowo.event; alert(“Hello World”); };

  1. 执行上下文是绑定的DOM元素。
  2. 返回值是false的时候阻止默认行为,但不阻止事件传播。
  3. 事件的传播只能是冒泡的。

这种方式缺点是只能绑定一个处理函数, 绑定多个会覆盖
在HTML 规范中,其返回值会以一种特殊的方式被处理

in DOM handler

避免使用的方法

  1. 执行上下文是DOM元素。
  2. 当返回值是false的时候阻止默认行为,但不阻止事件传播。
  3. 事件的传播只能是冒泡的。

关于this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#In_an_inline_event_handler
<button onclick=”alert(this.tagName.toLowerCase());”> 返回DOM元素
<button onclick=”alert((function() { return this; })());”> 内部的this没有设置, 所以返回global对象

兼容性

IE<=8使用attachEvent/ detachEvent
https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/attachEvent
封装一个兼容函数 https://www.cnblogs.com/rainman/archive/2009/02/11/1387955.html

事件流

https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
https://www.w3.org/TR/DOM-Level-3-Events/#event-flow
JavaScript - 图8


capture模式的栗子
https://www.cnblogs.com/bfgis/p/5460191.html
    var parent = document.getElementById(“parent”);
var child = document.getElementById(“child”);

document.body.addEventListener(“click”,function(e){
console.log(“click-body”);
},false);

parent.addEventListener(“click”,function(e){
console.log(“click-parent—-事件传播”);
},false);
    
     //新增事件捕获事件代码
parent.addEventListener(“click”,function(e){
console.log(“click-parent—事件捕获”);
},true);

child.addEventListener(“click”,function(e){
console.log(“click-child”);
},false);
JavaScript - 图9

触发

It can also be triggered programmatically, such as by calling the HTMLElement.click() method of an element,
or by defining the event, then sending it to a specified target using EventTarget.dispatchEvent().

属性及方法


Event.target Read only
A reference to the target to which the event was originally dispatched.


Event.preventDefault()
Cancels the event (if it is cancelable).
Event.stopImmediatePropagation()
For this particular event, no other listener will be called. Neither those attached on the same element, nor those attached on elements which will be traversed later (in capture phase, for instance)
Event.stopPropagation()
Stops the propagation of events further along in the DOM.

事件类型参考

HTML DOM 事件参考手册:
http://www.runoob.com/jsref/dom-obj-event.html
https://developer.mozilla.org/zh-CN/docs/Web/Events


onload 用户进入页面时被触发

onunload 用户离开页面时被触发

onchange 常结合对输入字段的验证来使用

onmouseover 用户的鼠标移至 HTML 元素上方
onmouseout 用户的鼠标移出元素
mouseenter
mouseleave
mouseenter 事件只有在鼠标指针进入被选元素时被触发,
mouseover 事件在鼠标指针进入任意子元素时也会被触发

onmousedown
onmouseup
onclick 首先当点击鼠标按钮时,会触发 onmousedown 事件,当释放鼠标按钮时,会触发 onmouseup 事件,最后,当完成鼠标点击时,会触发 onclick 事件。

onfocus 获得焦点时

事件坐标

https://www.w3cplus.com/css/viewports.html
事件坐标 Event coordinates
pageX/Y:从<html>原点到事件触发点的CSS的 pixels (IE6~8不支持)
clientX/Y:从viewport原点(浏览器窗口)到事件触发点的CSS的 pixels
screenX/Y:从用户显示器窗口原点到事件触发点的设备 的 pixels。


pageX,clientX,screenX,offsetX区别
https://blog.csdn.net/ajaxuser/article/details/7549661

pageX/pageY:
鼠标相对于整个页面的X/Y坐标。
注意,整个页面的意思就是你整个网页的全部,比如说网页很宽很长,宽2000px,高3000px,那pageX,pageY的最大值就是它们了。
特别说明:IE不支持!
https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/pageX

clientX/clientY:
事件发生时鼠标在浏览器内容区域的X/Y坐标(不包含滚动条)。
浏览器内容区域即浏览器窗口中用来显示网页的可视区域,注意这个可视,也就是说需要拖动滚动条才能看到的区域不算。
当你将浏览器窗口缩小时,clientX/clientY的最大值也会缩小,但始终,它们的最大值不会超过你浏览器可视区域。
特别说明:IE下此属性不规范,它们的最小值不是0而是2,也就是说IE下的clientX/clientY比火狐下始终大2px。
https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/clientX
e.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft = e.pageXe.clientY + document.documentElement.scrollTop - document.documentElement.clientTop = e.pageY
screenX/screenY
鼠标在屏幕上的坐标。screenX,screenY的最大值不会超过屏幕分辨率。
offsetX/offsetY:
得出的结果跟pageX/pageY一样,既然如此,它有什么存在价值?因为:特别说明:只有IE支持!相当于IE下的pageX,pageY。
MouseEvent 接口的只读属性 offsetX 规定了事件对象与目标节点的内填充边(padding edge)在 X 轴方向上的偏移量。
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX

自定义事件

https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events

var event = new Event(‘build’);

// Listen for the event.
elem.addEventListener(‘build’, function (e) { // }, false);

// Dispatch the event.
elem.dispatchEvent(event);

Event
https://developer.mozilla.org/en-US/docs/Web/API/Event
CustomEvent
https://developer.mozilla.org/zh-CN/docs/Web/API/CustomEvent
二者区别
var event = new CustomEvent(‘myevent’, {detail:123});
event.detail = 456; // Ignored in sloppy mode, throws in strict mode
console.log(event.detail); // 123
https://stackoverflow.com/questions/40794580/event-vs-customevent

createEvent / initCustomEvent / initKeyboardEvent等方法不要用了
https://developer.mozilla.org/en-US/docs/Web/API/Document/createEvent
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyboardEvent

键盘事件

https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent
应该用KeyboardEvent.key https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/key
KeyboardEvent.keyCode 已弃用
KeyboardEvent.code 不总能确定按键https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/code

键值列表
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
http://keycode.info/

事件属性

currentTarget 与 target
https://developer.mozilla.org/zh-CN/docs/Web/API/Event/currentTarget
<!DOCTYPE html>




















事件循环

https://segmentfault.com/a/1190000019759283
console.log(“script start”);

setTimeout(function () {
console.log(“setTimeout”);
}, 0);

Promise.resolve()
.then(function () {
console.log(“promise1”);
})
.then(function () {
console.log(“promise2”);
});

console.log(“script end”);


macrotasks: setTimeout, setInterval, setImmediate, I/O, UI renderingmicrotasks: process.nextTick, Promise, MutationObserver

另见MDN文档 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop


setTimeout( => console.log(4))

new Promise(resolve => {
resolve()
console.log(1)
}).then(
=> {
console.log(3)
Promise.resolve().then( => {
console.log(‘before timeout’)
}).then(
=> {
Promise.resolve().then(_ => {
console.log(‘also before timeout’)
})
})
})

console.log(2)

DOM

DOM元素


getElementById()
getElementsByTagName()
getElementsByClassName()

改变HTML内容:
document.getElementById(id).innerHTML=新的 HTML
改变HTML属性:
document.getElementById(id).attribute=__新属性值
改变HTML样式:
document.getElementById(id).style.property=新样式

CSSOM View Module

https://www.zhangxinxu.com/wordpress/2011/09/cssom%E8%A7%86%E5%9B%BE%E6%A8%A1%E5%BC%8Fcssom-view-module%E7%9B%B8%E5%85%B3%E6%95%B4%E7%90%86%E4%B8%8E%E4%BB%8B%E7%BB%8D/
https://drafts.csswg.org/cssom-view/

Window视图属性

window.innerWidth
window.innerHeight
获取window窗体的内部宽度,不包括用户界面元素,比如窗框

screen.availHeight 不包含windows任务栏
screen.height 包含windows任务栏
值是CSS像素值. 若在系统设置中开启了放大显示, 屏幕像素是1080, screen.height是864 (1080/1.25)
此时devicePixelRatio是1.25, 864是设备无关像素DIP
JavaScript - 图10


innerHeight
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/innerHeight
浏览器窗口的视口(viewport)高度(以像素为单位);如果有水平滚动条,也包括滚动条高度。

pageXOffset / pageYOffset
scrollX / scrollY
pageXOffset 属性是 scrollX 属性的别名
为了跨浏览器兼容,请使用 window.pageXOffsetwindow.pageYOffset 代替 window.scrollXwindow.scrollY``。
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect
旧版本的 IE(<9)两个属性都不支持,必须通过其他的非标准属性来解决此问题
https://developer.mozilla.org/zh-CN/docs/Web/API/window/scrollX

screen视图属性

availWidth和availHeight
colorDepth
pixelDepth
width和height

元素视图属性

https://www.zhangxinxu.com/wordpress/2011/09/cssom%e8%a7%86%e5%9b%be%e6%a8%a1%e5%bc%8fcssom-view-module%e7%9b%b8%e5%85%b3%e6%95%b4%e7%90%86%e4%b8%8e%e4%bb%8b%e7%bb%8d/

clientHeight和clientWidth用于描述元素内尺寸,content + padding,不包含border(IE8之前包含)、margin、滚动条部分.
此属性会将获取的值四舍五入取整数。 如果你需要小数结果, 请使用 element.getBoundingClientRect().
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/clientHeight

offsetHeight和offsetWidth用于描述元素外尺寸,content + padding + border+ scrollbars, 不包含margin, 不包含伪元素如::before or ::after
对于文档的body对象,它包括代替元素的CSS高度线性总含量高. 浮动元素的向下延伸内容高度是被忽略的。
JavaScript - 图11
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetHeight


scrollHeight和scrollWidth 元素完整的尺寸, 包括因滚动而不可见的部分. 包含伪元素
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight

If the element’s content can fit without a need for vertical scrollbar, its scrollHeight is equal to clientHeight
如果网页内容能够在浏览器窗口中全部显示,不出现滚动条,那么网页的clientWidth和scrollWidth应该相等。
但是实际上,不同浏览器有不同的处理,这两个值未必相等。取它们之中较大的那个值

scrollTop和scrollLeft 若发生了滚动, 则表示滚动的像素值. 若未产生滚动条, 值为0.
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop

另: document.body.scrollTop || document.documentElement.scrollTop
因为 chrome 没有 document.body.scrollTop

clientTop和clientLeft padding边缘和border外边缘之间的水平和垂直距离,也就是左上border宽度
offsetTop和offsetLeft 元素的左上角(border外边缘)与已定位的父容器(offsetParent对象)左上角的距离
offsetParent对象是指元素最近的定位(relative,absolute)祖先元素,递归上溯,如果没有祖先元素是定位的话,会返回null
JavaScript - 图12

Element.getBoundingClientRect() 返回元素的大小及其相对于视口的位置https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect
若要得到元素在滚动页中的绝对位置, 需加上scrollTop和scrollLeft
  var X= this.getBoundingClientRect().left+document.documentElement.scrollLeft;
  var Y =this.getBoundingClientRect().top+document.documentElement.scrollTop;
http://www.ruanyifeng.com/blog/2009/09/find_element_s_position_using_javascript.html

文档视图和元素视图方法

elementFromPoint()
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/elementFromPoint

getBoundingClientRect()
getClientRects()
scrollIntoView()

????

document.body.clientHeight
document.documentElement.clientHeight
Document.documentElement 是一个会返回文档对象(document)的根元素的只读属性
JavaScript - 图13

兼容性列表

https://www.quirksmode.org/dom/w3c_cssom.html
这个兼容性列表似乎过时了, 见其中的screen.colorDepth, 标准已经改了

HTML元素


创建新HTML元素

这是一个段落。

这是另一个段落。



删除HTML元素

这是一个段落。

这是另一个段落。





var child=document.getElementById(“p1”);child.parentNode.removeChild(child);


表单验证


onsubmit=”return validateForm()”

required disabled 等表单属性
http://www.runoob.com/html/html5-form-attributes.html

约束验证CSS伪类

:disabled 选取属性为 “disabled” 属性的 input 元素
:invalid 选取无效的 input 元素
:optional 选择没有”required”属性的 input 元素
:required 选择有”required”属性的 input 元素
:valid 选取有效值的 input 元素


约束验证API

checkValidity() 如果 input 元素中的数据是合法的返回 true,否则返回 false。
setCustomValidity() 设置 input 元素的 validationMessage 属性,用于自定义错误提示信息的方法。


var inpObj = document.getElementById(“id1”); if (inpObj.checkValidity() == false) { document.getElementById(“demo”).innerHTML = inpObj.validationMessage; }



约束验证DOM属性

validity 布尔属性值,返回 input 输入值是否合法
validationMessage 浏览器错误提示信息
willValidate 指定 input 是否需要验证

var txt = “”; if (document.getElementById(“id1”).validity.rangeOverflow) { txt = “输入的值太大了”; } document.getElementById(“demo”).innerHTML = txt;

validity属性

属性 描述
customError 设置为 true, 如果设置了自定义的 validity 信息。
patternMismatch 设置为 true, 如果元素的值不匹配它的模式属性。
rangeOverflow 设置为 true, 如果元素的值大于设置的最大值。
rangeUnderflow 设置为 true, 如果元素的值小于它的最小值。
stepMismatch 设置为 true, 如果元素的值不是按照规定的 step 属性设置。
tooLong 设置为 true, 如果元素的值超过了 maxLength 属性设置的长度。
typeMismatch 设置为 true, 如果元素的值不是预期相匹配的类型。
valueMissing 设置为 true,如果元素 (required 属性) 没有值。
valid 设置为 true,如果元素的值是合法的。

Browser对象

http://www.runoob.com/jsref/obj-window.html

Navigator
Screen
History
Location

DOM对象

element
attr
onxxx

forEach() 和 map()

1.forEach()
没有返回值。
arr[].forEach(function(value,index,array){
  //do something
})
· 参数:value数组中的当前项, index当前项的索引, array原始数组;
· 数组中有几项,那么传递进去的匿名回调函数就需要执行几次;
· 理论上这个方法是没有返回值的,仅仅是遍历数组中的每一项,不对原来数组进行修改;但是可以自己通过数组的索引来修改原来的数组;
[javascript] view plain copy

1. var ary = [12,23,24,42,1];
2. var res = ary.forEach(function (item,index,input) {
3. input[index] = item*10;
4. })
5. console.log(res);//—> undefined;
6. console.log(ary);//—> 通过数组索引改变了原数组;

2.map()
有返回值,可以return 出来。
arr[].map(function(value,index,array){
  //do something
  return XXX
})
· 参数:value数组中的当前项,index当前项的索引,array原始数组;
· 区别:map的回调函数中支持return返回值;return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了);
[javascript] view plain copy

1. var ary = [12,23,24,42,1];
2. var res = ary.map(function (item,index,input) {
3. return item*10;
4. })
5. console.log(res);//—>[120,230,240,420,10]; 原数组拷贝了一份,并进行了修改
6. console.log(ary);//—>[12,23,24,42,1]; 原数组并未发生变化


其他



严格模式


“use strict”
1. 针对整个脚本: 要放在产生第一个运行结果的语句之前
2. 针对单个函数: 放在函数体的第一行
3. 变通写法: 方法1不利于合并文件, 将整个脚本文件放在一个立即执行的匿名函数中
  (function (){    “use strict”;
    // some code here   })();


ES6的”模块”自动使用严格模式

语法和行为


http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html


l 全局变量必须显式声明
(在正常模式中,如果一个变量没有声明就赋值,默认是全局变量)

l 属性和方法静态绑定, 在编译时就确定
n 禁止使用with语句
n 创设eval作用域

l 增强的安全措施
n 禁止this关键字指向全局对象
n 禁止在函数内部遍历调用栈

l 禁止删除变量
只有configurable设置为true的对象属性,才能被删除。

l 显式报错
对一个对象的只读属性进行赋值, 将报错
(正常模式只是默默地失败)

l 重名错误
n 不能有重名属性
正常模式同名属性覆盖, 严格模式报错
n 不能有重名参数

l 禁止八进制表示法

l arguments对象的限制
n 不允许对arguments赋值
n arguments不再追踪参数的变化
n 禁止使用arguments.callee

关于callee:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments/callee


l 函数必须声明在顶层
只允许在全局作用域或函数作用域的顶层声明函数,
不允许在非函数的代码块内声明函数

l 保留字
新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。

模块

l export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 报错
var m = 1;
export m;

正确写法:
// 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};

l export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

import
import { lastName as surname } from ‘./profile.js’;

import {a} from ‘./xxx.js’

a = {}; // Syntax Error : ‘a’ is read-only;
a.foo = ‘hello’; // 合法操作

l import命令具有提升效果,会提升到整个模块的头部,首先执行。
l import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
l import语句会执行所加载的模块
l 多次重复执行同一句import语句,那么只会执行一次,而不会执行多次

import as xxx from ‘./xxx’; // 整体加载

// 接口改名, 只是转接了一下接口, 并没有导入到当前模块内
export { foo as myFoo } from ‘my_module’;
export { foo as default } from ‘my_module’;
export { default as es6 } from ‘./someModule’;
export
from ‘circle’;

CommonJS

commonJS用同步的方式加载模块
Node.js是commonJS规范的主要实践者
它有四个重要的环境变量为模块化的实现提供支持:moduleexportsrequireglobal
实际使用时,用module.exports定义当前模块对外输出的接口(不推荐直接用exports),用require加载模块

加载模块过程
(function (exports, require, module, filename, dirname) {
var math = require(‘math’);
exports.area = function (radius) {
return Math.PI radius radius;
};
});
实际是对文件的内容进行了收尾包装
挂在exports上的属性都能被导出, 如exports.area
但是以下写法不行, 因为这样写只是在内部改变了exports变量的引用, 对外部无影响, 这种情况应该赋值给module.exports
exports = function () {
xxx
}
详细加载过程见<深入浅出Node.js>第二章

AMD

https://github.com/amdjs/amdjs-api/wiki/AMD
异步模块规范
所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行

AMD规范是CommonJS规范的延伸
在服务端,模块文件都存在本地磁盘,读取非常快,所以同步加载不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载

require.js
require.config()指定引用路径等,用define()定义模块,用require()加载模块
define(id?, dependencies?, factory);

CMD

http://wiki.commonjs.org/wiki/Modules

对于依赖的模块AMD是提前执行,CMD是延迟执行 (RequireJS从2.0开始,也可以延迟执行)
sea.js

UMD

https://github.com/umdjs/umd

通用模块规范 一种兼容以上二者的形式
栗子:
(function (root, factory) {
if (typeof define === ‘function’ && define.amd) {
// AMD
define([‘jquery’, ‘underscore’], factory);
} else if (typeof exports === ‘object’) {
// Node, CommonJS之类的
module.exports = factory(require(‘jquery’), require(‘underscore’));
} else {
// 浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery, root.);
}
}(this, function ($,
) {
// 方法
function a(){}; // 私有方法,因为它没被返回 (见下面)
function b(){}; // 公共方法,因为被返回了
function c(){}; // 公共方法,因为被返回了

// 暴露公共方法
return {
b: b,
c: c
}
}));

ES6 module

http://es6.ruanyifeng.com/#docs/module
http://es6.ruanyifeng.com/#docs/module-loader
https://juejin.im/post/5aaa37c8f265da23945f365c

ES6的模块不是对象,import命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。

ES6 的模块自动采用严格模式,不管你有没有在模块头部加上”use strict”;。
严格模式主要有以下限制。
· 变量必须声明后再使用
· 函数的参数不能有同名属性,否则报错
· 不能使用with语句
· 不能对只读属性赋值,否则报错
· 不能使用前缀 0 表示八进制数,否则报错
· 不能删除不可删除的属性,否则报错
· 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
· eval不会在它的外层作用域引入变量
· eval和arguments不能被重新赋值
· arguments不会自动反映函数参数的变化
· 不能使用arguments.callee
· 不能使用arguments.caller
· 禁止this指向全局对象
· 不能使用fn.caller和fn.arguments获取函数调用的堆栈
· 增加了保留字(比如protected、static和interface)

ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this


注意,export *命令会忽略模块的default方法。

本质上,export default就是输出一个叫做default的变量或方法
// error
export {
a: ‘aaa’
}

// ok
var a = ‘aaa’
export {
a
}

// ok
export default {
a: ‘aaa’
}



浏览器加载
浏览器加载 ES6 模块,也使用

Modules only execute once

Always CORS
cross-origin module scripts must return valid CORS headers such as Access-Control-Allow-Origin: *.

No credentials

Mime-types
Unlike regular scripts, modules scripts must be served with one of the valid JavaScript MIME types else they won’t execute.
The HTML Standard recommends text/javascript.

对比

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。
CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
https://juejin.im/post/5aaa37c8f265da23945f365c


应该在新代码中使用 ESM 吗?
如果你已经有或者打算使用像 webpack 这样的构建工具,答案是肯定的。这将更容易完成代码库的过渡,并使 tree shaking 成为可能。但要小心:一旦 Node.js 支持原生 ESM,可能需要重构其中的一些部分。
如果你正在编写一个库,答案是也肯定的,你的模块使用者将受益于 tree shaking。
如果你不想进行构建操作,或者正在编写一个 Node.js 应用程序,还是用 CJS 吧。

作为库作者该怎么办?
用 ESM 编写代码,并使用 Rollup 或 Webpack 转换成单个 CJS 模块,然后在 package.json 将 main 字段指向此 CJS 包,并将 module 字段指向原始 ESM。如果还使用 ESM 之外的其他新语言功能,则应编译成 ES5,并提供 CJS 和 ESM 的打包。这样,你的库用户仍然可以从 tree shaking 获利而无需对代码进行转换。
https://zhuanlan.zhihu.com/p/26567790
https://hackernoon.com/node-js-tc-39-and-modules-a1118aecf95e

参考链接

https://segmentfault.com/a/1190000004873947
http://web.jobbole.com/82238/

前端模块化:CommonJS,AMD,CMD,ES6
https://juejin.im/post/5aaa37c8f265da23945f365c

JSON

                        <br />教程链接<br />[http://www.runoob.com/json/json-tutorial.html](http://www.runoob.com/json/json-tutorial.html)<br /> <br />JSON 语法是 JavaScript 对象表示法语法的子集。<br />l  数据在名称/值对中<br />l  数据由逗号分隔<br />l  大括号保存对象<br />l  中括号保存数组<br /> <br />JSON 值可以是:<br />l  数字(整数或浮点数)<br />l  字符串(在双引号中)<br />l  逻辑值(true 或 false)<br />l  数组(在中括号中)<br />l  对象(在大括号中)<br />l  null<br /> <br />JSON 文本的 MIME 类型是 "application/json"<br /> <br />**parse() & stringify()**
JSON.parse() 用于将一个 JSON 字符串转换为 JavaScript 对象。
JSON.stringify() 用于将 JavaScript 值转换为 JSON 字符串。


JSON.parse(text[, reviver])
text必需,reviver可选
reviver 对对象的每个成员调用此函数, 用来转换parse的结果,
JavaScript - 图14

JSON 不能存储 Date 对象。
如果你需要存储 Date 对象,需要将其转换为字符串。
之后再将字符串转换为 Date 对象。
var text = ‘{ “name”:”Runoob”, “initDate”:”2013-12-14”, “site”:”www.runoob.com”}’;
var obj = JSON.parse(text);
obj.initDate = new Date(obj.initDate);

JSON 不允许包含函数,但你可以将函数作为字符串存储,之后再将字符串转换为函数.
不建议在 JSON 中使用函数。

JSON.stringify(value[, replacer[, space]])
value必需; replacer和space可选
如果 replacer 为函数,则 JSON.stringify 将调用该函数,并传入每个成员的键和值。使用返回值而不是原始值。如果此函数返回 undefined,则排除成员。根对象的键是一个空字符串:””。
如果 replacer 是一个数组,则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。当 value 参数也为数组时,将忽略 replacer 数组。

如果 space 是一个数字,则返回值文本在每个级别缩进指定数目的空格,如果 space 大于 10,则文本缩进 10 个空格。space 有可以使用非数字,如:\t。

JSON.stringify() 会将所有日期转换为字符串。
JSON 不允许包含函数,JSON.stringify() 会删除 JavaScript 对象的函数,包括 key 和 value。

更多ES6语法

Symbol

http://es6.ruanyifeng.com/#docs/symbol


Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。

注意,Symbol 值作为对象属性名时,不能用点运算符。
const mySymbol = Symbol();
const a = {};

a.mySymbol = ‘Hello!’;
a[mySymbol] // undefined
a[‘mySymbol’] // “Hello!”
上面代码中,因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值。

属性的遍历

Symbol 作为属性名,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。
但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。

Symbol.for()

Symbol.for()Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。

内置的Symbol值

http://es6.ruanyifeng.com/#docs/symbol#%E5%86%85%E7%BD%AE%E7%9A%84-Symbol-%E5%80%BC

Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.species
Symbol.match
Symbol.replace
Symbol.search
Symbol.split
Symbol.iterator
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables

Symbol.iterator


ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)
一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。

Set和Map

Set

add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。

NaN是同一值
对象总是不同的值

Array.from方法可以将 Set 结构转为数组。


遍历set
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
遍历顺序就是插入顺序

由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。


map()和filter()间接用于Set
set = new Set([…set].map(x => x * 2));
set = new Set([…set].filter(x => (x % 2) == 0));

Map

只有对同一个对象的引用,Map 结构才将其视为同一个键
否则, 即使同样的值的两个实例,在 Map 结构中被视为两个键

Proxy


一些栗子
https://zhuanlan.zhihu.com/p/35080324?group_id=962743902491602944


refect

Promise

http://es6.ruanyifeng.com/#docs/promise



三种状态: pending(进行中)、fulfilled(已成功)和rejected(已失败)
从pending 变为 fulfilled或rejected , 然后状态就不会再变

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。 rejected的函数可不填.
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。
catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。catch方法之中,还能再抛出错误。
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
finally方法的回调函数不接受任何参数.
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例

Promise.resolve 和 Promise.reject 将现有对象转换为Promise对象



栗子:
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, ‘done’);
});
}

timeout(100).then((value) => {
console.log(value);
});





如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。


Iterator

http://es6.ruanyifeng.com/#docs/iterator


Generator

http://es6.ruanyifeng.com/#docs/generator

综述

不能被new
返回的是一个遍历器, 不是this
不能被当作普通构造函数 ( Generator函数里用this绑定的值并不在返回的遍历器上 ), 但是可以用call()等方法绑定this
http://es6.ruanyifeng.com/#docs/generator#Generator-%E5%87%BD%E6%95%B0%E7%9A%84this

yield表达式

(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined

yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。
Generator 函数可以不用yield表达式,这时就变成了一个单纯的暂缓执行函数
yield表达式如果用在另一个表达式之中,必须放在圆括号里面
yield表达式本身没有返回值,或者说总是返回undefinednext方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值

function* demo() {
console.log(‘Hello ‘ + (yield 123));
}

var g = demo();
console.log(g.next())
console.log(g.next(‘World’))

// 第一次next
// { value: 123, done: false }

// 第二次next
// Hello World
// { value: undefined, done: true }

关于异常

Generator函数体内产生的异常可以被外部捕获

Generator 函数返回的遍历器对象,都有一个throw方法 (Generator.prototype.throw()),可以在函数体外抛出错误,然后在 Generator 函数体内捕获。

Generator.prototype.return()

结束函数


async

http://es6.ruanyifeng.com/#docs/async



class

http://es6.ruanyifeng.com/#docs/class

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。


栗子
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}

toString() {
return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)’;
}
}


注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。
另外,方法之间不需要逗号分隔,加了会报错。
事实上,类的所有方法都定义在类的prototype属性上面。
生产环境中,我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型,然后再来为原型添加方法/属性。
Object.assign方法可以很方便地一次向类添加多个方法。
类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
类的属性名,可以采用表达式。
let methodName = ‘getArea’;

class Square {
constructor(length) {
// …
}

methodName {
// …
}
}


类不存在变量提升(hoist), 因为必须保证子类在父类之后定义。
new Foo(); // ReferenceError
class Foo {}


私有方法是常见需求,但 ES6 不提供,只能通过变通方法模拟实现。
http://es6.ruanyifeng.com/#docs/class#私有方法和私有属性





Decorator

http://es6.ruanyifeng.com/#docs/decorator

修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。
类是不会提升的,所以就没有这方面的问题。
如果一定要修饰函数,可以采用高阶函数的形式直接执行。

多个Decorator的顺序
function dec(id){
console.log(‘evaluated’, id);
return (target, property, descriptor) => console.log(‘executed’, id);
}

class Example {
@dec(1)
@dec(2)
method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1


函数式编程

偏函数的应用:
var isType = function (type) {
return function (obj) {
return toString.call(obj) == ‘[object ‘ + type + ‘]’;
};
};
var isString = isType(‘String’);
var isFunction = isType(‘Function’);

链接

js函数式编程
https://llh911001.gitbooks.io/mostly-adequate-guide-chinese/content/

ramda
https://github.com/ramda/ramda

内存管理



node中查看内存使用
process.memoryUsage();
os.totalmem()
os.freemem()

var memoryUsageExample = {
rss: 13852672, // resident set size, 进程的常驻内存
heapTotal: 6131200, // 堆申请的总内存
heapUsed: 2757120 // 堆使用中的内存
}
常驻内存总是比堆内存大, 堆外内存

V8有内存使用限制
(堆内存 heapTotal)
(64位系统约1.4G, 32位约0.7G)
超过会造成进程退出

修改
—max-old-space-size
—max-new-space-size



回收
新生代 Scavenge
老生代 Mark-Sweep & Mark-Compact

当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”
垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了

引用计数的方式, 会有循环引用造成无法回收的问题

辣鸡回收日志
node —trace_gc -e “var a = [];for (var i = 0; i < 1000000; i++) a.push(new Array(100));” > gc.log

node —prof test01.js
linux-tick-processor v8.log
windows-tick-processor.bat

注意全局变量和闭包的使用

缓存

慎重将内存作为缓存

如果需要大量缓存, 可以考虑以下库
Redis
https://github.com/mranney/node_redis
Memcached
https://github.com/3rd-Eden/node-memcached

内存泄漏

node-heapdump
node-memwatch

大文件操作

stream模块
https://nodejs.org/docs/latest/api/stream.html

Buffer

在ECMAScript 2015(ES6)推出TypeArray标准之前,JavaScript语言处理二进制数据非常困难,这在后端开发中使用很不方便。
Node.js中的Buffer类就是为了解决二进制数据处理的问题,该类为Node.js带来了如TCP流操作和文件系统流操作的能力。
ECMAScript 2015中TypeArray做为语言标准被引入,使JavaScript可以原生处理二进制数据。

Buffer同样是一个Uint8Array类型数组实例。但它与ES6中的类型数组规范并不完全兼容,
如:ArrayBuffer#slice()会创建一个分隔部分数据的拷贝,而Buffer#slice()会创建一个从Buffer中拷贝数据的视图,相对来说Buffer#slice()更高效。

可以从TypedArray的.buffer属性或new ArrayBuffer()创建一个Buffer对象。该对象会与类型数组共享内存区:
const arr = new Uint16Array(2);
arr[0] = 5000;
arr[1] = 4000;
const buf1 = Buffer.from(arr); // 复制 buffer
const buf2 = Buffer.from(arr.buffer); // 与 arr 共享内存空间
console.log(buf1); // ,仅复制了两个元素
console.log(buf2); //

https://itbilu.com/nodejs/core/NyIjmp0wZ.html

Buffer

Buffer的内存分配不是在V8的堆内存中, 而是在Node的C++层面
具体是其Buffer内部parent属性指向的SlowBuffer对象来自C++

编码问题
// 正确姿势
var chunks = [];
var size = 0;
res.on(‘data’, function (chunk) {
chunks.push(chunk);
size += chunk.length;
});
res.on(‘end’, function () {
var buf = Buffer.concat(chunks, size);
var str = iconv.decode(buf, ‘utf8’);
console.log(str);
});

// 宽字节编码时有问题
var data = ‘’;
rs.on(“data”, function (chunk) {
data += chunk; // 隐含了toString(), 默认utf-8
});

// 仍然可能有问题
var rs = fs.createReadStream(‘test.md’, {
highWaterMark: 11
});
// 实际内部用了 var StringDecoder = require(‘string_decoder’).StringDecoder;
// 现在只支持 UTF-8、 Base64、 UCS-2/UTF-16LE 三种编码
rs.setEncoding(‘utf8’);


JavaScript - 图15
http://javascript.ruanyifeng.com/stdlib/arraybuffer.html

ArrayBuffer

ArrayBuffer对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。
http://javascript.ruanyifeng.com/stdlib/arraybuffer.html


ArrayBuffer @阮一峰
http://es6.ruanyifeng.com/#docs/arraybuffer



node网络编程

https://nodejs.org/docs/latest/api/net.html

其他

深复制


详见
http://jerryzou.com/posts/dive-into-deep-clone-in-javascript/
http://jerryzou.com/posts/deepcopy/

Underscore —— .clone() 浅复制

jQuery —— $.clone() / $.extend()
$.clone() 并不是用于一般的 JS 对象的深复制,而是用于 DOM 对象
$.extend(true, {}, …)可以实现深复制

lodash ——
.clone() / .cloneDeep()
.clone(obj, true)等价于_.cloneDeep(obj)

借助JSON全局对象
function jsonClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
这种方法会抛弃对象的constructor,也就是深复制之后,无论这个对象原本的构造函数是什么,在深复制之后都会变成Object。另外诸如RegExp对象是无法通过这种方式深复制的。

对比:

特性 jQuery lodash JSON.parse 所谓“拥抱未来的深复制实现”
浏览器兼容性 IE6+ (1.x) & IE9+ (2.x) IE6+ (Compatibility) & IE9+ (Modern) IE8+ IE9+
能够深复制存在环的对象 抛出异常 RangeError: Maximum call stack size exceeded 支持 抛出异常 TypeError: Converting circular structure to JSON 支持
对 Date, RegExp 的深复制支持 × 支持 × 支持
对 ES6 新引入的标准对象的深复制支持 × 支持 × ×
复制数组的属性 × 仅支持RegExp#exec返回的数组结果 × 支持
是否保留非源生对象的类型 × × × 支持
复制不可枚举元素 × × × ×
复制函数 × × × ×

void


href=”#”与href=”javascript:void(0)”的区别
void 是 JavaScript 中非常重要的关键字,该操作符指定要计算一个表达式但是不返回值
# 包含了一个位置信息,默认的锚是#top 也就是网页的上端。
而javascript:void(0), 仅仅表示一个死链接。
在页面很长的时候会使用 # 来定位页面的具体位置,格式为:# + id
如果你要定义一个死链接请使用 javascript:void(0) 。

for…in


https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for…in

数组索引只是具有整数名称的枚举属性,并且与通用对象属性相同。不能保证for … in将以任何特定的顺序返回索引。
当迭代访问顺序很重要的数组时,最好用整数索引去进行for循环(或者使用 Array.prototype.forEach()for…of 循环)。
hasOwnProperty()的用法:继承的属性不显示

this

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this
https://github.com/creeperyang/blog/issues/16


当函数被调用,一个activation record(即 execution context)被创建。这个record包涵信息:函数在哪调用(call-stack),函数怎么调用的,参数等等。record的一个属性就是this,指向函数执行期间的this对象。

  • this不是author-time binding,而是 runtime binding。
  • this的上下文基于函数调用的情况。和函数在哪定义无关,而和函数怎么调用有关。

2.1.1 Global context
在全局上下文(任何函数以外),this指向全局对象。
console.log(this === window); // true

2.1.2 Function context
在函数内部时,this由函数怎么调用来确定。

2.1.2.1 Simple call
简单调用,即独立函数调用。由于this没有通过call来指定,且this必须指向对象,那么默认就指向全局对象。
function f1(){
return this;
}

f1() === window; // global object
在严格模式下,this保持进入execution context时被设置的值。如果没有设置,那么默认是undefined。它可以被设置为任意值(包括null/undefined/1等等基础值,不会被转换成对象)
function f2(){
“use strict”; // see strict mode
return this;
}

f2() === undefined;

2.1.2.2 Arrow functions
在箭头函数中,this由词法/静态作用域设置(set lexically)。它被设置为包含它的execution context的this,并且不再被调用方式影响(call/apply/bind)。
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

// Call as a method of a object
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// Attempt to set this using call
console.log(foo.call(obj) === globalObject); // true

// Attempt to set this using bind
foo = foo.bind(obj);
console.log(foo() === globalObject); // true
关于原理, 见 函数->箭头函数

2.1.2.3 As an object method
当函数作为对象方法调用时,this指向该对象。
var o = {
prop: 37,
f: function() {
return this.prop;
}
};

console.log(o.f()); // logs 37
this on the object’s prototype chain
原型链上的方法根对象方法一样,作为对象方法调用时this指向该对象。

2.1.2.4 构造函数
在构造函数(函数用new调用)中,this指向要被constructed的新对象。

2.1.2.5 call和apply
Function.prototype上的callapply可以指定函数运行时的this
function add(c, d){
return this.a + this.b + c + d;
}

var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
注意,当用callapply而传进去作为this的不是对象时,将会调用内置的ToObject操作转换成对象。所以4将会装换成new Number(4)**null/undefined**由于无法转换成对象,全局对象将作为**this**

2.1.2.6 bind
ES5引进了Function.prototype.bindf.bind(someObject)会创建新的函数(函数体和作用域与原函数一致),但this被永久绑定到someObject,不论你怎么调用。

2.1.2.7 As a DOM event handler
this自动设置为触发事件的dom元素。

FormData


使用FormData对象
https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects

解构赋值


解构赋值允许指定默认值。
成员严格等于undefined,默认值才会生效。(===)


数组, 对象, 字符串 都可以解构


对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let { foo: baz } = { foo: “aaa”, bar: “bbb” };
baz // “aaa”
foo // error: foo is not defined


解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true


解构可以应用到函数参数
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]


只要有可能,就不要在模式中放置圆括号
模式不能使用圆括号
但是不容易确定是模式还是表达式


变量声明语句和函数参数不能使用圆括号(函数参数相当于变量声明)
let [(a)] = [1]; // 报错, 声明
[(b)] = [3]; // 正确, 赋值


可以便捷的加载模块指定方法
const { SourceMapConsumer, SourceNode } = require(“source-map”);

Annex

MDN开发者文档

https://developer.mozilla.org/en-US/docs/Web/JavaScript

ES6

ES6教程 @阮一峰
http://es6.ruanyifeng.com/

Exploring ES6
https://exploringjs.com/es6/index.html

babel
https://babeljs.io/

es6 浏览器兼容表
https://kangax.github.io/compat-table/es6/

一个正则工具网站

http://regex.zjmainstay.cn/


JavaScript 对象参考手册

http://www.runoob.com/jsref/jsref-tutorial.html
参考手册描述了每个对象的属性和方法,并提供了在线实例。
· Array 对象
· Boolean 对象
· Date 对象
· Math 对象
· Number 对象
· String 对象
· RegExp 对象
· 全局属性和函数

Browser 对象参考手册

参考手册描述了每个对象的属性和方法,并提供了在线实例。
· Window 对象
· Navigator 对象
· Screen 对象
· History 对象
· Location 对象

HTML DOM 参考手册

参考手册描述了 HTML DOM 的属性和方法,并提供在线实例。
· HTML Document
· HTML Element
· HTML Attributes
· HTML Events

HTML DOM 元素对象参考手册

参考手册描述了每个对象的属性和方法,并提供了在线实例。
· Anchor 对象
· Area 对象
· Base 对象
· Body 对象
· Button 对象
· Form 对象
· Frame/IFrame 对象
· Frameset 对象
· Image 对象
· Input Button 对象
· Input Checkbox 对象
· Input File 对象
· Input Hidden 对象
· Input Password 对象
· Input Radio 对象
· Input Reset 对象
· Input Submit 对象
· Input Text 对象
· Link 对象
· Meta 对象
· Object 对象
· Option 对象
· Select 对象
· Style 对象
· Table 对象
· td / th 对象
· tr 对象
· Textarea 对象