ES6读后总结

读阮一峰的ECMAScript 6 入门的读书笔记,原文链接为
http://es6.ruanyifeng.com/#README

ES6

let (块级作用域)

  • let只在命令所在的代码块内有效
  • for循环适合用let
  • let不会变量提升
  • let存在暂时性死区(TDZ),在声明变量之前,该变量都是无法使用的(如let x= x就会报错)
  • 因为let的存在,typeof不再是一个百分百不会报错的操作,在TDZ中也会报错。
  • let不允许重复声明
// 报错
function func() {
     
  let a = 10;
  var a = 1;}
// 报错
function func() {
     
  let a = 10;
  let a = 1;}

不使用块级作用域的问题


var tmp = new Date();

function f() {
     
 console.log(tmp);
 if (false) {
     
   var tmp = 'hello world';
 }}

f(); // undefined

var s = 'hello';

for (var i = 0; i < s.length; i++) {
     
  console.log(s[i]);}

console.log(i); // 5

  • E55不允许在块级作用域(if,try)中函数声明(函数表达式声明倒是可以),而ES6允许。
  • ES6的函数声明类似于let不会提升

const

  • 声明常量,不可改变
  • 和let一样属于块级作用域
  • 也存在暂时性死区(TDZ),声明不可提升
  • const只保证变量指向的地址的值不变。因为变量直接指向简单类型的数据(数值、字符串、布尔值),所以保证简单类型的数据不变。而变量只指向一个地址,指向复合类型数据(对象和数组),所以无法保证复合类型的数据不变。
  • 如果想要将对象冻结,则使用Object.freeze方法
const foo = Object.freeze({
     })
  • Object.keys(obj) 能讲对象中的key以数组的方式返回

顶层对象的属性

  • 浏览器:window,self
    Node:global
    Web Worker: self

  • 缺点:

    1. 无法编译时就报出变量未声明(因为全局变量可能是顶层对象创造的,是动态的)
    2. 可能无意间创建了全局变量
    3. 顶层对象的属性到处可以读写,不利于模块化编程
    4. window对象指的是浏览器的窗口对象,不大合适。
  • ES6规定,let,const,class命令声明的全局变量不属于顶层对象的属性。

  • 函数中的this在严格模式下指向的不是顶层对象而实undefined

想要在严格模式下返回全局对象的代码(如果用了CSP,Content Security Policy就无法使用)

new Function('return this')()
  • npm安装 system.global 可以统一顶层对象为global
  1. // ES6 模块的写法
    import shim from ‘system.global/shim’; shim();
    保证各种环境里面,global对象都是存在的。

  2. // ES6 模块的写法
    import getGlobal from ‘system.global’;
    const global = getGlobal();
    上面代码将顶层对象放入变量global。

变量的解构赋值

let [a, b, c] = [1, 2, 3];
可以从数组中提取值,按照对应位置,对变量赋值。
这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。


let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null

Map与Object的区别

https://stackoverflow.com/questions/18541940/map-vs-object-in-javascript
map便于以for…of 遍历对象,以及能直接获取对象的size。

字符串

codePointAt

  • codePointAt与 for…of 方法结合能正确识别32位的UTF-16字符。

let s = '?a';for (let ch of s) {
     
  console.log(ch.codePointAt(0).toString(16));}
  
// 20bb7
// 61
  • codePointAt 是测试一个字符由两个字节还是四个字节组成的最简单的方法
function is32Bit(c) {
     
  return c.codePointAt(0) > 0xFFFF;}

is32Bit("?") // true
is32Bit("a") // false

String.fromCodePoint()

  • 能逆翻译成UTF-16编码,2,4个字节的都能翻译成。
String.fromCodePoint(0x20BB7)
// "?"

for…of 遍历字符串

普通遍历的方法无法识别4个字节的字符串,会拆分成2个。


let text = String.fromCodePoint(0x20BB7);

for (let i = 0; i < text.length; i++) {
     
  console.log(text[i]);}
// " "
// " "
for (let i of text) {
     
  console.log(i);}
// "?"

includes(),startsWith(),endsWith()

includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

repeat()

  • 参数小数向下取整
  • 参数为(-1,0]返回空
  • 参数(-∞,-1] 报错

padStart(),padEnd() 字符串补全

  • 如果省略第二个参数,则默认使用空格补全

模板字符串

  • 反引号配合${}
  • trim()消除模板字符串中的换行与空格
  • 模板字符串可以嵌套

rest参数

  • function f(a,…values){} values中包括所有传入没命名的参数,是一个数组

标签模板(tagged template)

模板字符串的功能,不仅仅是上面这些。它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能

  • 模板字符串中如果有变量的话就不能简单的传入函数了,而是先处理为多个参数,再调用函数。
let a = 5;let b = 10;

tag`Hello ${
        a + b } world ${
        a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);
  • “标签模板”的一个重要应用,就是过滤 HTML 字符串,防止用户输入恶意内容。

在往页面中添加用户输入的数据时可能是这种代码,造成页面结构破坏。


let message =
  SaferHTML`

${ sender} has sent you a message.

`
; function SaferHTML(templateData) { let s = templateData[0]; for (let i = 1; i < arguments.length; i++) { let arg = String(arguments[i]); // Escape special characters in the substitution. s += arg.replace(/&/g, "&") .replace(/, "<") .replace(/>/g, ">"); // Don't escape special characters in the template. s += templateData[i]; } return s; } let sender = ''; // 恶意代码 let message = SaferHTML`

${ sender} has sent you a message.

`
; message //

<script>alert("abc")</script> has sent you a message.

String.raw()

作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。
第一个参数为具有raw属性的对象,后面的参数不限数量。

String.raw({
      raw: 'test' }, 0, 1, 2);
// 't0e1s2t'


String.raw`Hi\n${
       2+3}!`;
// 返回 "Hi\\n5!"

正则的扩展

  • ES6允许这种写法
var regex = new RegExp(/xyz/, 'i');

new RegExp(/abc/ig, 'i').flags
// "i" 第二个参数会覆盖第一个参数的ig

正则方法

  1. match()
  2. replace()
  3. search()
  4. split()

u修饰符(unicode属性检验)

/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true
  1. 对于码点大于0xFFFF的 Unicode 字符,点字符不能识别,必须加上u修饰符。
  2. 大括号表示 Unicode 字符,需要加u修饰符
 /\u{61}/.test('a') // false
 /\u{
     61}/u.test('a') // true
  1. 加了之后量词能恢复正常
/?{2}/.test('??') // false
/?{
     2}/u.test('??') // true
  1. \S匹配所有非空白字符,也需要加u修饰符,才能识别大于0xFFFF的字符
/^\S$/.test('?') // false
/^\S$/u.test('?') // true
  1. 能识别相同字型,不会报错。
String.fromCodePoint('0x004B')
//"K"
String.fromCodePoint('0x212A')
//"K"
'K' === 'K'
//false



/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true

/[a-z]/i.test('\u004B') // true
/[a-z]/iu.test('\u004B') // true

所有正则实例对象新增了unicode属性

表示是否设置了u修饰符

const r1 = /hello/;
const r2 = /hello/u;

r1.unicode // false
r2.unicode // true

y修饰符 (sticky属性检验)

粘连(sticky)修饰符

必须保证下一个匹配的位置紧接着上一个。
(必须和g联用,不然只返回第一个)

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

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

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

sticky属性

var r = /hello\d/y;
r.sticky // true

ES5的source属性与ES6的flags属性

// ES5 的 source 属性
// 返回正则表达式的正文
/abc/ig.source
// "abc"

// ES6 的 flags 属性
// 返回正则表达式的修饰符
/abc/ig.flags
// 'gi'

s修饰符(也称dotAll模式)

让正则表达式中 , . 能匹配 行终止符(\n,\r,行分隔符,段分隔符)

后行断言


// 都想要匹配x 

//这是先行断言  /x(?=y)/或者/x(?!y)/     x需要在y前面
/\d+(?=%)/.exec('100% of US presidents have been male')  // ["100"]
/\d+(?!%)/.exec('that’s all 44 of them')                 // ["44"]

//这是后行断言   /(?<=y)x/ 或者/(?
/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill')  // ["100"]
/(?<!\$)\d+/.exec('it’s is worth about €90')                // ["90"]

  • 后行断言从右往左进行贪婪模式
/(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]
/^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]
  • 因为后行断言从后往前扫描,所以\1 需要放在小括号定义的左边。(注:此处\1代表的就是小括号包裹的内容)
    var RegExp = /^(123)(456)\2\1$/;这个正则表达式匹配到的字符串就是123456456123
/(?<=(o)d\1)r/.exec('hodor')  // null
/(?<=\1d(o))r/.exec('hodor')  // ["r", "o"]

Unicode 属性类 \p{…}和\P{…} (匹配希腊字母什么的方法)

允许正则表达式匹配符合 Unicode 某种属性的所有字符。
\p{Number} 匹配罗马数字 等等

const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π') // true

具体名匹配 在圆括号中加上 ?<组名> 然后groups属性中找

//传统
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

//具体名匹配
const RE_DATE = /(?\d{4})-(?\d{2})-(?\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

具体名匹配之后,将匹配生成的对象利用解构赋值能快速为变量赋值

let {
     groups: {
     one, two}} = /^(?.*):(?.*)$/u.exec('foo:bar');
one  // foo
two  // bar

replace()时,可以用$<组名> 引用

let re = /(?\d{4})-(?\d{2})-(?\d{2})/u;
'2015-01-02'.replace(re, '$/$/$')
// '02/01/2015'

正则表达式中引用"具体组匹配",使用\k<组名>

const RE_TWICE = /^(?[a-z]+)!\k$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false

也可以照常使用\1 ,\2这种方法

string.matchAll(regex)

  • 返回的是遍历器不是数组。
  • 遍历器也可以用for…of循环取出。
  • 因为返回的是遍历器,所以比较节省资源
  • 遍历器很容易转化为数组

// 转为数组方法一
[...string.matchAll(regex)]
// 转为数组方法二
Array.from(string.matchAll(regex));

数值的扩展

都是为了促进模块化,减少全局方法

Number的扩展

  1. 八进制要使用前缀 0o
  2. Number转化0b,0o为十进制
  3. Number.isFinite(),Number.isNaN() ,先调用Number转化为数值,再判断,与全局不同
  4. Number.parseInt(),Number.parseFloat() 移植入Number
  5. Number.isInteger
  6. Number.EPSILON, 表示 1 与大于 1 的最小浮点数之间的差。
  7. Number.isSafeInteger,Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER

Math的扩展

  1. Math.trunc() ,返回整数,去小数点后
  2. Math.sign() 判断正负
  3. Math.cbrt() 计算立方根
  4. Math.clz32() 转成32位,看前面几个0
  5. Math.imul() 传两个参,返回32位乘积,避免很大的数直接相乘低位不精确
  6. Math.fround() 返回一个数的32位单精度浮点数形式。
  7. Math.hypot() 返回所有参数的平方和的平方根。
  8. Math.expm1() 等同于 Math.exp(x) - 1
  9. Math.log1p() 等同于 Math.log(1 + x)
  10. Math.log10() 范围以10为底的x的对数。
  11. Math.log2() 范围以2为底的x的对数

指数运算符(**)

  • 右结合
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
  • 与Math.pow在特别大的运算中会有细微差别

函数的扩展

  1. 参数变量默认声明,不能用let或const再次声明
  2. 不能有同名参数
  3. 参数默认值惰性求值
  4. 通常情况下,定义了默认值的参数,应该是函数的尾参数。
function f(x, y = 5, z) {
     
  return [x, y, z];}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]
  1. 函数具有length属性,返回没有指定默认值的参数的个数(
    如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。)
(function (a = 0, b, c) {
     }).length // 0
(function (a, b = 1, c) {
     }).length // 1

作用域

函数声明时的独立作用域

var x = 1;

function f(x, y = x) {
     
  console.log(y);}

f(2) // 2

//这种行为只在声明初始化的时候形成单独的作用域

let x = 1;

function f(y = x) {
     
  let x = 2;
  console.log(y);}

f() // 1

  • f调用时, y=x形成一个独立的作用域,x么密友定义就指向外层的x。所以内部无法影响结果。

  • 参数默认值可以给一个报错函数,不给就报错

function throwIfMissing() {
     
  throw new Error('Missing parameter');}

function foo(mustBeProvided = throwIfMissing()) {
     
  return mustBeProvided;}

foo()
// Error: Missing parameter
  • 可以将参数默认值设为undefined,表明这个参数是可以省略的。

rest 参数 (返回数组)

  1. 可以利用rest参数返回数组的特性, 直接对返回的数组进行排序等操作,而不是用Array.prototype.slice.call(arguments) 来操作。
  2. rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
  3. 函数的length属性,不包括 rest 参数。

严格模式

  1. 参数默认值、解构赋值、扩展运算符(rest)一旦使用,函数内部就不能显示设定严格模式,否则报错。 (在外部全局性的设定就可以,或者把函数包在一个无参数的立即执行函数中也行。)
  2. 因为严格模式需要适用于函数体和参数,但是只有运行到函数体才能知道是严格模式,所以产生bug。

name属性

返回函数名

  1. bind返回的函数,name属性值会加上bound前缀。

箭头函数

ES6 允许使用“箭头”(=>)定义函数。

  1. 如果返回对象,必须在外面加上圆括号,否则报错
// 报错
let getTempItem = id => {
      id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({
      id: id, name: "Temp" });
  1. 只有一行语句,不需要返回值
let fn = () => void doesNotReturn();
  1. 不能作为构造函数,无法new
  2. 不可以使用arguments对象,可以用rest代替
  3. 不可以使用yield,不能用作generator函数
  4. this对象是定义时的对象,不是使用时的对象
function foo() {
     
  setTimeout(() => {
     
    console.log('id:', this.id);
  }, 100);}

var id = 21;

foo.call({
      id: 42 });
// id: 42

因为箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

7.箭头函数因为没有this,不能用call(), apply(),bind()改变this的指向

箭头函数不使用场合

  1. 定义函数的方法时不适用,因为this不指向函数而指向全局对象
  2. 动态this不应该使用。 (比如click事件中的this指向的是全局对象,而不是被点击对象)

双冒号运算符 ::

函数绑定运算符,用来取代call、apply、bind

document :: function ;
//等同于
function.bind(document)

document :: function (...values)
//等同于
function.apply(document,values)

尾调用(Tail Call)[尾调用优化]

某个函数的最后一步是调用另一个函数。
函数调用会在内存中形成‘调用记录’(也叫调用帧,call frame),保存调用位置和内部变量等信息。
如果上一个调用函数还没有结束,则会在下一个调用函数上方保存上一个函数的调用位置和变量信息,一步步则形成一个“调用栈”(call stack),这样会增加负担
如果尾调用的话则不会保存上一个调用函数的调用帧

尾递归

递归非常耗费内存,容易“栈溢出”(哈哈,stack overflow),而尾递归只存在一个调用帧,则不会“栈溢出”

//复杂度O(n)
function factorial(n) {
     
  if (n === 1) return 1;
  return n * factorial(n - 1);}

factorial(5) // 120

//复杂度O(1)

function factorial(n, total) {
     
  if (n === 1) return total;
  return factorial(n - 1, n * total);}

factorial(5, 1) // 120
斐波那契数列利用尾递归

function Fibonacci (n) {
     
  if ( n <= 1 ) {
     return 1};

  return Fibonacci(n - 1) + Fibonacci(n - 2);}

Fibonacci(10) // 89
Fibonacci(100) // 堆栈溢出
Fibonacci(500) // 堆栈溢出

function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
     
  if( n <= 1 ) {
     return ac2};

  return Fibonacci2 (n - 1, ac2, ac1 + ac2);}

Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity

尾调用优化只在严格模式下开启!正常模式无效

因为在正常模式下,函数内部有两个变量可以跟踪函数的调用栈。
func.arguments:返回调用时函数的参数。
func.caller:返回调用当前函数的那个函数。
严格模式禁用这两个变量 。(不过不能在函数内部声明严格模式的同时用箭头函数哦!)

正常模式下进行尾递归优化


function tco(f) {
     
  var value;
  var active = false;
  var accumulated = [];

  return function accumulator() {
     
    accumulated.push(arguments);
    if (!active) {
     
      active = true;
      while (accumulated.length) {
     
        value = f.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  };}

var sum = tco(function(x, y) {
     
  if (y > 0) {
     
    return sum(x + 1, y - 1)
  }
  else {
     
    return x
  }});

sum(1, 100000)
// 100001

ES6允许函数的参数最后存在尾逗号

使得版本管理系统不会显示多余的变动

数组的扩展

扩展运算符( … )

和rest参数相反,将数组转为逗号分隔的参数序列

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

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [
,
,
]

替代apply方法

用扩展运算符替代apply将数组转化为函数的参数。

// ES5 的写法
function f(x, y, z) {
     
  // ...
}

var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
     
  // ...
}

let args = [0, 1, 2];f(...args);

扩展运算符的应用

  1. 复制数组
//ES5
const a1 = [1, 2];
const a2 = a1.concat();
//ES6
const a2 = [...a1]const [...a2] = a1
  1. 合并数组
[...arr1,...arr2,...arr3]
和concat一样是浅拷贝,如果数组中的对象属性或方法改变,会被影响。
  1. 与解构赋值结合
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
  1. 字符串 (能识别四个字节的unicode字符)
[...'hello']
// [ "h", "e", "l", "l", "o" ]

'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3
  1. 扩展运算符需要iterator接口,如果没有可以自己设置,如
Number.prototype[Symbol.iterator] = function*() {
     
  let i = 0;
  let num = this.valueOf();
  while (i < num) {
     
    yield i++;
  }}

console.log([...5]) // [0, 1, 2, 3, 4]
  1. Map,Set和Generator函数都可以使用扩展运算符

Map和Set具有Iterator接口。
Generator 运行后会返回遍历器对象。

//Map
let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],]);

let arr = [...map.keys()]; // [1, 2, 3]

//Generator
const go = function*(){
     
  yield 1;
  yield 2;
  yield 3;};

[...go()] // [1, 2, 3]

Array.from()

可以将类数组(array-like object)和可遍历(iterable)的对象(包括Set和Map)转化为数组。
可以接收第二个参数(函数),用来将每个元素进行处理。类似于map方法

主要的类数组:

  1. DOM操作返回的NodeList集合 (querySelectorAll等方法返回的集合)
  2. arguments对象

主要的iterable的对象

  1. 字符串
  2. Set
  3. Map

一个应用是将字符串转化为Unicode字符,能正确识别4个字节的unicode字符

function countSymbols(string) {
     
  return Array.from(string).length;}

Array.of() — 用来代替Array()或new Array()

将一组值转化为数组,主要为了弥补构造函数Array()的不足

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

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

数组实例的copyWithin()

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

Array.prototype.copyWithin(target, start = 0, end = this.length)

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

数组实例的find() 和 findIndex()

都能发现NaN,弥补了数组indexOf方法的不足

[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
find()

不符合返回undefined ,符合返回第一个
三个参数:当前的值、当前的位置、原数组

[1, 4, -5, -10,-100].find((value,index,arr) => value < 0)
// -5
findIndex()

不符合返回-1,符合返回第一个的位置
三个参数:当前的值、当前的位置、原数组

[1, 5, 10, 15].findIndex(function(value, index, arr) {
     
  return value > 9;}) // 2

find和findIndex都能接受第二个参数,绑定回调函数中this的对象。

//重要!!!
function f(v){
     
  return v > this.age;}let person = {
     name: 'John', age: 20};[10, 12, 26, 15].find(f, person);    // 26

数组实例fill()

fill方法使用给定值,填充数组
第二第三个参数指定起始与结束位置

['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]

fill使用对象的话只填充对象的地址,所以是浅拷贝

数组实例 entries() keys() values()

用于遍历数组,返回一个遍历器对象,可以用for…of遍历

数组实例 includes()

返回布尔值,判定是否包含。
类似于字符串的Includes方法
第二个参数为起始位置
比indexOf的优点在于indexOf内部使用===判断,会误判NaN

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

数组实例 flat(),flatMap()

flat

将嵌套的数组拉平,返回一个新数组.
只会拉平第一层,如果向多层需要传入层数,默认为1,可以传Infinity

[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
flatMap

对原数组的每一个成员先执行类似于map的方法,然后flat()
只能展开一层数组
函数也接受3个参数,当前成员,当前位置,原数组

// 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]

数组的空位

空位不是undefined ,是有值的

0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false

ES6,Array.from将空位转化成undefined。
扩展运算符也会将空位转为undefined
copyWithin()也会一起拷贝
fill视空位为正常的位置
for…of也会遍历空位
entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。

对象的扩展

简写

  1. 允许对象中直接写变量。 此时属性名为变量名,属性值为变量值
function f(x, y) {
     
  return {
     x, y};}
f(1, 2) // Object {x: 1, y: 2}

//CommonJS 模块输出时就适合简写
module.exports = {
      getItem, setItem, clear };
// 等同于
module.exports = {
     
  getItem: getItem,
  setItem: setItem,
  clear: clear
};
  1. 方法简写
const o = {
     
  method() {
     
    return "Hello!";
  }};

属性名表达式

对象的属性名可以用表达式

let propKey = 'foo';

let obj = {
     
  [propKey]: true,
  ['a' + 'bc']: 123};

属性名表达式如果传入对象的话会把对象转为字符串 [object Object] !!!!!

方法的name属性

方法的name属性,返回方法名。 像函数的name属性返回函数名一样

有getter和setter的对象的特例

const obj = {
     
  get foo() {
     },
  set foo(x) {
     }};

obj.foo.name
// TypeError: Cannot read property 'name' of undefined

const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');

descriptor.get.name // "get foo"
descriptor.set.name // "set foo"

Object.getOwnPropertyDescriptor

对象的属性的描述对象

let obj = {
      foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
     
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

以下操作会忽略enumerable为false的属性

  1. for…in
  2. Object.keys()
  3. JSON.stringify()
  4. Object.assign()

其中只有for…in会返回继承的属性,其他都忽略。

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

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

属性的遍历

能遍历对象属性的方法

方法名 遍历内容
for…in 对象自身和继承的可枚举属性(不含Symbol属性)
Object.keys(obj) 返回对象自身(不含继承)所有可枚举属性的键名(不含Symbol属性)
Object.getOwnPropertyNames(obj) 和Object.keys一样,但能遍历不可枚举的属性
Object.getOwnPropertySymbols(obj) 返回对象自身所有Symbol属性的键名
Reflect.ownKeys(obj) (最全)返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

super

指向当前对象的原型对象
只能用在对象的方法中,其他地方都会报错

super.foo等同于Object.getPrototypeOf(this).foo(属性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

目前必须用对象方法的简写法super才能被识别

//这样简写
const obj = {
     
  foo: 'world',
  find() {
     
    return super.foo;
  }};

对象的解构赋值

扩展运算符的解构赋值不能继承原型对象的属性
扩展运算符用于提出对象的所有可遍历属性,拷贝到当前对象之中。

对象的扩展运算符等同于使用Object.assign()

完全拷贝一个对象(包括原型)


// 写法一(非浏览器环境不一定部署)
const clone1 = {
     
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};
// 写法二
const clone2 = Object.assign(
  Object.create(Object.getPrototypeOf(obj)),
  obj
);
// 写法三
const clone3 = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj))


对象的新增方法

Object.is() 同值相等算法

与“===”的不同之处在于补足了缺点。
认为+0和-0不同, 认为NaN与NaN相同

+0 === -0 //true
NaN === NaN // false

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

Object.assign()

存在同名属性时,后面的属性会覆盖前面的
只拷贝对象属性和symbol属性,不拷贝继承也不拷贝不可枚举的属性
浅拷贝

const target = {
      a: 1, b: 1 };

const source1 = {
      b: 2, c: 2 };
const source2 = {
      c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

需要可枚举属性

const v1 = 'abc';
const v2 = true;
const v3 = 10;

const obj = Object.assign({
     }, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }


//可以用Object函数查看
Object(true) // {[[PrimitiveValue]]: true}
Object(10)  //  {[[PrimitiveValue]]: 10}
Object('abc') // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}

可以处理数组,将数组视为对象,属性名0、1、2的对象

Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]

Object.assign常见用法

  1. 为对象添加属性
class Point {
     
    constructor(x,y){
     
        Object.assign(this,{
     x,y})
    }
}
  1. 为对象添加方法
Object.assign(SomeClass.prototype, {
     
  someMethod(arg1, arg2) {
     
    ···
  },
  anotherMethod() {
     
    ···
  }});
// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) {
     
  ···
};
SomeClass.prototype.anotherMethod = function () {
     
  ···
};
  1. 克隆对象
//先把origin中的原型属性赋予originProto的实例属性,然后根据originProto创建一个新的具有原型方法的target对象,传给assign。
function clone(origin) {
     
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);}
  1. 合并多个对象
const merge =
  (...sources) => Object.assign({
     }, ...sources);
  1. 为属性指定默认值

由于是浅拷贝 所以值最好是简单类型,不要指向一个对象。

const DEFAULTS = {
     
  logLevel: 0,
  outputFormat: 'html'};

function processContent(options) {
     
  options = Object.assign({
     }, DEFAULTS, options);
  console.log(options);
  // ...
}

Object.getOwnPropertyDescriptors()

返回对象属性(非继承属性)的描述对象
主要为了解决Object.assign()无法正确拷贝get和set属性
因为Object.assign方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。

Object.assign不能正确拷贝get,set属性的解决方法 (使用Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法)
const source = {
     
  set foo(value) {
     
    console.log(value);
  }};

const target2 = {
     };
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
//   set: [Function: set foo],
//   enumerable: true,
//   configurable: true }
也可以克隆对象(浅拷贝)
//Object.create第二个参数需要是描述对象,用来赋予实例属性
const shallowClone = (obj) => Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj));

__proto__

调用的实际上是Object.prototype.__proto__

推荐用Object.setPrototypeOf() 和 Object.getPrototypeOf()

Object.entries的妙用

将Object转化为Map解构
将key和value以数组的形式输出

const obj = {
      foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

Object.fromEntries()

Object.entries的你操作
将Map转回为Object

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]])
// { foo: "bar", baz: 42 }

可以配合URLSearchParams对象,将查询字符转为对象。

Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }

Symbol

前六种数据类型

  1. undefined
  2. null
  3. Boolean
  4. String
  5. Number
  6. Object

每一个Symbol值都是不相等的
不能使用new命令。因为Symbol不是对象,而实一个原始类型的值
可以传入字符串,表示描述
Symbol 值不能与其他类型的值进行运算,会报错。
Symbol可以toString或String()转化为字符串
Symbol也可以转化为布尔值 Boolean()

作为属性名

let mySymbol = Symbol();
// 第一种写法
let a = {
     };
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
     
  [mySymbol]: 'Hello!'};
// 第三种写法
let a = {
     };
Object.defineProperty(a, mySymbol, {
      value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

不能用点运算符,因为点运算符后面总是字符串,会将其识别为字符串
再对象内部定义必须放在方括号之中,不然也被认为是字符串

不会被发现 会被发现
for…in,for…of,Object.keys(),Object.getOwnPropertyNames()、JSON.stringify() Object.getOwnPropertySymbols

当然用 Reflect.ownKeys 可以获取所有的。

Symbol.for() ,Symbol.keyFor()

Symbol.for()

能够重复使用同一个值。
Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。

Symbol.for("bar") === Symbol.for("bar")
// true
Symbol("bar") === Symbol("bar")
// false
Symbol.keyFor()
//必须用Symbol.for()等级过的Symbol才能用Symbol.keyFor()寻找
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

Symbol.for登记一定是全局环境的,在其他地方取到的是同一个值

模块的Singleton模式

Singleton 模式指的是调用一个类,任何时候返回的都是同一个实例。
但是返回的实例一般都放在顶层对象中,容易被在其他模块中修改。

防止这种情况可以用Symbol.for()

// mod.js
const FOO_KEY = Symbol.for('foo');

function A() {
     
  this.foo = 'hello';}

if (!global[FOO_KEY]) {
     
  global[FOO_KEY] = new A();}

module.exports = global[FOO_KEY];


// 引用

global[Symbol.for('foo')] = {
      foo: 'world' };

const a = require('./mod.js');

内置的Symbol值

1. Symbol.hasInstance (配合instanceof)

在调用instanceof时触发

class MyClass {
     
  [Symbol.hasInstance](foo) {
     
    return foo instanceof Array;
  }}

[1, 2, 3] instanceof new MyClass() // true
2. Symbol.isConcatSpreadable (配合concat)

设置后决定用concat方法时是否可以展开

let arr1 = ['c', 'd'];['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
arr1[Symbol.isConcatSpreadable] // undefined
let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
3. Symbol.species

解决 instanceof 有多个父类的问题
调用该属性指定的构造函数

class MyArray extends Array {
     
  static get [Symbol.species]() {
      return Array; }}

const a = new MyArray();
const b = a.map(x => x);

b instanceof MyArray // false
b instanceof Array // true
4. Symbol.match (配合match方法)

执行str.match(obj)方法时调用

String.prototype.match(regexp)
// 等同于
regexp[Symbol.match](this)

class MyMatcher {
     
  [Symbol.match](string) {
     
    return 'hello world'.indexOf(string);
  }}

'e'.match(new MyMatcher()) // 1

5.Symbol.replace (调用replace时调用)

const x = {
     };
x[Symbol.replace] = (...s) => console.log(s);

'Hello'.replace(x, 'World') // ["Hello", "World"]

6.Symbol.search (配合search方法)

String.prototype.search(regexp)
// 等同于
regexp[Symbol.search](this)

class MySearch {
     
  constructor(value) {
     
    this.value = value;
  }
  [Symbol.search](string) {
     
    return string.indexOf(this.value);
  }}'foobar'.search(new MySearch('foo')) // 0

7.Symbol.split (配合split方法)

class MySplitter {
     
  constructor(value) {
     
    this.value = value;
  }
  [Symbol.split](string) {
     
    let index = string.indexOf(this.value);
    if (index === -1) {
     
      return string;
    }
    return [
      string.substr(0, index),
      string.substr(index + this.value.length)
    ];
  }}

'foobar'.split(new MySplitter('foo'))
// ['', 'bar']
'foobar'.split(new MySplitter('bar'))
// ['foo', '']
'foobar'.split(new MySplitter('baz'))
// 'foobar'

8.Symbol.iterator (配合for…of循环)

对象的Symbol.iterator属性,指向对象的默认遍历器方法

const myIterable = {
     };
myIterable[Symbol.iterator] = function* () {
     
  yield 1;
  yield 2;
  yield 3;};

[...myIterable] // [1, 2, 3]


class Collection {
     
  *[Symbol.iterator]() {
     
    let i = 0;
    while(this[i] !== undefined) {
     
      yield this[i];
      ++i;
    }
  }}

let myCollection = new Collection();
myCollection[0] = 1;
myCollection[1] = 2;

for(let value of myCollection) {
     
  console.log(value);}
// 1
// 2

9.Symbol.toPrimitive (对象被转为原始类型时调用)

有三种模式
Number:该场合需要转成数值
String:该场合需要转成字符串
Default:该场合可以转成数值,也可以转成字符串

let obj = {
     
  [Symbol.toPrimitive](hint) {
     
    switch (hint) {
     
      case 'number':
        return 123;
      case 'string':
        return 'str';
      case 'default':
        return 'default';
      default:
        throw new Error();
     }
   }};

2 * obj // 246
3 + obj // '3default'
obj == 'default' // true
String(obj) // 'str'

10.Symbol.toStringTag (调用toString方法时运用)

可以定制[object Object]或[object Array]中object后面的那个字符串。

// 例一
({
     [Symbol.toStringTag]: 'Foo'}.toString())
// "[object Foo]"

// 例二
class Collection {
     
  get [Symbol.toStringTag]() {
     
    return 'xxx';
  }}
  let x = new Collection();
Object.prototype.toString.call(x) // "[object xxx]"
11.Symbol.unscopables (指定使用with关键字时哪些会被排除)
// 没有 unscopables 时
class MyClass {
     
  foo() {
      return 1; }}

var foo = function () {
      return 2; };

with (MyClass.prototype) {
     
  foo(); // 1
}
// 有 unscopables 时
class MyClass {
     
  foo() {
      return 1; }
  get [Symbol.unscopables]() {
     
    return {
      foo: true };
  }}

var foo = function () {
      return 2; };

with (MyClass.prototype) {
     
  foo(); // 2
}

Set和Map数据解构

Set

构造函数
类似于数组,但没有重复的值
可接受一个数组(或具有iterable接口的数据结构)
用的是“Same-value-zero equality”,认为NaN等于自身
Set没有键名 ,只有键值(或者说键名和键值是同一个值)

//比较实用的案例
const set = new Set(document.querySelectorAll('div'));
set.size // 56


//用于去除字符串中的重复字符

两个对象总是不相等的

let set = new Set();

set.add({
     });set.size // 1
set.add({
     });set.size // 2
Set实例的四个方法
  1. add(value):添加某个值,返回 Set 结构本身。
  2. delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  3. has(value):返回一个布尔值,表示该值是否为Set的成员。
  4. clear():清除所有成员,没有返回值。

Array.from可以将Set转化为数组

Array.from(new Set(array))
//可以去除数组重复成员

//这是直接将Set转化为数组的方法
[...set]
Set实例的四个遍历方法

Set的遍历顺序就是插入顺序

  1. keys():返回键名的遍历器
  2. values():返回键值的遍历器 (和keys方法完全一样,因为set没有键名)
  3. entries():返回键值对的遍历器 ([key,value]两个值完全一样)
  4. forEach():使用回调函数遍历每个成员
实现并集,交集和差集
let a = new Set([1, 2, 3]);let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

WebSet

WeakSet的成员只能是对象
WeakSet的参数可以接受数组,类数组(具有Iterable接口的对象都行)
弱引用 (垃圾回收极致不考虑WeakSet对对象的引用)
因为弱引用的原因,内部成员运行前后可能个数不一样,所以不可遍历

方法
  1. WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
  2. WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
  3. WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。

Map

各种类型的值都可以当作键

Map可以接受数组(以及任何具有Iterator接口的、双元素数组的数据解构)作为参数,实际算法如下
接受的数组需要是[[1,2],[3,4]] 这样的双元素数组

const items = [
  ['name', '张三'],
  ['title', 'Author']];

const map = new Map();

items.forEach(
  ([key, value]) => map.set(key, value));

只有对同一个对象的引用,Map 结构才将其视为同一个键。

// 这两个值内存地址不一样
const map = new Map();

map.set(['a'], 555);
map.get(['a']) // undefined

0和-0是一个键
NaN 是同一个键

属性与方法
  1. size
  2. set(key,value) 因为返回的是当前Map对象,可以链式写法
  3. get(key)
  4. has(key)
  5. delete(key)
  6. clear()

遍历方法

Map的遍历顺序是插入顺序

  1. keys()
  2. values()
  3. entries()
  4. forEach()

Map解构的默认遍历器接口是entries方法
扩展运算符能快速将Map转化为数组,然后运用数组的map和filter方法
Map自身也有forEach方法,可接受第二个参数,绑定this

与其他数据解构的互相转换
转换目标 方法 逆方法
数组 扩展运算符(…) 传入Map构造函数
对象 strMapToObj(方法见下面) objToStrMap
JSON strMapToJson或mapToArrayJson jsonToStrMap
//如果键名非字符串,会被转化为字符串,会有损


function strMapToObj(strMap) {
     
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
     
    obj[k] = v;
  }
  return obj;}


function objToStrMap(obj) {
     
  let strMap = new Map();
  for (let k of Object.keys(obj)) {
     
    strMap.set(k, obj[k]);
  }
  return strMap;}


//Map键名都是字符串,可以转化为对象JSON
function strMapToJson(strMap) {
     
  return JSON.stringify(strMapToObj(strMap));}

//Map 的键名有非字符串,这时可以选择转为数组 JSON。
function mapToArrayJson(map) {
     
  return JSON.stringify([...map]);}


function jsonToStrMap(jsonStr) {
     
  return objToStrMap(JSON.parse(jsonStr));}

WeakMap (典型场合,以DOM节点作为键名)

只接受对象作为键名
弱引用 ( 弱引用的只是键名,不是键值。键值是正常引用)

const wm = new WeakMap();let key = {
     };let obj = {
     foo: 1};

wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}

也没有遍历操作和size属性

只有四个方法,get(),set(),has(),delete()

适合用于部署私有属性,删除实例后,就会随之消失,不会造成内存泄漏

const _counter = new WeakMap();
const _action = new WeakMap();

class Countdown {
     
  constructor(counter, action) {
     
    _counter.set(this, counter);
    _action.set(this, action);
  }
  dec() {
     
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
     
      _action.get(this)();
    }
  }}

const c = new Countdown(2, () => console.log('DONE'));

c.dec()
c.dec()
// DONE

Proxy

用于修改操作的默认行为,是一种"元编程"(meta programming)
在目标对象前进行拦截,可以对外节访问过滤和改写

ES6提供的Proxy构造函数

//写法
var proxy = new Proxy(target, handler);


//例子
var proxy = new Proxy({
     }, {
     
  get: function(target, property) {
     
    return 35;
  }});

proxy.time // 35
proxy.name // 35
proxy.title // 35
  1. 可以将Proxy对象设置成对象的属性
var object = {
      proxy: new Proxy(target, handler) };
  1. 可以将Proxy对象设置成原型对象
var proxy = new Proxy({
     }, {
     
  get: function(target, property) {
     
    return 35;
  }});

let obj = Object.create(proxy);
obj.time // 35

//访问不存在的对象属性的话会进入原型对象中寻找。
//可以拦截多个操作
var handler = {
     
  get: function(target, name) {
     
    if (name === 'prototype') {
     
      return Object.prototype;
    }
    return 'Hello, ' + name;
  },

  apply: function(target, thisBinding, args) {
     
    return args[0];
  },

  construct: function(target, args) {
     
    return {
     value: args[1]};
  }};

var fproxy = new Proxy(function(x, y) {
     
  return x + y;}, handler);

fproxy(1, 2) // 1
new fproxy(1, 2) // {value: 2}
fproxy.prototype === Object.prototype // true
fproxy.foo === "Hello, foo" // true
Proxy支持的handler拦截操作
方法名 作用
get(target, propKey, receiver) 拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]。
set(target, propKey, value, receiver) 拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v,返回一个布尔值。 需要返回true
has(target, propKey) 拦截propKey in proxy的操作,返回一个布尔值。(对for…in不生效)
deleteProperty(target, propKey) 拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target) 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 返回的数组只能是字符串或Symbol值 。
getOwnPropertyDescriptor(target, propKey) 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc) 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target) 拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target) 拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target) 拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto) 拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
apply(target, ctxobj, args) 拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。
construct(target, args) 拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。必须返回一个对象

receiver指原始读操作的那个对象 target指传入的对象

const proxy = new Proxy({
     }, {
     
  get: function(target, property, receiver) {
     
    return receiver;
  }});

const d = Object.create(proxy);
d.a === d // true

如果属性不可配置且不可写,proxy读此属性会报错

严格模式下 set代理需要返回true,否则会报错

如果obj对象禁止扩展,则用has拦截就会报错

var obj = {
      a: 10 };
Object.preventExtensions(obj);

var p = new Proxy(obj, {
     
  has: function(target, prop) {
     
    return false;
  }});

'a' in p // TypeError is thrown

Proxy.revocable()

返回一个可取消的Proxy实例

let target = {
     };let handler = {
     };

let {
     proxy, revoke} = Proxy.revocable(target, handler);

proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked

一种情况是,目标对象不允许直接访问,必须代理访问,访问结束就要收回代理权.

this问题

使用proxy代理后 this指向的就是proxy而不是target
用bind绑定原始对象就好了

Reflect

  1. 将Object的一些方法移植到Reflect上
  2. 修改一些Object的方法的返回结果,使其更合理
// 老写法
try {
     
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
     
  // failure
}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
     
  // success
} else {
     
  // failure
}

//不抛出错误 ,而是返回false
  1. 让Object操作变为函数行为
// 老写法
'assign' in Object // true

// 新写法
Reflect.has(Object, 'assign') // true
  1. Reflect对象的方法与Proxy对象的方法一一对应

**拦截target对象的方法 ,采用Reflect方法,确保完成原有的行为后,再部属别的功能 **

Proxy(target, {
     
  set: function(target, name, value, receiver) {
     
    var success = Reflect.set(target,name, value, receiver);
    if (success) {
     
      console.log('property ' + name + ' on ' + target + ' set to ' + value);
    }
    return success;
  }});

另一个例子

var loggedObj = new Proxy(obj, {
     
  get(target, name) {
     
    console.log('get', target, name);
    return Reflect.get(target, name);
  },
  deleteProperty(target, name) {
     
    console.log('delete' + name);
    return Reflect.deleteProperty(target, name);
  },
  has(target, name) {
     
    console.log('has' + name);
    return Reflect.has(target, name);
  }});
Reflect的13个静态方法

Reflect.apply(target, thisArg, args)
//等于 Function.prototype.apply.call(func, thisArg, args)

Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target,name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target,name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)

使用Proxy实现观察者模式
const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {
     set});

function set(target, key, value, receiver) {
     
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
  }

Promise

异步编程的一种解决方案
一个保存着某个未来菜会结束的事件的容器(通常是指异步操作)
只有异步操作的结果才能决定Promise的对象状态 所以命名Promise

Promise对象有以下两个特点

特点 描述
对象的状态不受外界影响 有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
一旦状态改变,就不会再变 Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

Promise的缺点:

  1. 无法取消Promise,一旦新建就立即执行,中途无法取消
  2. 如果不设置回调函数,内部错误不会反应到外部
  3. 处于pending时,无法得知进展

所以如果某些事件不断反复发生,Stream模式比Promise好。

Generator

Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

两个特征:

  1. function关键字与函数名之间有一个星号
  2. 函数体内部使用yield表达式,定义不同的内部状态
function* helloWorldGenerator() {
     
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

yield表达式

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

next的运行逻辑

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

需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。

  • yield 与return的区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield表达式。
  • Generator 函数可以不用yield表达式,这时就变成了一个单纯的暂缓执行函数。
function* f() {
     
  console.log('执行了!')
}

var generator = f();

setTimeout(function () {
     
  generator.next()
}, 2000);


//此函数只有在调用next方法时才会执行
  • yield表达式只能在Generator函数中使用
var arr = [1, [[2, 3], 4], [5, 6]];

var flat = function* (a) {
     
  a.forEach(function (item) {
     
    if (typeof item !== 'number') {
     
      yield* flat(item);
    } else {
     
      yield item;
    }
  });
};

for (var f of flat(arr)){
     
  console.log(f);
}

//这里在forEach的方法里使用了,forEach的参数是一个普通函数,用了yield就会报错
//所以应该改成for循环

var arr = [1, [[2, 3], 4], [5, 6]];

var flat = function* (a) {
     
  var length = a.length;
  for (var i = 0; i < length; i++) {
     
    var item = a[i];
    if (typeof item !== 'number') {
     
      yield* flat(item);
    } else {
     
      yield item;
    }
  }
};

for (var f of flat(arr)) {
     
  console.log(f);
}
// 1, 2, 3, 4, 5, 6
  • yield表达式如果放在另一个表达式中,必须放在圆括号里面
function* demo() {
     
  console.log('Hello' + yield); // SyntaxError
  console.log('Hello' + yield 123); // SyntaxError

  console.log('Hello' + (yield)); // OK
  console.log('Hello' + (yield 123)); // OK
}
  • yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
function* demo() {
     
  foo(yield 'a', yield 'b'); // OK
  let input = yield; // OK
}

与iterator的关系

任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。

由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

var myIterable = {
     };
myIterable[Symbol.iterator] = function* () {
     
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3]

Generator 函数执行后,返回一个遍历器对象。该对象本身也具有Symbol.iterator属性,执行后返回自身。

function* gen(){
     
  // some code
}

var g = gen();

g[Symbol.iterator]() === g
// true

next方法的参数

yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

function* f() {
     
  for(var i = 0; true; i++) {
     
    var reset = yield i;
    if(reset) {
      i = -1; }
  }}

var g = f();

g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.next(true) // { value: 0, done: false }

通过next方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。


function* foo(x) {
     
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

//在这个方法中不停的输入参数改变yield的返回值
// 5+12*2+13 = 42

for…of循环

for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法。

function* foo() {
     
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
     
  console.log(v);
}
// 1 2 3 4 5
//利用Generator函数和for...of循环,实现斐波那契数列

function* fibonacci() {
     
  let [prev, curr] = [0, 1];
  for (;;) {
     
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

for (let n of fibonacci()) {
     
  if (n > 1000) break;
  console.log(n);
}
//可以为任意对象用Generator增加一个接口,然后用for...of遍历

function* objectEntries(obj) {
     
  let propKeys = Reflect.ownKeys(obj);

  for (let propKey of propKeys) {
     
    yield [propKey, obj[propKey]];
  }
}

let jane = {
      first: 'Jane', last: 'Doe' };


//方法一:(较麻烦)
for (let [key, value] of objectEntries(jane)) {
     
  console.log(`${
       key}: ${
       value}`);
}
// first: Jane
// last: Doe

//方法二:(直接加到Symbol.iterator属性上)
jane[Symbol.iterator] = objectEntries;

for (let [key, value] of jane) {
     
  console.log(`${
       key}: ${
       value}`);
}
// first: Jane
// last: Doe

出了for…of以外,扩展运算符,解构赋值和Array.from()方法调用的其实都是遍历器接口。

这意味着他们都能将Generator函数返回的对象作为参数

function* numbers () {
     
  yield 1
  yield 2
  return 3
  yield 4
}

// 扩展运算符
[...numbers()] // [1, 2]

// Array.from 方法
Array.from(numbers()) // [1, 2]

// 解构赋值
let [x, y] = numbers();
x // 1
y // 2

// for...of 循环
for (let n of numbers()) {
     
  console.log(n)
}
// 1
// 2

Generator.prototype.throw()

Generator函数返回的对象都有throw方法,可以在Generator外部抛出错误,然而却能在Generator函数内部捕获

var g = function* () {
     
  try {
     
    yield;
  } catch (e) {
     
    console.log('内部捕获', e);
  }
};

var i = g();
i.next();

try {
     
  i.throw('a');
  i.throw('b');
} catch (e) {
     
  console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b

//内部只捕获一次
  • throw方法可以接受一个参数,这个参数会被Generator函数体里的catch方法接收,所以抛出Error对象比较好

  • 遍历器对象用throw方法抛出的错误要被内部捕获,必须至少执行过一次next方法。因为执行一次next方法才启动执行Generator函数的内部代码

  • throw方法被捕获以后,会附带执行下一条yield表达式。也就是说,会附带执行一次next方法。

var gen = function* gen(){
     
  try {
     
    yield console.log('a');
  } catch (e) {
     
    // ...
  }
  yield console.log('b');
  yield console.log('c');
}

var g = gen();
g.next() // a
g.throw() // b
g.next() // c

这种函数体内捕获错误的机制,大大方便了对错误的处理。多个yield表达式,可以只用一个try...catch代码块来捕获错误。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数内部写一个错误处理语句,现在只在 Generator 函数内部写一次catch语句就可以了。

函数体外捕获函数内部的错误

Generator 函数体内抛出的错误,也可以被函数体外的catch捕获。

function* foo() {
     
  var x = yield 3;
  var y = x.toUpperCase();
  yield y;
}

var it = foo();

it.next(); // { value:3, done:false }

try {
     
  it.next(42);
} catch (err) {
     
  console.log(err);
}

Generator.prototype.return()

Generator 函数返回的遍历器对象除了next,throw还有一个return方法,可以返回特定的值同时终结Generator函数

function* gen() {
     
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

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

function* numbers () {
     
  yield 1;
  try {
     
    yield 2;
    yield 3;
  } finally {
     
    yield 4;
    yield 5;
  }
  yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }

next()、throw()、return() 的共同点

它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式。

yield* 表达式

yield*表达式,用来在一个 Generator 函数里面执行另一个 Generator 函数。

function* foo() {
     
  yield 'a';
  yield 'b';
}

function* bar() {
     
  yield 'x';
  yield* foo();
  yield 'y';
}

// 等同于
function* bar() {
     
  yield 'x';
  yield 'a';
  yield 'b';
  yield 'y';
}
  • 如果yield表达式后面跟的是一个遍历器对象,需要在yield表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*表达式。

  • yield*后面的 Generator 函数(没有return语句时),等同于在 Generator 函数内部,部署一个for...of循环。

// 如果`yield*`后面跟着一个数组,由于数组原生支持遍历器,因此就会遍历数组成员。

function* gen(){
     
  yield* ["a", "b", "c"];
}

gen().next() // { value:"a", done:false }

//不加星号返回的是整个数组
//任何数据结构只要有iterator接口,都可以被yield* 遍历
  • 如果被代理的 Generator 函数有return语句,那么就可以向代理它的 Generator 函数返回数据。
function* foo() {
     
  yield 2;
  yield 3;
  return "foo";
}

function* bar() {
     
  yield 1;
  var v = yield* foo();
  console.log("v: " + v);
  yield 4;
}

var it = bar();

it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}
  • yield* 的另外一个例子
function* genFuncWithReturn() {
     
  yield 'a';
  yield 'b';
  console.log('test')
  return 'The result';
}
function* logReturned(genObj) {
     
  let result = yield* genObj;
  console.log(result);
}

[...logReturned(genFuncWithReturn())]
// test
// The result
// 值为 [ 'a', 'b' ]
  • yield*命令可以很方便地取出嵌套数组的所有成员。
function* iterTree(tree) {
     
  if (Array.isArray(tree)) {
     
    for(let i=0; i < tree.length; i++) {
     
      yield* iterTree(tree[i]);
    }
  } else {
     
    yield tree;
  }
}

const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];

for(let x of iterTree(tree)) {
     
  console.log(x);
}
// a
// b
// c
// d
// e

作为对象属性的Generator函数

如果一个对象的属性是 Generator 函数,可以简写成下面的形式。

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

//等价于

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

Generator 函数的this

Generator返回的总是遍历器对象,而不是this对象。

function* g() {
     
  this.a = 11;
}

let obj = g();
obj.next();
obj.a // undefined

既可以用next方法,又可以获得正常的this的变通的方法

  • 生成一个空对象
  • 使用call方法绑定 Generator 函数内部的this
function* F() {
     
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}
var obj = {
     };
var f = F.call(obj);

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

obj.a // 1
obj.b // 2
obj.c // 3
  • 如果想要将两个统一的话可以将obj换成F.prototype
function* F() {
     
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}
var f = F.call(F.prototype);

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

f.a // 1
f.b // 2
f.c // 3
  • 如果再将F改成构造函数,就可以new了
function* gen() {
     
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}

function F() {
     
  return gen.call(gen.prototype);
}

var f = new F();

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

f.a // 1
f.b // 2
f.c // 3

Generator与状态机

反复改变状态可以用Generator实现

var clock = function* () {
     
  while (true) {
     
    console.log('Tick!');
    yield;
    console.log('Tock!');
    yield;
  }
};

半协程的Generator函数

Generator 函数是 ES6 对协程的实现,但属于不完全实现。Generator 函数被称为“半协程”(semi-coroutine),意思是只有 Generator 函数的调用者,才能将程序的执行权还给 Generator 函数。如果是完全执行的协程,任何函数都可以让暂停的协程继续执行。

如果将 Generator 函数当作协程,完全可以将多个需要互相协作的任务写成 Generator 函数,它们之间使用yield表达式交换控制权。

Generator函数的应用

  1. 异步操作的同步化表达
  • Generator 函数的暂停执行的效果,意味着可以把异步操作写在yield表达式里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield表达式下面,反正要等到调用next方法时再执行。

    function* loadUI() {
           
      showLoadingScreen();
      yield loadUIDataAsynchronously();
      hideLoadingScreen();
    }
    var loader = loadUI();
    // 加载UI
    loader.next()
    
    // 卸载UI
    loader.next()
    
  • Generator函数与Ajax的结合

    function* main() {
           
      var result = yield request("http://some.url");
      var resp = JSON.parse(result);
        console.log(resp.value);
    }
    
    function request(url) {
           
      makeAjaxCall(url, function(response){
           
        it.next(response);
      });
    }
    
    var it = main();
    it.next()
    
  • Generator函数与异步操作(读取文件结合)

    function* numbers() {
           
      let file = new FileReader("numbers.txt");
      try {
           
        while(!file.eof) {
           
          yield parseInt(file.readLine(), 10);
        }
      } finally {
           
        file.close();
      }
    }
    
  1. 控制流管理
  • 多步操作

    function* longRunningTask(value1) {
           
      try {
           
        var value2 = yield step1(value1);
        var value3 = yield step2(value2);
        var value4 = yield step3(value3);
        var value5 = yield step4(value4);
        // Do something with value4
      } catch (e) {
           
        // Handle any error from step1 through step4
      }
    }
    
    //然后使用一个函数按次序执行所有步骤
    scheduler(longRunningTask(initialValue));
    
    function scheduler(task) {
           
      var taskObj = task.next(task.value);
      // 如果Generator函数未结束,就继续调用
      if (!taskObj.done) {
           
        task.value = taskObj.value
        scheduler(task);
      }
    }
    
  1. 部署Iterator接口

利用 Generator 函数,可以在任意对象上部署 Iterator 接口。

  1. 作为数据结构

Generator 可以看作是数据结构,更确切地说,可以看作是一个数组结构,因为 Generator 函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口。

function* doStuff() {
     
  yield fs.readFile.bind(null, 'hello.txt');
  yield fs.readFile.bind(null, 'world.txt');
  yield fs.readFile.bind(null, 'and-such.txt');
}

for (task of doStuff()) {
     
  // task是一个函数,可以像回调函数那样使用它
}

Generator函数的异步应用

Node 约定,回调函数的第一个参数,必须是错误对象err(如果没有错误,该参数就是null

因为执行分成两段,第一段执行完以后,任务所在的上下文环境就已经结束了。在这以后抛出的错误,原来的上下文环境已经无法捕捉,只能当作参数,传入第二段。

  • Promise 的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。

协程(coroutine)

协程有点像函数,又有点像线程。它的运行流程大致如下。

  • 第一步,协程A开始执行。
  • 第二步,协程A执行到一半,进入暂停,执行权转移到协程B
  • 第三步,(一段时间后)协程B交还执行权。
  • 第四步,协程A恢复执行。

协程的Generator函数实现

Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。

Generator 函数的数据交换和错误处理

Generator 函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:

  1. 函数体内外的数据交换
  2. 错误处理机制。
  • next返回值的 value 属性,是 Generator 函数向外输出数据;next方法还可以接受参数,向 Generator 函数体内输入数据。
  • Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。

异步任务的封装

真实案例

var fetch = require('node-fetch');

function* gen(){
     
  var url = 'https://api.github.com/users/github';
  var result = yield fetch(url);
  console.log(result.bio);
}

执行这段代码的方法如下:

var g = gen();
var result = g.next();

result.value.then(function(data){
     
  return data.json();
}).then(function(data){
     
  g.next(data);
});

可以看到,虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。

Thunk 函数

Thunk 函数是自动执行 Generator 函数的一种方法。是传名调用。

“传值调用”(call by value):即在进入函数体之前,就计算x + 5的值(等于 6),再将这个值传入函数f。C 语言就采用这种策略。

“传名调用”(call by name):即直接将表达式x + 5传入函数体,只在用到它的时候求值。Haskell 语言采用这种策略。

编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。

function f(m) {
     
  return m * 2;
}

f(x + 5);

// 等同于

var thunk = function () {
     
  return x + 5;
};

function f(thunk) {
     
  return thunk() * 2;
}

在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。

// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(单参数版本)
var Thunk = function (fileName) {
     
  return function (callback) {
     
    return fs.readFile(fileName, callback);
  };
};

var readFileThunk = Thunk(fileName);
readFileThunk(callback);

任何函数,只要参数有回调函数,就能写成 Thunk 函数的形式。

//Thunk 函数转换器

// ES5版本
var Thunk = function(fn){
     
  return function (){
     
    var args = Array.prototype.slice.call(arguments);
    return function (callback){
     
      args.push(callback);
      return fn.apply(this, args);
    }
  };
};

// ES6版本
const Thunk = function(fn) {
     
  return function (...args) {
     
    return function (callback) {
     
      return fn.call(this, ...args, callback);
    }
  };
};

使用上面的转换器,生成fs.readFile的 Thunk 函数。

var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);

例子:

function f(a, cb) {
     
  cb(a);
}
const ft = Thunk(f);

ft(1)(console.log) // 1

Thunkify 模块

var thunkify = require('thunkify');
var fs = require('fs');

var read = thunkify(fs.readFile);
read('package.json')(function(err, str){
     
  // ...
});

源码:

function thunkify(fn) {
     
  return function() {
     
    var args = new Array(arguments.length);
    var ctx = this;

    for (var i = 0; i < args.length; ++i) {
     
      args[i] = arguments[i];
    }

    return function (done) {
     
      var called;

      args.push(function () {
     
        if (called) return;
        called = true;
        done.apply(null, arguments);
      });

      try {
     
        fn.apply(ctx, args);
      } catch (err) {
     
        done(err);
      }
    }
  }
};

Thunk函数的自动流程管理

Thunk 函数真正的威力,在于可以自动执行 Generator 函数。下面就是一个基于 Thunk 函数的 Generator 执行器。

function run(fn) {
     
  var gen = fn();

  function next(err, data) {
     
    var result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }

  next();
}

function* g() {
     
  // ...
}

run(g);

async函数

Generator函数的语法糖

async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

async函数对 Generator 函数的改进,体现在以下四点

  1. Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器
  2. asyncawait,比起星号和yield,语义更清楚了。
  3. co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值
  4. async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。可以用then方法指定下一步的操作。

async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

async 函数有多种使用形式。

// 函数声明
async function foo() {
     }

// 函数表达式
const foo = async function () {
     };

// 对象的方法
let obj = {
      async foo() {
     } };
obj.foo().then(...)

// Class 的方法
class Storage {
     
  constructor() {
     
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
     
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${
       name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jake').then();

// 箭头函数
const foo = async () => {
     };

返回Promise对象

async函数内部return语句返回的值,会成为then方法回调函数的参数。

async function f() {
     
  return 'hello world';
}

f().then(v => console.log(v))
// "hello world"

async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

async function f() {
     
  throw new Error('出错了');
}

f().then(
  v => console.log(v),
  e => console.log(e)
)
// Error: 出错了

Promise 对象的状态变化

只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

async function getTitle(url) {
     
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/([\s\S]+)<\/title>/i</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token function">getTitle</span><span class="token punctuation">(</span><span class="token string">'https://tc39.github.io/ecma262/'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>console<span class="token punctuation">.</span>log<span class="token punctuation">)</span>
<span class="token comment">// "ECMAScript 2017 Language Specification"</span>
</code></pre> 
  <h4>await 命令</h4> 
  <blockquote> 
   <p>如果await后面不是Promise对象就直接返回对应的值</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token comment">// 等同于</span>
  <span class="token comment">// return 123;</span>
  <span class="token keyword">return</span> <span class="token keyword">await</span> <span class="token number">123</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>v <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment">// 123</span>
</code></pre> 
  <blockquote> 
   <p><code>await</code>命令后面是一个<code>thenable</code>对象(即定义<code>then</code>方法的对象),那么<code>await</code>会将其等同于 Promise 对象</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">Sleep</span> <span class="token punctuation">{
     </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>timeout<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>timeout <span class="token operator">=</span> timeout<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">then</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">const</span> startTime <span class="token operator">=</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">setTimeout</span><span class="token punctuation">(</span>
      <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">resolve</span><span class="token punctuation">(</span>Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> startTime<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>timeout
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{
     </span>
  <span class="token keyword">const</span> actualTime <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token keyword">new</span> <span class="token class-name">Sleep</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>actualTime<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//这个实例不是 Promise 对象,但是因为定义了then方法,await会将其视为Promise处理。</span>
</code></pre> 
  <p>任何一个<code>await</code>语句后面的 Promise 对象变为<code>reject</code>状态,那么整个<code>async</code>函数都会中断执行。</p> 
  <p>可以将第一个<code>await</code>放在<code>try...catch</code>结构里面,这样不管这个异步操作是否成功,第二个<code>await</code>都会执行。</p> 
  <p>另一种方法是<code>await</code>后面的 Promise 对象再跟一个<code>catch</code>方法,处理前面可能出现的错误。</p> 
  <h4>错误处理</h4> 
  <ol> 
   <li>await后面的Promise对象可能是reject,所以最好放在try…catch命令中</li> 
  </ol> 
  <pre><code class="prism language-javascript"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">myFunction</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token keyword">try</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">await</span> <span class="token function">somethingThatReturnsAPromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">err</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 另一种写法</span>

<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">myFunction</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token keyword">await</span> <span class="token function">somethingThatReturnsAPromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> 
  <ol start="2"> 
   <li>多个<code>await</code>命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。</li> 
  </ol> 
  <pre><code class="prism language-javascript"><span class="token keyword">let</span> foo <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getFoo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> bar <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getBar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//改成</span>
<span class="token keyword">let</span> <span class="token punctuation">[</span>foo<span class="token punctuation">,</span> bar<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">await</span> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token function">getFoo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">getBar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre> 
  <p>这样同时触发可以缩短时间</p> 
  <ol start="3"> 
   <li> <p>await必须在async函数中</p> </li> 
   <li> <p>async 函数可以保留运行堆栈。</p> <pre><code class="prism language-javascript"><span class="token keyword">const</span> <span class="token function-variable function">a</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{
       </span>
  <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">c</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <p>等到<code>b()</code>运行结束,可能<code>a()</code>早就运行结束了,<code>b()</code>所在的上下文环境已经消失了。如果<code>b()</code>或<code>c()</code>报错,错误堆栈将不包括<code>a()</code>。</p> <p>现在将这个例子改成<code>async</code>函数。</p> <pre><code class="prism language-javascript"><span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{
       </span>
  <span class="token keyword">await</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">c</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">//b()运行的时候,a()是暂停执行,上下文环境都保存着。一旦b()或c(),错误堆栈将包括a()。</span>
</code></pre> </li> 
  </ol> 
  <h4>async函数的实现原理</h4> 
  <blockquote> 
   <p>async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">fn</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token comment">// ...</span>
<span class="token punctuation">}</span>

<span class="token comment">// 等同于</span>

<span class="token keyword">function</span> <span class="token function">fn</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token keyword">return</span> <span class="token function">spawn</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token comment">// ...</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> 
  <h2>Class</h2> 
  <ul> 
   <li> <p>类的所有方法都定义在类的<code>prototype</code>属性上面。</p> </li> 
   <li> <p>由于类的方法都定义在<code>prototype</code>对象上面,所以类的新方法可以添加在<code>prototype</code>对象上面。<code>Object.assign</code>方法可以很方便地一次向类添加多个方法。</p> </li> 
   <li> <p><code>prototype</code>对象的<code>constructor</code>属性,直接指向“类”的本身</p> </li> 
   <li> <p>类的内部所有定义的方法,都是不可枚举的(non-enumerable),ES5可以</p> </li> 
   <li> <p>一个类必须有<code>constructor</code>方法,如果没有显式定义,一个空的<code>constructor</code>方法会被默认添加。</p> </li> 
   <li> <p><code>constructor</code>方法默认返回实例对象(即<code>this</code>),完全可以指定返回另外一个对象。</p> </li> 
   <li> <p>get和set函数设置在原型对象Descriptor对象上</p> </li> 
   <li> <p>类的属性名可以用表达式</p> </li> 
   <li> <p>注意点:</p> 
    <ol> 
     <li>类和模块的内部默认就是严格模式</li> 
    </ol> <p>​ 2. 不存在变量提升</p> <p>​3. 有name属性</p> 
    <ol start="4"> 
     <li> <p>方法前加* 就表示该方法是Generator函数</p> </li> 
     <li> <p>类的方法内部如果含有<code>this</code>,它默认指向类的实例。一旦单独使用该方法,很可能报错。</p> <pre><code class="prism language-javascript">解决方法:
<span class="token comment">// 方法1:在构造方法中绑定this,这样就不会找不到print方法了。</span>
<span class="token keyword">class</span> <span class="token class-name">Logger</span> <span class="token punctuation">{
         </span>
 <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
   <span class="token keyword">this</span><span class="token punctuation">.</span>printName <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>printName<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span>

 <span class="token comment">// ...</span>
<span class="token punctuation">}</span>

<span class="token comment">//方法2:使用箭头函数法</span>
<span class="token keyword">class</span> <span class="token class-name">Logger</span> <span class="token punctuation">{
         </span>
 <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
   <span class="token keyword">this</span><span class="token punctuation">.</span>printName <span class="token operator">=</span> <span class="token punctuation">(</span>name <span class="token operator">=</span> <span class="token string">'there'</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{
         </span>
     <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${
           </span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token punctuation">}</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span>

 <span class="token comment">// ...</span>
<span class="token punctuation">}</span>

<span class="token comment">//方法3:使用Proxy</span>
<span class="token keyword">function</span> <span class="token function">selfish</span> <span class="token punctuation">(</span>target<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
 <span class="token keyword">const</span> cache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WeakMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token keyword">const</span> handler <span class="token operator">=</span> <span class="token punctuation">{
         </span>
   <span class="token keyword">get</span> <span class="token punctuation">(</span>target<span class="token punctuation">,</span> key<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
     <span class="token keyword">const</span> value <span class="token operator">=</span> Reflect<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>
     <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> value <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
       <span class="token keyword">return</span> value<span class="token punctuation">;</span>
     <span class="token punctuation">}</span>
     <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>cache<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
       cache<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> value<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
     <span class="token punctuation">}</span>
     <span class="token keyword">return</span> cache<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token punctuation">}</span>
 <span class="token punctuation">}</span><span class="token punctuation">;</span>
 <span class="token keyword">const</span> proxy <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Proxy</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token keyword">return</span> proxy<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> logger <span class="token operator">=</span> <span class="token function">selfish</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Logger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> </li> 
    </ol> <h4>静态方法</h4> 
    <blockquote> 
     <p>加上<code>static</code>关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。</p> 
    </blockquote> 
    <ul> 
     <li>如果静态方法包含<code>this</code>关键字,这个<code>this</code>指的是类,而不是实例</li> 
     <li>父类的静态方法,可以被子类继承。</li> 
    </ul> <h4>静态属性</h4> 
    <blockquote> 
     <p>因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。</p> 
     <p>只能在外部定义。</p> 
    </blockquote> <h4>私有方法和属性</h4> 
    <blockquote> 
     <p>ES6 不提供,只能通过变通方法模拟实现</p> 
    </blockquote> <p>用symbol定义属性名比较好</p> <h4>new.target属性</h4> 
    <blockquote> 
     <p>如果构造函数不是通过<code>new</code>命令调用的,<code>new.target</code>会返回<code>undefined</code></p> 
     <p>这个属性可以用来确定构造函数是怎么调用的。</p> 
    </blockquote> 
    <ul> 
     <li> <p>子类继承父类时,<code>new.target</code>会返回子类。</p> </li> 
     <li> <p>利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。</p> <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">Shape</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">new</span><span class="token punctuation">.</span>target <span class="token operator">===</span> Shape<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'本类不能实例化'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Rectangle</span> <span class="token keyword">extends</span> <span class="token class-name">Shape</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>length<span class="token punctuation">,</span> width<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// ...</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Shape</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 报错</span>
<span class="token keyword">var</span> y <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Rectangle</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 正确</span>
</code></pre> </li> 
    </ul> </li> 
  </ul> 
  <h2>Class的继承</h2> 
  <blockquote> 
   <p>Class 可以通过<code>extends</code>关键字实现继承</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">ColorPoint</span> <span class="token keyword">extends</span> <span class="token class-name">Point</span> <span class="token punctuation">{
     </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> color<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 调用父类的constructor(x, y)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">=</span> color<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 调用父类的toString()</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> 
  <ul> 
   <li><code>super</code>关键字,它在这里表示父类的构造函数,用来新建父类的<code>this</code>对象</li> 
   <li>子类必须在<code>constructor</code>方法中调用<code>super</code>方法,否则新建实例时会报错。</li> 
  </ul> 
  <blockquote> 
   <p>ES5 的继承,实质是先创造子类的实例对象<code>this</code>,然后再将父类的方法添加到<code>this</code>上面(<code>Parent.apply(this)</code>)。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到<code>this</code>上面(所以必须先调用<code>super</code>方法),然后再用子类的构造函数修改<code>this</code>。</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token comment">//如果子类没有添加constructor方法</span>
<span class="token keyword">class</span> <span class="token class-name">ColorPoint</span> <span class="token keyword">extends</span> <span class="token class-name">Point</span> <span class="token punctuation">{
     </span>
<span class="token punctuation">}</span>

<span class="token comment">// 等同于</span>
<span class="token keyword">class</span> <span class="token class-name">ColorPoint</span> <span class="token keyword">extends</span> <span class="token class-name">Point</span> <span class="token punctuation">{
     </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> 
  <ul> 
   <li><code>Object.getPrototypeOf</code>方法可以用来从子类上获取父类。</li> 
  </ul> 
  <h4>super关键字</h4> 
  <p>有两种使用情况</p> 
  <ol> 
   <li> <p>作为函数</p> <p>子类的构造函数必须执行一次<code>super</code>函数。</p> <p><code>super()</code>,代表调用父类的构造函数。</p> </li> 
  </ol> 
  <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
     </span><span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
     </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">//super()在这里相当于A.prototype.constructor.call(this)。</span>
</code></pre> 
  <ol start="2"> 
   <li> <p>作为对象</p> <p><code>super</code>作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。</p> <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
       </span>
  <span class="token function">p</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
       </span>
    <span class="token keyword">return</span> <span class="token number">2</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
       </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
       </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">p</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 2</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> 
    <ul> 
     <li> <p>子类普通方法中通过<code>super</code>调用父类的方法时,方法内部的<code>this</code>指向当前的子类实例。</p> <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
b<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 2</span>

<span class="token comment">//实际上执行的是super.print.call(this)</span>
</code></pre> </li> 
     <li> <p>由于<code>this</code>指向子类实例,所以如果通过<code>super</code>对某个属性赋值,这时<code>super</code>就是<code>this</code>,赋值的属性会变成子类实例的属性。</p> <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
    <span class="token keyword">super</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">super</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// undefined</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 3</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> </li> 
     <li> <p>如果<code>super</code>作为对象,用在静态方法之中,这时<code>super</code>将指向父类,而不是父类的原型对象。</p> <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">Parent</span> <span class="token punctuation">{
         </span>
  <span class="token keyword">static</span> <span class="token function">myMethod</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'static'</span><span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">myMethod</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'instance'</span><span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Child</span> <span class="token keyword">extends</span> <span class="token class-name">Parent</span> <span class="token punctuation">{
         </span>
  <span class="token keyword">static</span> <span class="token function">myMethod</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">myMethod</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">myMethod</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">myMethod</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Child<span class="token punctuation">.</span><span class="token function">myMethod</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// static 1</span>

<span class="token keyword">var</span> child <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Child</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
child<span class="token punctuation">.</span><span class="token function">myMethod</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// instance 2</span>
</code></pre> </li> 
     <li> <p>在子类的静态方法中通过<code>super</code>调用父类的方法时,方法内部的<code>this</code>指向当前的子类,而不是子类的实例。</p> <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">static</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">static</span> <span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token constant">B</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
<span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 3</span>
</code></pre> </li> 
     <li> <p>使用<code>super</code>的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。</p> <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span><span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">super</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 报错</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">//像这样用super.valueOf()救恩表明super是一个对象,就不会报错</span>
<span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span><span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
         </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
         </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">instanceof</span> <span class="token class-name">B</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// true</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> </li> 
    </ul> </li> 
  </ol> 
  <h4>类的prototype和__proto__属性</h4> 
  <blockquote> 
   <p>类同时有<code>prototype</code>属性和<code>__proto__</code>属性,因此同时存在两条继承链。</p> 
  </blockquote> 
  <p>(1)子类的<code>__proto__</code>属性,表示构造函数的继承,总是指向父类。</p> 
  <p>(2)子类<code>prototype</code>属性的<code>__proto__</code>属性,表示方法的继承,总是指向父类的<code>prototype</code>属性。</p> 
  <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
     </span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token keyword">extends</span> <span class="token class-name">A</span> <span class="token punctuation">{
     </span>
<span class="token punctuation">}</span>

<span class="token constant">B</span><span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> <span class="token constant">A</span> <span class="token comment">// true</span>
<span class="token constant">B</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> <span class="token constant">A</span><span class="token punctuation">.</span>prototype <span class="token comment">// true</span>
</code></pre> 
  <p>原因是类的继承是用Object.setPrototypeOf实现的</p> 
  <pre><code class="prism language-javascript">Object<span class="token punctuation">.</span><span class="token function-variable function">setPrototypeOf</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>obj<span class="token punctuation">,</span> proto<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  obj<span class="token punctuation">.</span>__proto__ <span class="token operator">=</span> proto<span class="token punctuation">;</span>
  <span class="token keyword">return</span> obj<span class="token punctuation">;</span>
<span class="token punctuation">}</span>


<span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{
     </span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token punctuation">{
     </span>
<span class="token punctuation">}</span>

<span class="token comment">// B 的实例继承 A 的实例</span>
Object<span class="token punctuation">.</span><span class="token function">setPrototypeOf</span><span class="token punctuation">(</span><span class="token constant">B</span><span class="token punctuation">.</span>prototype<span class="token punctuation">,</span> <span class="token constant">A</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// B 继承 A 的静态属性</span>
Object<span class="token punctuation">.</span><span class="token function">setPrototypeOf</span><span class="token punctuation">(</span><span class="token constant">B</span><span class="token punctuation">,</span> <span class="token constant">A</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> 
  <h4>实例的 <strong>proto</strong> 属性</h4> 
  <blockquote> 
   <p>子类实例的<code>__proto__</code>属性的<code>__proto__</code>属性,指向父类实例的<code>__proto__</code>属性。</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">var</span> p1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Point</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> p2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ColorPoint</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token string">'red'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

p2<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> p1<span class="token punctuation">.</span>__proto__ <span class="token comment">// false</span>
p2<span class="token punctuation">.</span>__proto__<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> p1<span class="token punctuation">.</span>__proto__ <span class="token comment">// true</span>
</code></pre> 
  <p>通过子类实例的<code>__proto__.__proto__</code>属性,可以修改父类实例的行为。</p> 
  <pre><code class="prism language-javascript">p2<span class="token punctuation">.</span>__proto__<span class="token punctuation">.</span>__proto__<span class="token punctuation">.</span><span class="token function-variable function">printName</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Ha'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

p1<span class="token punctuation">.</span><span class="token function">printName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// "Ha"</span>
</code></pre> 
  <h4>原生构造函数的继承</h4> 
  <p>原生构造函数是指语言内置的构造函数,通常用来生成数据结构。ECMAScript 的原生构造函数大致有下面这些。</p> 
  <ul> 
   <li>Boolean()</li> 
   <li>Number()</li> 
   <li>String()</li> 
   <li>Array()</li> 
   <li>Date()</li> 
   <li>Function()</li> 
   <li>RegExp()</li> 
   <li>Error()</li> 
   <li>Object()</li> 
  </ul> 
  <blockquote> 
   <p>ES5中,子类无法获得原生构造函数的内部属性,通过<code>Array.apply()</code>或者分配给原型对象都不行。原生构造函数会忽略<code>apply</code>方法传入的<code>this</code>,也就是说,原生构造函数的<code>this</code>无法绑定,导致拿不到内部属性。</p> 
  </blockquote> 
  <blockquote> 
   <p>ES6 允许继承原生构造函数定义子类</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">MyArray</span> <span class="token keyword">extends</span> <span class="token class-name">Array</span> <span class="token punctuation">{
     </span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> arr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
arr<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">12</span><span class="token punctuation">;</span>
arr<span class="token punctuation">.</span>length <span class="token comment">// 1</span>

arr<span class="token punctuation">.</span>length <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
arr<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token comment">// undefined</span>
</code></pre> 
  <h4>Mixin 模式的实现</h4> 
  <blockquote> 
   <p>Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口。</p> 
  </blockquote> 
  <p>简单实现:</p> 
  <pre><code class="prism language-javascript"><span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token punctuation">{
     </span>
  a<span class="token punctuation">:</span> <span class="token string">'a'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> b <span class="token operator">=</span> <span class="token punctuation">{
     </span>
  b<span class="token punctuation">:</span> <span class="token string">'b'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> c <span class="token operator">=</span> <span class="token punctuation">{
     </span><span class="token operator">...</span>a<span class="token punctuation">,</span> <span class="token operator">...</span>b<span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// {a: 'a', b: 'b'}</span>
</code></pre> 
  <p>更完整的实现:</p> 
  <pre><code class="prism language-javascript"><span class="token keyword">function</span> <span class="token function">mix</span><span class="token punctuation">(</span><span class="token operator">...</span>mixins<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token keyword">class</span> <span class="token class-name">Mix</span> <span class="token punctuation">{
     </span><span class="token punctuation">}</span>

  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> mixin <span class="token keyword">of</span> mixins<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token function">copyProperties</span><span class="token punctuation">(</span>Mix<span class="token punctuation">.</span>prototype<span class="token punctuation">,</span> mixin<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 拷贝实例属性</span>
    <span class="token function">copyProperties</span><span class="token punctuation">(</span>Mix<span class="token punctuation">.</span>prototype<span class="token punctuation">,</span> Reflect<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>mixin<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 拷贝原型属性</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">return</span> Mix<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">copyProperties</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> source<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> key <span class="token keyword">of</span> Reflect<span class="token punctuation">.</span><span class="token function">ownKeys</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span> key <span class="token operator">!==</span> <span class="token string">"constructor"</span>
      <span class="token operator">&&</span> key <span class="token operator">!==</span> <span class="token string">"prototype"</span>
      <span class="token operator">&&</span> key <span class="token operator">!==</span> <span class="token string">"name"</span>
    <span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
      <span class="token keyword">let</span> desc <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyDescriptor</span><span class="token punctuation">(</span>source<span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>
      Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> key<span class="token punctuation">,</span> desc<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> 
  <p>上面代码的<code>mix</code>函数,可以将多个对象合成为一个类。使用的时候,只要继承这个类即可。</p> 
  <pre><code class="prism language-javascript"><span class="token keyword">class</span> <span class="token class-name">DistributedEdit</span> <span class="token keyword">extends</span> <span class="token class-name">mix</span><span class="token punctuation">(</span>Loggable<span class="token punctuation">,</span> Serializable<span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  <span class="token comment">// ...</span>
<span class="token punctuation">}</span>
</code></pre> 
  <h2>Module</h2> 
  <blockquote> 
   <p>在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器</p> 
  </blockquote> 
  <blockquote> 
   <p>ES6 模块不是对象,而是通过<code>export</code>命令显式指定输出的代码,再通过<code>import</code>命令输入。</p> 
  </blockquote> 
  <p>ES6模块的好处</p> 
  <ul> 
   <li>不再需要<code>UMD</code>模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。</li> 
   <li>将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者<code>navigator</code>对象的属性。</li> 
   <li>不再需要对象作为命名空间(比如<code>Math</code>对象),未来这些功能可以通过模块提供。</li> 
  </ul> 
  <h4>严格模式</h4> 
  <blockquote> 
   <p>ES6 的模块自动采用严格模式,不管你有没有在模块头部加上<code>"use strict";</code></p> 
  </blockquote> 
  <p>严格模式主要有以下限制。</p> 
  <ul> 
   <li>变量必须声明后再使用</li> 
   <li>函数的参数不能有同名属性,否则报错</li> 
   <li>不能使用<code>with</code>语句</li> 
   <li>不能对只读属性赋值,否则报错</li> 
   <li>不能使用前缀 0 表示八进制数,否则报错</li> 
   <li>不能删除不可删除的属性,否则报错</li> 
   <li>不能删除变量<code>delete prop</code>,会报错,只能删除属性<code>delete global[prop]</code></li> 
   <li><code>eval</code>不会在它的外层作用域引入变量</li> 
   <li><code>eval</code>和<code>arguments</code>不能被重新赋值</li> 
   <li><code>arguments</code>不会自动反映函数参数的变化</li> 
   <li>不能使用<code>arguments.callee</code></li> 
   <li>不能使用<code>arguments.caller</code></li> 
   <li>禁止<code>this</code>指向全局对象</li> 
   <li>不能使用<code>fn.caller</code>和<code>fn.arguments</code>获取函数调用的堆栈</li> 
   <li>增加了保留字(比如<code>protected</code>、<code>static</code>和<code>interface</code>)</li> 
  </ul> 
  <h4>export命令</h4> 
  <blockquote> 
   <p><code>export</code>命令用于规定模块的对外接口</p> 
   <p>export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。</p> 
  </blockquote> 
  <p>export命令的写法:</p> 
  <pre><code class="prism language-javascript"><span class="token comment">//写法1:</span>
<span class="token keyword">export</span> <span class="token keyword">var</span> firstName <span class="token operator">=</span> <span class="token string">'Michael'</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">var</span> lastName <span class="token operator">=</span> <span class="token string">'Jackson'</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">var</span> year <span class="token operator">=</span> <span class="token number">1958</span><span class="token punctuation">;</span>

<span class="token comment">//写法2:</span>
<span class="token keyword">var</span> firstName <span class="token operator">=</span> <span class="token string">'Michael'</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> lastName <span class="token operator">=</span> <span class="token string">'Jackson'</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> year <span class="token operator">=</span> <span class="token number">1958</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token punctuation">{
     </span>firstName<span class="token punctuation">,</span> lastName<span class="token punctuation">,</span> year<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> 
  <p>可以用as关键字重命名</p> 
  <pre><code class="prism language-javascript"><span class="token keyword">function</span> <span class="token function">v1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span> <span class="token operator">...</span> <span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function">v2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span> <span class="token operator">...</span> <span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token punctuation">{
     </span>
  v1 <span class="token keyword">as</span> streamV1<span class="token punctuation">,</span>
  v2 <span class="token keyword">as</span> streamV2<span class="token punctuation">,</span>
  v2 <span class="token keyword">as</span> streamLatestVersion
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> 
  <p>export必须提供对外的接口,直接export数值或者变量会出错。</p> 
  <pre><code class="prism language-javascript"><span class="token comment">// 报错</span>
<span class="token keyword">export</span> <span class="token number">1</span><span class="token punctuation">;</span>

<span class="token comment">// 报错</span>
<span class="token keyword">var</span> m <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> m<span class="token punctuation">;</span>

<span class="token comment">// 写法一</span>
<span class="token keyword">export</span> <span class="token keyword">var</span> m <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

<span class="token comment">// 写法二</span>
<span class="token keyword">var</span> m <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token punctuation">{
     </span>m<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 写法三</span>
<span class="token keyword">var</span> n <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token punctuation">{
     </span>n <span class="token keyword">as</span> m<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> 
  <p>function和class也必须输出接口</p> 
  <pre><code class="prism language-javascript"><span class="token comment">// 报错</span>
<span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span><span class="token punctuation">}</span>
<span class="token keyword">export</span> f<span class="token punctuation">;</span>

<span class="token comment">// 正确</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 正确</span>
<span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span><span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token punctuation">{
     </span>f<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> 
  <p>export输出的值是动态绑定关系,可动态更新。</p> 
  <pre><code class="prism language-javascript"><span class="token keyword">export</span> <span class="token keyword">var</span> foo <span class="token operator">=</span> <span class="token string">'bar'</span><span class="token punctuation">;</span>
<span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> foo <span class="token operator">=</span> <span class="token string">'baz'</span><span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//0.5秒后输出的值改变</span>
</code></pre> 
  <p>export必须位于顶层对象</p> 
  <h4>import命令</h4> 
  <ul> 
   <li> <p><code>import</code>命令输入的变量都是只读的,因为它的本质是输入接口。</p> </li> 
   <li> <p>如果<code>a</code>是一个对象,改写<code>a</code>的属性是允许的!(不推荐)</p> </li> 
   <li> <p><code>import</code>后面的<code>from</code>指定模块文件的位置,可以是相对路径,也可以是绝对路径,<code>.js</code>后缀可以省略。如果只是模块名,不带有路径,那么必须有<strong>配置文件</strong>,告诉 JavaScript 引擎该模块的位置。</p> </li> 
   <li> <p><code>import</code>命令具有提升效果,会提升到整个模块的头部,首先执行。</p> </li> 
   <li> <p>由于<code>import</code>是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。</p> </li> 
   <li> <p><code>import</code>语句会执行所加载的模块</p> <pre><code class="prism language-javascript"><span class="token keyword">import</span> <span class="token string">'lodash'</span><span class="token punctuation">;</span>
<span class="token comment">//会执行,但是不输入任何值</span>

<span class="token keyword">import</span> <span class="token string">'lodash'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token string">'lodash'</span><span class="token punctuation">;</span>
<span class="token comment">//只执行一次</span>
</code></pre> </li> 
  </ul> 
  <h4>export default命令</h4> 
  <blockquote> 
   <p>使用<code>import</code>命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。</p> 
   <p>export default可以指定默认输出,如果指定函数名在外部也视为匿名函数</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{
     </span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">//import的时候就可以指定任意名字</span>

<span class="token keyword">import</span> customName <span class="token keyword">from</span> <span class="token string">'./export-default'</span><span class="token punctuation">;</span>
<span class="token function">customName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 'foo'</span>
</code></pre> 
  <blockquote> 
   <p>本质上,<code>export default</code>就是输出一个叫做<code>default</code>的变量或方法,然后系统允许你为它取任意名字。</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">export</span> <span class="token punctuation">{
     </span>add <span class="token keyword">as</span> <span class="token keyword">default</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// 等同于</span>
<span class="token comment">// export default add;</span>

<span class="token keyword">import</span> <span class="token punctuation">{
     </span> <span class="token keyword">default</span> <span class="token keyword">as</span> foo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'modules'</span><span class="token punctuation">;</span>
<span class="token comment">// 等同于</span>
<span class="token comment">// import foo from 'modules';</span>
</code></pre> 
  <ul> 
   <li>export default 只是输出一个叫做<code>default</code>的变量,所以它后面不能跟变量声明语句。</li> 
   <li>和export不一样 export default后面就可以直接跟值。</li> 
  </ul> 
  <h4>export和import的复合写法</h4> 
  <blockquote> 
   <p>import可以和export合并</p> 
  </blockquote> 
  <pre><code class="prism language-javascript"><span class="token keyword">export</span> <span class="token punctuation">{
     </span> foo<span class="token punctuation">,</span> bar <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'my_module'</span><span class="token punctuation">;</span>

<span class="token comment">// 可以简单理解为</span>
<span class="token keyword">import</span> <span class="token punctuation">{
     </span> foo<span class="token punctuation">,</span> bar <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'my_module'</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token punctuation">{
     </span> foo<span class="token punctuation">,</span> bar <span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> 
  <ul> 
   <li>写成一行以后,<code>foo</code>和<code>bar</code>实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用<code>foo</code>和<code>bar</code>。</li> 
  </ul> 
  <h4>模块的继承</h4> 
  <ul> 
   <li><code>export *</code>命令会忽略<code>circle</code>模块的<code>default</code>方法</li> 
  </ul> 
  <h4>跨模块常量</h4> 
  <blockquote> 
   <p><code>const</code>声明的常量只在当前代码块有效。</p> 
  </blockquote> 
  <p>以下方法可以跨模块使用</p> 
  <pre><code class="prism language-javascript"><span class="token comment">// constants.js 模块</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">C</span> <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">;</span>

<span class="token comment">// test1.js 模块</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> constants <span class="token keyword">from</span> <span class="token string">'./constants'</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>constants<span class="token punctuation">.</span><span class="token constant">A</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 1</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>constants<span class="token punctuation">.</span><span class="token constant">B</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 3</span>

<span class="token comment">// test2.js 模块</span>
<span class="token keyword">import</span> <span class="token punctuation">{
     </span><span class="token constant">A</span><span class="token punctuation">,</span> <span class="token constant">B</span><span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./constants'</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token constant">A</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 1</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token constant">B</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 3</span>
</code></pre> 
  <p>可以专门简一个目录,放常量</p> 
  <pre><code class="prism language-javascript"><span class="token comment">// constants/db.js</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> db <span class="token operator">=</span> <span class="token punctuation">{
     </span>
  url<span class="token punctuation">:</span> <span class="token string">'http://my.couchdbserver.local:5984'</span><span class="token punctuation">,</span>
  admin_username<span class="token punctuation">:</span> <span class="token string">'admin'</span><span class="token punctuation">,</span>
  admin_password<span class="token punctuation">:</span> <span class="token string">'admin password'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// constants/user.js</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> users <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'root'</span><span class="token punctuation">,</span> <span class="token string">'admin'</span><span class="token punctuation">,</span> <span class="token string">'staff'</span><span class="token punctuation">,</span> <span class="token string">'ceo'</span><span class="token punctuation">,</span> <span class="token string">'chief'</span><span class="token punctuation">,</span> <span class="token string">'moderator'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token comment">//在index中合并</span>
<span class="token comment">// constants/index.js</span>
<span class="token keyword">export</span> <span class="token punctuation">{
     </span>db<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./db'</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token punctuation">{
     </span>users<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./users'</span><span class="token punctuation">;</span>

<span class="token comment">//使用的时候直接加载index</span>
<span class="token comment">// script.js</span>
<span class="token keyword">import</span> <span class="token punctuation">{
     </span>db<span class="token punctuation">,</span> users<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./constants/index'</span><span class="token punctuation">;</span>
</code></pre> 
  <h2>Module 的加载实现</h2> 
  <blockquote> 
   <p>默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<code><script></code>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。</p> 
  </blockquote> 
  <blockquote> 
   <p>如果脚本体积很大,下载和执行的时间就会很长,因此造成浏览器堵塞,用户会感觉到浏览器“卡死”了,没有任何响应。这显然是很不好的体验,所以浏览器允许脚本异步加载,下面就是两种异步加载的语法。</p> 
  </blockquote> 
  <pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>path/to/myModule.js<span class="token punctuation">"</span></span> <span class="token attr-name">defer</span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>path/to/myModule.js<span class="token punctuation">"</span></span> <span class="token attr-name">async</span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
</code></pre> 
  <blockquote> 
   <p>渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。</p> 
  </blockquote> 
  <p><code>defer</code>与<code>async</code>的区别:</p> 
  <p>​ <code>defer</code>要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;</p> 
  <p>​ <code>async</code>一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。</p> 
  <p><code>defer</code>是“渲染完再执行”,<code>async</code>是“下载完就执行”。另外,如果有多个<code>defer</code>脚本,会按照它们在页面出现的顺序加载,而多个<code>async</code>脚本是不能保证加载顺序的。</p> 
  <h4>ES6模块加载规则</h4> 
  <p>浏览器加载 ES6 模块,也使用<code><script></code>标签,但是要加入<code>type="module"</code>属性。</p> 
  <pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>./foo.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
</code></pre> 
  <blockquote> 
   <p>浏览器对于带有<code>type="module"</code>的<code><script></code>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<code><script></code>标签的<code>defer</code>属性。</p> 
  </blockquote> 
  <pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>./foo.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token comment"><!-- 等同于 --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>./foo.js<span class="token punctuation">"</span></span> <span class="token attr-name">defer</span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
</code></pre> 
  <p>如果网页有多个<code><script type="module"></code>,它们会按照在页面出现的顺序依次执行。</p> 
  <p>如果使用了sasync属性,就是只要加载完成就执行</p> 
  <pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>./foo.js<span class="token punctuation">"</span></span> <span class="token attr-name">async</span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
</code></pre> 
  <ul> 
   <li>代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。</li> 
   <li>模块脚本自动采用严格模式,不管有没有声明<code>use strict</code>。</li> 
   <li>模块之中,可以使用<code>import</code>命令加载其他模块(<code>.js</code>后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用<code>export</code>命令输出对外接口。</li> 
   <li>模块之中,顶层的<code>this</code>关键字返回<code>undefined</code>,而不是指向<code>window</code>。也就是说,在模块顶层使用<code>this</code>关键字,是无意义的。</li> 
   <li>同一个模块如果加载多次,将只执行一次。</li> 
  </ul> 
  <h4>ES6与CommonJS的区别</h4> 
  <p>它们有两个重大差异。</p> 
  <ul> 
   <li>CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。</li> 
   <li>CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。</li> 
  </ul> 
  <h4>node加载</h4> 
  <blockquote> 
   <p>Node 要求 ES6 模块采用<code>.mjs</code>后缀文件名。也就是说,只要脚本文件里面使用<code>import</code>或者<code>export</code>命令,那么就必须采用**<code>.mjs</code>**后缀名!!!!!</p> 
  </blockquote> 
  <ul> 
   <li>目前,Node 的<code>import</code>命令只支持加载本地模块(<code>file:</code>协议),不支持加载远程模块。</li> 
   <li>如果模块名不含路径,那么<code>import</code>命令会去<code>node_modules</code>目录寻找这个模块。</li> 
   <li>如果脚本文件省略了后缀名,比如<code>import './foo'</code>,Node 会依次尝试四个后缀名:<code>./foo.mjs</code>、<code>./foo.js</code>、<code>./foo.json</code>、<code>./foo.node</code> 如果这些脚本文件都不存在,Node 就会去加载<code>./foo/package.json</code>的<code>main</code>字段指定的脚本。如果<code>./foo/package.json</code>不存在或者没有<code>main</code>字段,那么就会依次加载<code>./foo/index.mjs</code>、<code>./foo/index.js</code>、<code>./foo/index.json</code>、<code>./foo/index.node</code>。如果以上四个文件还是都不存在,就会抛出错误。</li> 
   <li>Node 的<code>import</code>命令是异步加载,这一点与浏览器的处理方法相同。</li> 
  </ul> 
 </div> 
</div>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1363690328707588096"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(笔记,ES6,阮一峰,笔记)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1950232782412247040.htm"
                           title="日更006 终极训练营day3" target="_blank">日更006 终极训练营day3</a>
                        <span class="text-muted">懒cici</span>

                        <div>人生创业课(2)今天的主题:学习方法一:遇到有用的书,反复读,然后结合自身实际,列践行清单,不要再写读书笔记思考这本书与我有什么关系,我在哪些地方能用到,之后我该怎么用方法二:读完书没映像怎么办?训练你的大脑,方法:每读完一遍书,立马合上书,做一场分享,几分钟都行对自己的学习要求太低,要逼自己方法三:学习深度不够怎么办?找到细分领域的榜样,把他们的文章、书籍、产品都体验一遍,成为他们的超级用户,向</div>
                    </li>
                    <li><a href="/article/1950220179610857472.htm"
                           title="【花了N长时间读《过犹不及》,不断练习,可以越通透】" target="_blank">【花了N长时间读《过犹不及》,不断练习,可以越通透】</a>
                        <span class="text-muted">君君Love</span>

                        <div>我已经记不清花了多长时间去读《过犹不及》,读书笔记都写了42页,这算是读得特别精细的了。是一本难得的好书,虽然书中很多内容和圣经吻合,我不是基督徒,却觉得这样的文字值得细细品味,和我们的生活息息相关。我是个界线建立不牢固的人,常常愧疚,常常害怕他人的愤怒,常常不懂拒绝,还有很多时候表达不了自己真实的感受,心里在说不嘴里却在说好……这本书给我很多的启示,让我学会了怎样去建立属于自己的清晰的界限。建立</div>
                    </li>
                    <li><a href="/article/1950218819616174080.htm"
                           title="基于redis的Zset实现作者的轻量级排名" target="_blank">基于redis的Zset实现作者的轻量级排名</a>
                        <span class="text-muted">周童學</span>
<a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/%E7%BC%93%E5%AD%98/1.htm">缓存</a>
                        <div>基于redis的Zset实现轻量级作者排名系统在今天的技术架构中,Redis是一种广泛使用的内存数据存储系统,尤其在需要高效检索和排序的场景中表现优异。在本篇博客中,我们将深入探讨如何使用Redis的有序集合(ZSet)构建一个高效的笔记排行榜系统,并提供相关代码示例和详细的解析。1.功能背景与需求假设我们有一个笔记分享平台,用户可以发布各种笔记,系统需要根据用户发布的笔记数量来生成一个实时更新的</div>
                    </li>
                    <li><a href="/article/1950216170401492992.htm"
                           title="常规笔记本和加固笔记本的区别" target="_blank">常规笔记本和加固笔记本的区别</a>
                        <span class="text-muted">luchengtech</span>
<a class="tag" taget="_blank" href="/search/%E7%94%B5%E8%84%91/1.htm">电脑</a><a class="tag" taget="_blank" href="/search/%E4%B8%89%E9%98%B2%E7%AC%94%E8%AE%B0%E6%9C%AC/1.htm">三防笔记本</a><a class="tag" taget="_blank" href="/search/%E5%8A%A0%E5%9B%BA%E8%AE%A1%E7%AE%97%E6%9C%BA/1.htm">加固计算机</a><a class="tag" taget="_blank" href="/search/%E5%8A%A0%E5%9B%BA%E7%AC%94%E8%AE%B0%E6%9C%AC/1.htm">加固笔记本</a>
                        <div>在现代科技产品中,笔记本电脑因其便携性和功能性被广泛应用。根据使用场景和需求的不同,笔记本可分为常规笔记本和加固笔记本,二者在多个方面存在显著区别。适用场景是区分二者的重要标志。常规笔记本主要面向普通消费者和办公人群,适用于家庭娱乐、日常办公、学生学习等相对稳定的室内环境。比如,人们在家用它追剧、处理文档,学生在教室用它完成作业。而加固笔记本则专为特殊行业设计,像军事、野外勘探、工业制造、交通运输</div>
                    </li>
                    <li><a href="/article/1950210374787723264.htm"
                           title="第八课: 写作出版你最关心的出书流程和市场分析(无戒学堂复盘)" target="_blank">第八课: 写作出版你最关心的出书流程和市场分析(无戒学堂复盘)</a>
                        <span class="text-muted">人在陌上</span>

                        <div>今天是周六,恰是圣诞节。推掉了两个需要凑腿的牌局,在一个手机,一个笔记本,一台电脑,一杯热茶的陪伴下,一个人静静地回听无戒学堂的最后一堂课。感谢这一个月,让自己的习惯开始改变,至少,可以静坐一个下午而不觉得乏味枯燥难受了,要为自己点个赞。我深知,这最后一堂课的内容,以我的资质和毅力,可能永远都用不上。但很明显,无戒学堂是用了心的,毕竟,有很多优秀学员,已经具备了写作能力,马上就要用到这堂课的内容。</div>
                    </li>
                    <li><a href="/article/1950208107430866944.htm"
                           title="python笔记14介绍几个魔法方法" target="_blank">python笔记14介绍几个魔法方法</a>
                        <span class="text-muted">抢公主的大魔王</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>python笔记14介绍几个魔法方法先声明一下各位大佬,这是我的笔记。如有错误,恳请指正。另外,感谢您的观看,谢谢啦!(1).__doc__输出对应的函数,类的说明文档print(print.__doc__)print(value,...,sep='',end='\n',file=sys.stdout,flush=False)Printsthevaluestoastream,ortosys.std</div>
                    </li>
                    <li><a href="/article/1950205034075582464.htm"
                           title="《感官品牌》读书笔记 1" target="_blank">《感官品牌》读书笔记 1</a>
                        <span class="text-muted">西红柿阿达</span>

                        <div>原文:最近我在东京街头闲逛时,与一位女士擦肩而过,我发现她的香水味似曾相识。“哗”的一下,记亿和情感立刻像潮水般涌了出来。这个香水味把我带回了15年前上高中的时候,我的一位亲密好友也是用这款香水。一瞬间,我呆站在那里,东京的街景逐渐淡出,取而代之的是我年少时的丹麦以及喜悦、悲伤、恐惧、困惑的记忆。我被这熟悉的香水味征服了。感想:感官是有记忆的,你所听到,看到,闻到过的有代表性的事件都会在大脑中深深</div>
                    </li>
                    <li><a href="/article/1950203883577995264.htm"
                           title="我不想再当知识的搬运工" target="_blank">我不想再当知识的搬运工</a>
                        <span class="text-muted">楚煜楚尧</span>

                        <div>因为学校课题研究的需要,这个暑假我依然需要完成一本书的阅读笔记。我选的是管建刚老师的《习课堂十讲》。这本书,之前我读过,所以重读的时候,感到很亲切,摘抄起来更是非常得心应手。20页,40面,抄了十天,终于在今天大功告成了。这对之前什么事都要一拖再拖的我来说,是破天荒的改变。我发现至从认识小尘老师以后,我的确发生了很大的改变。遇到必须做却总是犹豫不去做的事,我学会了按照小尘老师说的那样,在心里默默数</div>
                    </li>
                    <li><a href="/article/1950199576451411968.htm"
                           title="20210517坚持分享53天读书摘抄笔记 非暴力沟通——爱自己" target="_blank">20210517坚持分享53天读书摘抄笔记 非暴力沟通——爱自己</a>
                        <span class="text-muted">f79a6556cb19</span>

                        <div>让生命之花绽放在赫布·加德纳(HerbGardner)编写的《一千个小丑》一剧中,主人公拒绝将他12岁的外甥交给儿童福利院。他郑重地说道:“我希望他准确无误地知道他是多么特殊的生命,要不,他在成长的过程中将会忽视这一点。我希望他保持清醒,并看到各种奇妙的可能。我希望他知道,一旦有机会,排除万难给世界一点触动是值得的。我还希望他知道为什么他是一个人,而不是一张椅子。”然而,一旦负面的自我评价使我们看</div>
                    </li>
                    <li><a href="/article/1950196906563006464.htm"
                           title="Unity学习笔记1" target="_blank">Unity学习笔记1</a>
                        <span class="text-muted">zy_777</span>

                        <div>通过一个星期的简单学习,初步了解了下unity,unity的使用,以及场景的布局,UI,以及用C#做一些简单的逻辑。好记性不如烂笔头,一些关键帧还是记起来比较好,哈哈,不然可能转瞬即逝了,(PS:纯小白观点,unity大神可以直接忽略了)一:MonoBehaviour类的初始化1,Instantiate()创建GameObject2,通过Awake()和Start()来做初始化3,Update、L</div>
                    </li>
                    <li><a href="/article/1950190146074767360.htm"
                           title="大数据技术笔记—spring入门" target="_blank">大数据技术笔记—spring入门</a>
                        <span class="text-muted">卿卿老祖</span>

                        <div>篇一spring介绍spring.io官网快速开始Aop面向切面编程,可以任何位置,并且可以细致到方法上连接框架与框架Spring就是IOCAOP思想有效的组织中间层对象一般都是切入service层spring组成前后端分离已学方式,前后台未分离:Spring的远程通信:明日更新创建第一个spring项目来源:科多大数据</div>
                    </li>
                    <li><a href="/article/1950187554129113088.htm"
                           title="Django学习笔记(一)" target="_blank">Django学习笔记(一)</a>
                        <span class="text-muted"></span>

                        <div>学习视频为:pythondjangoweb框架开发入门全套视频教程一、安装pipinstalldjango==****检查是否安装成功django.get_version()二、django新建项目操作1、新建一个项目django-adminstartprojectproject_name2、新建APPcdproject_namedjango-adminstartappApp注:一个project</div>
                    </li>
                    <li><a href="/article/1950179614320029696.htm"
                           title="python学习笔记(汇总)" target="_blank">python学习笔记(汇总)</a>
                        <span class="text-muted">朕的剑还未配妥</span>
<a class="tag" taget="_blank" href="/search/python%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/1.htm">python学习笔记整理</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>文章目录一.基础知识二.python中的数据类型三.运算符四.程序的控制结构五.列表六.字典七.元组八.集合九.字符串十.函数十一.解决bug一.基础知识print函数字符串要加引号,数字可不加引号,如print(123.4)print('小谢')print("洛天依")还可输入表达式,如print(1+3)如果使用三引号,print打印的内容可不在同一行print("line1line2line</div>
                    </li>
                    <li><a href="/article/1950175578921431040.htm"
                           title="Redis 分布式锁深度解析:过期时间与自动续期机制" target="_blank">Redis 分布式锁深度解析:过期时间与自动续期机制</a>
                        <span class="text-muted">爱恨交织围巾</span>
<a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/1.htm">分布式事务</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/%E5%BE%AE%E6%9C%8D%E5%8A%A1/1.htm">微服务</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/go/1.htm">go</a>
                        <div>Redis分布式锁深度解析:过期时间与自动续期机制在分布式系统中,Redis分布式锁的可靠性很大程度上依赖于对锁生命周期的管理。上一篇文章我们探讨了分布式锁的基本原理,今天我们将聚焦于一个关键话题:如何通过合理设置过期时间和实现自动续期机制,来解决分布式锁中的死锁与锁提前释放问题。一、为什么过期时间是分布式锁的生命线?你的笔记中提到"服务挂掉时未删除锁可能导致死锁",这正是过期时间要解决的核心问题</div>
                    </li>
                    <li><a href="/article/1950175508729753600.htm"
                           title="08.学习闭环三部曲:预习、实时学习、复习" target="_blank">08.学习闭环三部曲:预习、实时学习、复习</a>
                        <span class="text-muted">0058b195f4dc</span>

                        <div>人生就是一本效率手册,你怎样对待时间,时间就会给你同比例的回馈。单点突破法。预习,实时学习,复习。1、预习:凡事提前【计划】(1)前一晚设置三个当日目标。每周起始于每周日。(2)提前学习。预习法进行思考。预不预习效果相差20%,预习法学会提问。(3)《学会提问》。听电子书。2.实时学习(1)(10%)相应场景,思维导图,快速笔记。灵感笔记。(2)大纲,基本记录,总结篇。3.复习法则,(70%),最</div>
                    </li>
                    <li><a href="/article/1950165603524341760.htm"
                           title="《如何写作》文心读书笔记" target="_blank">《如何写作》文心读书笔记</a>
                        <span class="text-muted">逆熵反弹力</span>

                        <div>《文心》这本书的文体是以讲故事的形式来讲解如何写作的,读起来不会觉得刻板。读完全书惊叹大师的文笔如此之好,同时感叹与此书相见恨晚。工作了几年发现表达能力在生活中越来越重要,不管是口语还是文字上的表达。有时候甚至都不能把自己想说的东西表达清楚,平时也有找过一些书来看,想通过提升自己的阅读量来提高表达能力。但是看了这么久的书发现见效甚微,这使得我不得不去反思,该怎么提高表达能力。因此打算从写作入手。刚</div>
                    </li>
                    <li><a href="/article/1950161706533580800.htm"
                           title="SQL笔记纯干货" target="_blank">SQL笔记纯干货</a>
                        <span class="text-muted">AI入门修炼</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a>
                        <div>软件:DataGrip2023.2.3,phpstudy_pro,MySQL8.0.12目录1.DDL语句(数据定义语句)1.1数据库操作语言1.2数据表操作语言2.DML语句(数据操作语言)2.1增删改2.2题2.3备份表3.DQL语句(数据查询语言)3.1查询操作3.2题一3.3题二4.多表详解4.1一对多4.2多对多5.多表查询6.窗口函数7.拓展:upsert8.sql注入攻击演示9.拆表</div>
                    </li>
                    <li><a href="/article/1950157326245752832.htm"
                           title="《4D卓越团队》习书笔记 第十六章 创造力与投入" target="_blank">《4D卓越团队》习书笔记 第十六章 创造力与投入</a>
                        <span class="text-muted">Smiledmx</span>

                        <div>《4D卓越团队-美国宇航局的管理法则》(查理·佩勒林)习书笔记第十六章创造力与投入本章要点:务实的乐观不是盲目乐观,而是带来希望的乐观。用真相激起希望吉姆·科林斯在《从优秀到卓越》中写道:“面对残酷的现实,平庸的公司选择解释和逃避,而不是正视。”创造你想要的项目1.你必须从基于真相的事实出发。正视真相很难,逃避是人类的本性。2.面对现实,你想创造什么?-我想利用现有资源创造一支精干、高效、积极的橙</div>
                    </li>
                    <li><a href="/article/1950151583857700864.htm"
                           title="2020-12-10" target="_blank">2020-12-10</a>
                        <span class="text-muted">生活有鱼_727f</span>

                        <div>今日汇总:1.学习了一只舞蹈2.专业知识抄了一遍3.讲师训作业完成今日不足之处:1.时间没管理好,浪费了很多时间到现在才做完明日必做:1.讲师训作业完成2.群消息做好笔记3.宽带安装</div>
                    </li>
                    <li><a href="/article/1950147339964444672.htm"
                           title="【Druid】学习笔记" target="_blank">【Druid】学习笔记</a>
                        <span class="text-muted">fixAllenSun</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a>
                        <div>【Druid】学习笔记【一】简介【1】简介【2】数据库连接池(1)能解决的问题(2)使用数据库连接池的好处【3】监控(1)监控信息采集的StatFilter(2)监控不影响性能(3)SQL参数化合并监控(4)执行次数、返回行数、更新行数和并发监控(5)慢查监控(6)Exception监控(7)区间分布(8)内置监控DEMO【4】Druid基本配置参数介绍【5】Druid相比于其他数据库连接池的优点</div>
                    </li>
                    <li><a href="/article/1950144473719697408.htm"
                           title="微信公众号写作:如何通过文字变现?" target="_blank">微信公众号写作:如何通过文字变现?</a>
                        <span class="text-muted">氧惠爱高省</span>

                        <div>微信公众号已成为许多人分享知识、表达观点的重要平台。随着自媒体的发展,越来越多的人开始关注微信公众号上写文章如何挣钱的问题。本文将详细探讨微信公众号写作的盈利模式,帮助广大写作者实现文字变现的梦想。公众号流量主就找善士导师(shanshi2024)公众号:「善士笔记」主理人,《我的亲身经历,四个月公众号流量主从0到日入过万!》公司旗下管理800+公众号矩阵账号。代表案例如:爸妈领域、职场道道、国学</div>
                    </li>
                    <li><a href="/article/1950143071731642368.htm"
                           title="流利说懂你英语笔记要点句型·核心课·Level 8·Unit 3·Part 2·Video 1·Healing Architecture 1" target="_blank">流利说懂你英语笔记要点句型·核心课·Level 8·Unit 3·Part 2·Video 1·Healing Architecture 1</a>
                        <span class="text-muted">羲之大鹅video</span>

                        <div>HealingArchitecture1EveryweekendforaslongasIcanremember,myfatherwouldgetuponaSaturday,putonawornsweatshirtandhe'dscrapeawayatthesqueakyoldwheelofahousethatwelivedin.ps:从我记事起,每个周末,我父亲都会在周六起床,穿上一件破旧的运动衫</div>
                    </li>
                    <li><a href="/article/1950142042847899648.htm"
                           title="java学习笔记8" target="_blank">java学习笔记8</a>
                        <span class="text-muted">幸福,你等等我</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>一、异常处理Error:错误,程序员无法处理,如OOM内存溢出错误、内存泄漏...会导出程序崩溃1.异常:程序中一些程序自身处理不了的特殊情况2.异常类Exception3.异常的分类:(1).检查型异常(编译异常):在编译时就会抛出的异常(代码上会报错),需要在代码中编写处理方式(和程序之外的资源访问)直接继承Exception(2).运行时异常:在代码运行阶段可能会出现的异常,可以不用明文处理</div>
                    </li>
                    <li><a href="/article/1950141538352820224.htm"
                           title="2025.07 Java入门笔记01" target="_blank">2025.07 Java入门笔记01</a>
                        <span class="text-muted">殷浩焕</span>
<a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a>
                        <div>一、熟悉IDEA和Java语法(一)LiuCourseJavaOOP1.一直在用C++开发,python也用了些,Java是真的不熟,用什么IDE还是问的同事;2.一开始安装了jdk-23,拿VSCode当编辑器,在cmd窗口编译运行,也能玩;但是想正儿八经搞项目开发,还是需要IDE;3.安装了IDEA社区版:(1)IDE通常自带对应编程语言的安装包,例如IDEA自带jbr-21(和jdk是不同的</div>
                    </li>
                    <li><a href="/article/1950140148146565120.htm"
                           title="Java注解笔记" target="_blank">Java注解笔记</a>
                        <span class="text-muted">m0_65470938</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>一、什么是注解Java注解又称Java标注,是在JDK5时引入的新特性,注解(也被称为元数据)Javaa注解它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程元素类、方法、成员变量等)进行关联二、注解的应用1.生成文档这是最常见的,也是iava最早提供的注解2.在编译时进行格式检查,如@Overide放在方法前,如果你这个方法并不是看盖了超类Q方法,则编译时就能检查</div>
                    </li>
                    <li><a href="/article/1950139769560297472.htm"
                           title="Java 笔记 transient 用法" target="_blank">Java 笔记 transient 用法</a>
                        <span class="text-muted"></span>

                        <div>transient关键字用于标记不希望被序列化(Serialization)的字段。序列化是指将对象的状态保存到字节流中,以便将其传输或存储。当使用如ObjectOutputStream进行序列化时,transient修饰的字段将不会被序列化。✅1.使用场景避免序列化敏感信息privatetransientStringpassword;某些字段不需要持久化(如缓存、临时数据)privatetran</div>
                    </li>
                    <li><a href="/article/1950139768872431616.htm"
                           title="Java 笔记 lambda" target="_blank">Java 笔记 lambda</a>
                        <span class="text-muted">五行缺弦</span>
<a class="tag" taget="_blank" href="/search/Java%E7%AC%94%E8%AE%B0/1.htm">Java笔记</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a>
                        <div>✅Lambda基本语法(parameters)->expression或(parameters)->{statements}//无参数Runnabler=()->System.out.println("Hello");//单个参数(小括号可省略)Consumerc=s->System.out.println(s);//多参数+多语句Comparatorcomp=(a,b)->{System.out</div>
                    </li>
                    <li><a href="/article/1950135315482079232.htm"
                           title="【208】《班级管理课》读书感悟(一百零五)2023-07-23" target="_blank">【208】《班级管理课》读书感悟(一百零五)2023-07-23</a>
                        <span class="text-muted">南风如我意</span>

                        <div>-----------《班级管理课》读书感悟四文/李现风2023年读书笔记读书笔记以以下三个出发点为目的:一、书中的思想,提升自己的教育理念;二、书中的值得借鉴的做法,提升自己的教育技巧;三、书中的美句,有鉴于哲理性的句子,提升自己文章的语言魅力和教育文化水准。读《班级管理课》作者陈宇读书感悟四:【书目】《班级管理课》【页数】第70页第87页【阅读内容(摘录)】第四课开学一个月:班级常规工作正常运</div>
                    </li>
                    <li><a href="/article/1950125273672380416.htm"
                           title="3步搞定群晖NAS Synology Drive远程同步Obsidian笔记" target="_blank">3步搞定群晖NAS Synology Drive远程同步Obsidian笔记</a>
                        <span class="text-muted"></span>

                        <div>文章目录1.简介1.1软件特色演示:2.使用免费群晖虚拟机搭建群晖SynologyDrive服务,实现局域网同步2.1安装并设置SynologyDrive套件2.1局域网内同步文件测试3.内网穿透群晖SynologyDrive,实现异地多端同步3.1安装Cpolar步骤4.实现固定TCP地址同步1.简介之前我们介绍过如何免费多端同步Zotero科研文献管理软件,使用了群晖NAS虚拟机和WebDav</div>
                    </li>
                    <li><a href="/article/1950122432392130560.htm"
                           title="R语言笔记Day1(排序、筛选以及分类汇总))" target="_blank">R语言笔记Day1(排序、筛选以及分类汇总))</a>
                        <span class="text-muted">养猪场小老板</span>

                        <div>一、排序1、单变量序列排序2、数据表(矩阵)排序二、筛选三、分类汇总一、排序1、单变量序列排序rank、sort和order函数>aa[1]315#rank用来计算序列中每个元素的秩#这里的“秩”可以理解为该元素在序列中由小到大排列的次序#上面例子给出的序列[3,1,5]中,1最小,5最大,3居中#于是1的秩为1,3的秩为2,5的秩为3,(3,1,5)对应的秩的结果就是(2,1,3)>rank(a</div>
                    </li>
                                <li><a href="/article/4.htm"
                                       title="java的(PO,VO,TO,BO,DAO,POJO)" target="_blank">java的(PO,VO,TO,BO,DAO,POJO)</a>
                                    <span class="text-muted">Cb123456</span>
<a class="tag" taget="_blank" href="/search/VO/1.htm">VO</a><a class="tag" taget="_blank" href="/search/TO/1.htm">TO</a><a class="tag" taget="_blank" href="/search/BO/1.htm">BO</a><a class="tag" taget="_blank" href="/search/POJO/1.htm">POJO</a><a class="tag" taget="_blank" href="/search/DAO/1.htm">DAO</a>
                                    <div>转: 
http://www.cnblogs.com/yxnchinahlj/archive/2012/02/24/2366110.html 
  
------------------------------------------------------------------- 
 O/R Mapping 是 Object Relational Mapping(对象关系映</div>
                                </li>
                                <li><a href="/article/131.htm"
                                       title="spring ioc原理(看完后大家可以自己写一个spring)" target="_blank">spring ioc原理(看完后大家可以自己写一个spring)</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                                    <div>         最近,买了本Spring入门书:spring In Action 。大致浏览了下感觉还不错。就是入门了点。Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning,但怀着崇敬 的心情和激情通览了一遍。又一次接受了IOC 、DI、AOP等Spring核心概念。 先就IOC和DI谈一点我的看法。IO</div>
                                </li>
                                <li><a href="/article/258.htm"
                                       title="MyEclipse 2014中Customize Persperctive设置无效的解决方法" target="_blank">MyEclipse 2014中Customize Persperctive设置无效的解决方法</a>
                                    <span class="text-muted">Kai_Ge</span>
<a class="tag" taget="_blank" href="/search/MyEclipse2014/1.htm">MyEclipse2014</a>
                                    <div>高高兴兴下载个MyEclipse2014,发现工具条上多了个手机开发的按钮,心生不爽就想弄掉他!
结果发现Customize Persperctive失效!!
有说更新下就好了,可是国内Myeclipse访问不了,何谈更新...
so~这里提供了更新后的一下jar包,给大家使用! 
1、将9个jar复制到myeclipse安装目录\plugins中
2、删除和这9个jar同包名但是版本号较</div>
                                </li>
                                <li><a href="/article/385.htm"
                                       title="SpringMvc上传" target="_blank">SpringMvc上传</a>
                                    <span class="text-muted">120153216</span>
<a class="tag" taget="_blank" href="/search/springMVC/1.htm">springMVC</a>
                                    <div>  
@RequestMapping(value = WebUrlConstant.UPLOADFILE)
	@ResponseBody
	public Map<String, Object> uploadFile(HttpServletRequest request,HttpServletResponse httpresponse) {
		try {
			// </div>
                                </li>
                                <li><a href="/article/512.htm"
                                       title="Javascript----HTML DOM 事件" target="_blank">Javascript----HTML DOM 事件</a>
                                    <span class="text-muted">何必如此</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a>
                                    <div>HTML DOM 事件允许Javascript在HTML文档元素中注册不同事件处理程序。 
 
事件通常与函数结合使用,函数不会在事件发生前被执行! 
 
 
注:DOM: 指明使用的 DOM 属性级别。 
 
1.鼠标事件 
属性               </div>
                                </li>
                                <li><a href="/article/639.htm"
                                       title="动态绑定和删除onclick事件" target="_blank">动态绑定和删除onclick事件</a>
                                    <span class="text-muted">357029540</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a>
                                    <div>因为对JQUERY和JS的动态绑定事件的不熟悉,今天花了好久的时间才把动态绑定和删除onclick事件搞定!现在分享下我的过程。 
 
     在我的查询页面,我将我的onclick事件绑定到了tr标签上同时传入当前行(this值)参数,这样可以在点击行上的任意地方时可以选中checkbox,但是在我的某一列上也有一个onclick事件是用于下载附件的,当</div>
                                </li>
                                <li><a href="/article/766.htm"
                                       title="HttpClient|HttpClient请求详解" target="_blank">HttpClient|HttpClient请求详解</a>
                                    <span class="text-muted">7454103</span>
<a class="tag" taget="_blank" href="/search/apache/1.htm">apache</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">应用服务器</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/1.htm">网络协议</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%BA%94%E7%94%A8/1.htm">网络应用</a><a class="tag" taget="_blank" href="/search/Security/1.htm">Security</a>
                                    <div>HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。本文首先介绍 HTTPClient,然后根据作者实际工作经验给出了一些常见问题的解决方法。HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需</div>
                                </li>
                                <li><a href="/article/893.htm"
                                       title="递归 逐层统计树形结构数据" target="_blank">递归 逐层统计树形结构数据</a>
                                    <span class="text-muted">darkranger</span>
<a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a>
                                    <div>将集合递归获取树形结构: 
 
 
/** 
 * 
 * 递归获取数据 
 * @param alist:所有分类 
 * @param subjname:对应统计的项目名称 
 * @param pk:对应项目主键 
 * @param reportList: 最后统计的结果集 
 * @param count:项目级别 
 */ 
 
 public void getReportVO(Arr</div>
                                </li>
                                <li><a href="/article/1020.htm"
                                       title="访问WEB-INF下使用frameset标签页面出错的原因" target="_blank">访问WEB-INF下使用frameset标签页面出错的原因</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/struts2/1.htm">struts2</a>
                                    <div><frameset rows="61,*,24" cols="*" framespacing="0" frameborder="no" border="0">          </div>
                                </li>
                                <li><a href="/article/1147.htm"
                                       title="MAVEN常用命令" target="_blank">MAVEN常用命令</a>
                                    <span class="text-muted">avords</span>

                                    <div>Maven库: 
http://repo2.maven.org/maven2/ 
Maven依赖查询: 
http://mvnrepository.com/ 
Maven常用命令: 1. 创建Maven的普通java项目:    mvn archetype:create    -DgroupId=packageName </div>
                                </li>
                                <li><a href="/article/1274.htm"
                                       title="PHP如果自带一个小型的web服务器就好了" target="_blank">PHP如果自带一个小型的web服务器就好了</a>
                                    <span class="text-muted">houxinyou</span>
<a class="tag" taget="_blank" href="/search/apache/1.htm">apache</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">应用服务器</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a><a class="tag" taget="_blank" href="/search/%E8%84%9A%E6%9C%AC/1.htm">脚本</a>
                                    <div>最近单位用PHP做网站,感觉PHP挺好的,不过有一些地方不太习惯,比如,环境搭建。PHP本身就是一个网站后台脚本,但用PHP做程序时还要下载apache,配置起来也不太很方便,虽然有好多配置好的apache+php+mysq的环境,但用起来总是心里不太舒服,因为我要的只是一个开发环境,如果是真实的运行环境,下个apahe也无所谓,但只是一个开发环境,总有一种杀鸡用牛刀的感觉。如果php自己的程序中</div>
                                </li>
                                <li><a href="/article/1401.htm"
                                       title="NoSQL数据库之Redis数据库管理(list类型)" target="_blank">NoSQL数据库之Redis数据库管理(list类型)</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/NoSQL/1.htm">NoSQL</a>
                                    <div>3.list类型及操作 
        List是一个链表结构,主要功能是push、pop、获取一个范围的所有值等等,操作key理解为链表的名字。Redis的list类型其实就是一个每个子元素都是string类型的双向链表。我们可以通过push、pop操作从链表的头部或者尾部添加删除元素,这样list既可以作为栈,又可以作为队列。 
  &nbs</div>
                                </li>
                                <li><a href="/article/1528.htm"
                                       title="谁在用Hadoop?" target="_blank">谁在用Hadoop?</a>
                                    <span class="text-muted">bingyingao</span>
<a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98/1.htm">数据挖掘</a><a class="tag" taget="_blank" href="/search/%E5%85%AC%E5%8F%B8/1.htm">公司</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF/1.htm">应用场景</a>
                                    <div>Hadoop技术的应用已经十分广泛了,而我是最近才开始对它有所了解,它在大数据领域的出色表现也让我产生了兴趣。浏览了他的官网,其中有一个页面专门介绍目前世界上有哪些公司在用Hadoop,这些公司涵盖各行各业,不乏一些大公司如alibaba,ebay,amazon,google,facebook,adobe等,主要用于日志分析、数据挖掘、机器学习、构建索引、业务报表等场景,这更加激发了学习它的热情。</div>
                                </li>
                                <li><a href="/article/1655.htm"
                                       title="【Spark七十六】Spark计算结果存到MySQL" target="_blank">【Spark七十六】Spark计算结果存到MySQL</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                                    <div>package spark.examples.db

import java.sql.{PreparedStatement, Connection, DriverManager}

import com.mysql.jdbc.Driver
import org.apache.spark.{SparkContext, SparkConf}

object SparkMySQLInteg</div>
                                </li>
                                <li><a href="/article/1782.htm"
                                       title="Scala: JVM上的函数编程" target="_blank">Scala: JVM上的函数编程</a>
                                    <span class="text-muted">bookjovi</span>
<a class="tag" taget="_blank" href="/search/scala/1.htm">scala</a><a class="tag" taget="_blank" href="/search/erlang/1.htm">erlang</a><a class="tag" taget="_blank" href="/search/haskell/1.htm">haskell</a>
                                    <div>    说Scala是JVM上的函数编程一点也不为过,Scala把面向对象和函数型编程这两种主流编程范式结合了起来,对于熟悉各种编程范式的人而言Scala并没有带来太多革新的编程思想,scala主要的有点在于Java庞大的package优势,这样也就弥补了JVM平台上函数型编程的缺失,MS家.net上已经有了F#,JVM怎么能不跟上呢? 
    对本人而言</div>
                                </li>
                                <li><a href="/article/1909.htm"
                                       title="jar打成exe" target="_blank">jar打成exe</a>
                                    <span class="text-muted">bro_feng</span>
<a class="tag" taget="_blank" href="/search/java+jar+exe/1.htm">java jar exe</a>
                                    <div>今天要把jar包打成exe,jsmooth和exe4j都用了。 
遇见几个问题。记录一下。 
两个软件都很好使,网上都有图片教程,都挺不错。 
 
首先肯定是要用自己的jre的,不然不能通用,其次别忘了把需要的lib放到classPath中。 
困扰我很久的一个问题是,我自己打包成功后,在一个同事的没有装jdk的电脑上运行,就是不行,报错jvm.dll为无效的windows映像,如截图 
最后发现</div>
                                </li>
                                <li><a href="/article/2036.htm"
                                       title="读《研磨设计模式》-代码笔记-策略模式-Strategy" target="_blank">读《研磨设计模式》-代码笔记-策略模式-Strategy</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a>
                                    <div>声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/ 
 
 




/*
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化

简单理解:
1、将不同的策略提炼出一个共同接口。这是容易的,因为不同的策略,只是算法不同,需要传递的参数</div>
                                </li>
                                <li><a href="/article/2163.htm"
                                       title="cmd命令值cvfM命令" target="_blank">cmd命令值cvfM命令</a>
                                    <span class="text-muted">chenyu19891124</span>
<a class="tag" taget="_blank" href="/search/cmd/1.htm">cmd</a>
                                    <div>     cmd命令还真是强大啊。今天发现jar -cvfM aa.rar @aaalist 就这行命令可以根据aaalist取出相应的文件 
  例如: 
     在d:\workspace\prpall\test.java 有这样一个文件,现在想要将这个文件打成一个包。运行如下命令即可比如在d:\wor</div>
                                </li>
                                <li><a href="/article/2290.htm"
                                       title="OpenJWeb(1.8) Java Web应用快速开发平台" target="_blank">OpenJWeb(1.8) Java Web应用快速开发平台</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%A1%86%E6%9E%B6/1.htm">框架</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/1.htm">项目管理</a><a class="tag" taget="_blank" href="/search/%E4%BC%81%E4%B8%9A%E5%BA%94%E7%94%A8/1.htm">企业应用</a>
                                    <div>  
  OpenJWeb(1.8) Java Web应用快速开发平台的作者是我们技术联盟的成员,他最近推出了新版本的快速应用开发平台  OpenJWeb(1.8),我帮他做做宣传 
 
  OpenJWeb快速开发平台以快速开发为核心,整合先进的java 开源框架,本着自主开发+应用集成相结合的原则,旨在为政府、企事业单位、软件公司等平台用户提供一个架构透</div>
                                </li>
                                <li><a href="/article/2417.htm"
                                       title="Python 报错:IndentationError: unexpected indent" target="_blank">Python 报错:IndentationError: unexpected indent</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/tab/1.htm">tab</a><a class="tag" taget="_blank" href="/search/%E7%A9%BA%E6%A0%BC/1.htm">空格</a><a class="tag" taget="_blank" href="/search/%E7%BC%A9%E8%BF%9B/1.htm">缩进</a>
                                    <div>    IndentationError: unexpected indent 是缩进的问题,也有可能是tab和空格混用啦 
 
    Python开发者有意让违反了缩进规则的程序不能通过编译,以此来强制程序员养成良好的编程习惯。并且在Python语言里,缩进而非花括号或者某种关键字,被用于表示语句块的开始和退出。增加缩进表示语句块的开</div>
                                </li>
                                <li><a href="/article/2544.htm"
                                       title="HttpClient 超时设置" target="_blank">HttpClient 超时设置</a>
                                    <span class="text-muted">dongwei_6688</span>
<a class="tag" taget="_blank" href="/search/httpclient/1.htm">httpclient</a>
                                    <div>HttpClient中的超时设置包含两个部分: 
1. 建立连接超时,是指在httpclient客户端和服务器端建立连接过程中允许的最大等待时间 
2. 读取数据超时,是指在建立连接后,等待读取服务器端的响应数据时允许的最大等待时间 
  
在HttpClient 4.x中如下设置: 
  
  
HttpClient httpclient = new DefaultHttpC</div>
                                </li>
                                <li><a href="/article/2671.htm"
                                       title="小鱼与波浪" target="_blank">小鱼与波浪</a>
                                    <span class="text-muted">dcj3sjt126com</span>

                                    <div>一条小鱼游出水面看蓝天,偶然间遇到了波浪。  小鱼便与波浪在海面上游戏,随着波浪上下起伏、汹涌前进。  小鱼在波浪里兴奋得大叫:“你每天都过着这么刺激的生活吗?简直太棒了。”  波浪说:“岂只每天过这样的生活,几乎每一刻都这么刺激!还有更刺激的,要有潮汐变化,或者狂风暴雨,那才是兴奋得心脏都会跳出来。”  小鱼说:“真希望我也能变成一个波浪,每天随着风雨、潮汐流动,不知道有多么好!”  很快,小鱼</div>
                                </li>
                                <li><a href="/article/2798.htm"
                                       title="Error Code: 1175 You are using safe update mode and you tried to update a table" target="_blank">Error Code: 1175 You are using safe update mode and you tried to update a table</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                                    <div> 
 快速高效用:SET SQL_SAFE_UPDATES = 0;下面的就不要看了! 
 今日用MySQL Workbench进行数据库的管理更新时,执行一个更新的语句碰到以下错误提示: 
 Error Code: 1175 
 You are using safe update mode and you tried to update a table without a WHERE that </div>
                                </li>
                                <li><a href="/article/2925.htm"
                                       title="枚举类型详细介绍及方法定义" target="_blank">枚举类型详细介绍及方法定义</a>
                                    <span class="text-muted">gaomysion</span>
<a class="tag" taget="_blank" href="/search/enum/1.htm">enum</a><a class="tag" taget="_blank" href="/search/javaee/1.htm">javaee</a>
                                    <div>转发 
http://developer.51cto.com/art/201107/275031.htm 
 
枚举其实就是一种类型,跟int, char 这种差不多,就是定义变量时限制输入的,你只能够赋enum里面规定的值。建议大家可以看看,这两篇文章,《java枚举类型入门》和《C++的中的结构体和枚举》,供大家参考。 
 
枚举类型是JDK5.0的新特征。Sun引进了一个全新的关键字enum</div>
                                </li>
                                <li><a href="/article/3052.htm"
                                       title="Merge Sorted Array" target="_blank">Merge Sorted Array</a>
                                    <span class="text-muted">hcx2013</span>
<a class="tag" taget="_blank" href="/search/array/1.htm">array</a>
                                    <div>Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. 
Note:You may assume that nums1 has enough space (size that is</div>
                                </li>
                                <li><a href="/article/3179.htm"
                                       title="Expression Language 3.0新特性" target="_blank">Expression Language 3.0新特性</a>
                                    <span class="text-muted">jinnianshilongnian</span>
<a class="tag" taget="_blank" href="/search/el+3.0/1.htm">el 3.0</a>
                                    <div>Expression Language 3.0表达式语言规范最终版从2013-4-29发布到现在已经非常久的时间了;目前如Tomcat 8、Jetty 9、GlasshFish 4已经支持EL 3.0。新特性包括:如字符串拼接操作符、赋值、分号操作符、对象方法调用、Lambda表达式、静态字段/方法调用、构造器调用、Java8集合操作。目前Glassfish 4/Jetty实现最好,对大多数新特性</div>
                                </li>
                                <li><a href="/article/3306.htm"
                                       title="超越算法来看待个性化推荐" target="_blank">超越算法来看待个性化推荐</a>
                                    <span class="text-muted">liyonghui160com</span>
<a class="tag" taget="_blank" href="/search/%E8%B6%85%E8%B6%8A%E7%AE%97%E6%B3%95%E6%9D%A5%E7%9C%8B%E5%BE%85%E4%B8%AA%E6%80%A7%E5%8C%96%E6%8E%A8%E8%8D%90/1.htm">超越算法来看待个性化推荐</a>
                                    <div>  
       一提到个性化推荐,大家一般会想到协同过滤、文本相似等推荐算法,或是更高阶的模型推荐算法,百度的张栋说过,推荐40%取决于UI、30%取决于数据、20%取决于背景知识,虽然本人不是很认同这种比例,但推荐系统中,推荐算法起的作用起的作用是非常有限的。 
      就像任何</div>
                                </li>
                                <li><a href="/article/3433.htm"
                                       title="写给Javascript初学者的小小建议" target="_blank">写给Javascript初学者的小小建议</a>
                                    <span class="text-muted">pda158</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div>  一般初学JavaScript的时候最头痛的就是浏览器兼容问题。在Firefox下面好好的代码放到IE就不能显示了,又或者是在IE能正常显示的代码在firefox又报错了。     如果你正初学JavaScript并有着一样的处境的话建议你:初学JavaScript的时候无视DOM和BOM的兼容性,将更多的时间花在 了解语言本身(ECMAScript)。只在特定浏览器编写代码(Chrome/Fi</div>
                                </li>
                                <li><a href="/article/3560.htm"
                                       title="Java 枚举" target="_blank">Java 枚举</a>
                                    <span class="text-muted">ShihLei</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/enum/1.htm">enum</a><a class="tag" taget="_blank" href="/search/%E6%9E%9A%E4%B8%BE/1.htm">枚举</a>
                                    <div>注:文章内容大量借鉴使用网上的资料,可惜没有记录参考地址,只能再传对作者说声抱歉并表示感谢! 
  
一 基础  1)语法 
  
 
      枚举类型只能有私有构造器(这样做可以保证客户代码没有办法新建一个enum的实例) 
      枚举实例必须最先定义 
   2)特性   
 
     &nb</div>
                                </li>
                                <li><a href="/article/3687.htm"
                                       title="Java SE 6 HotSpot虚拟机的垃圾回收机制" target="_blank">Java SE 6 HotSpot虚拟机的垃圾回收机制</a>
                                    <span class="text-muted">uuhorse</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/HotSpot/1.htm">HotSpot</a><a class="tag" taget="_blank" href="/search/GC/1.htm">GC</a><a class="tag" taget="_blank" href="/search/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/1.htm">垃圾回收</a><a class="tag" taget="_blank" href="/search/VM/1.htm">VM</a>
                                    <div>官方资料,关于Java SE 6 HotSpot虚拟机的garbage Collection,非常全,英文。 
http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html 
  Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning 
&</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>