es6學(xué)習(xí)筆記

ECMAScript 6.0( 以下簡稱ES6) 是JavaScript語言的下一代標(biāo)準(zhǔn)及老。

ECMAScript和JavaScript的關(guān)系是, 前者是后者的規(guī)格谤祖, 后者是前者的一種實現(xiàn)( 另外的ECMAScript方言還有Jscript和ActionScript) 妇多。 日常場合菜皂, 這兩個詞是可以互換的。

在前端工程化的現(xiàn)在镇草,學(xué)習(xí)es6還是有必要的眶痰。
本文為個人根據(jù)阮老師的es6標(biāo)準(zhǔn)入門學(xué)習(xí)筆記。

ES6

let和const命令

let

  1. let用來聲明變量梯啤。 它的用法類似于var竖伯, 但是所聲明的變量, 只在let命令所在的代碼塊內(nèi)有效
  2. 在循環(huán)中因宇,如果變量i是var聲明的七婴, 在全局范圍內(nèi)都有效。 所以每一次循環(huán)察滑,新的i值都會覆蓋舊值打厘,如果變量i是let聲明的, 當(dāng)前的i只在本輪循環(huán)有效贺辰, 所以每一次循環(huán)的i其實都是一個新的變量户盯。
  3. let不像var那樣會發(fā)生“變量提升”現(xiàn)象。 所以饲化, 變量一定要在聲明后使用莽鸭, 否則報錯
  4. let不允許在相同作用域內(nèi), 重復(fù)聲明同一個變量
//所聲明的變量吃靠, 只在let命令所在的代碼塊內(nèi)有效
{
    let a = 10;
    var b = 1;
    console.log('a=' + a + '\nb=' + b);

}

console.log('let代碼塊外b=' + b);
// console.log('let代碼塊外b=' + a);

arr = [1, 2, 3, 4, 5, 6, 4];
for (let i = 0; i < arr.length; i++) {
    console.log(i);
}

/**
 * 變量i是var聲明的硫眨, 在全局范圍內(nèi)都有效。 所以每一次循環(huán)巢块,
 * 新的i值都會覆蓋舊值礁阁, 導(dǎo)致最后輸出的是最后一輪的i的值
 */
var a = [];
for (var i = 0; i < 10; i++) {
    a[i] = i;
}
console.log(i);

/**
 * 變量i是let聲明的, 當(dāng)前的i只在本輪循環(huán)有效夕冲, 所以每一次循環(huán)的i其實都是一個新的變量氮兵, 所以最后輸出的是6
 * @type {Array}
 */
var a = [];
for (let i = 0; i < 10; i++) {
    a[i] = i;
}
console.log(a[6]);

/**
 * 只要塊級作用域內(nèi)存在let命令, 它所聲明的變量就“綁定”( binding) 這個區(qū)域歹鱼, 不再受外部的影響
 *存在全局變量tmp泣栈, 但是塊級作用域內(nèi)let又聲明了一個局部變量tmp, 導(dǎo)致后者綁定這個塊級作用域, 所以在let聲明變量前南片, 對tmp賦
 值會報錯掺涛。
 如果區(qū)塊中存在let和const命令, 這個區(qū)塊對這些命令聲明的變量疼进, 從一開始就形成了封閉作用域薪缆。 凡是在聲明之前就使用這些變
 量, 就會報錯伞广。
 總之拣帽, 在代碼塊內(nèi), 使用let命令聲明變量之前嚼锄, 該變量都是不可用的
 暫時性死區(qū)”也意味著typeof不再是一個百分之百安全的操作
 * @type {number}
 */
var tmp = 123;
if (true) {
    //tmp = 'abc'; // ReferenceError
    let tmp;
}

/**
 *調(diào)用bar函數(shù)之所以報錯
 *參數(shù)x默認(rèn)值等于另一個參數(shù)y减拭, 而此時y還沒有聲明
 * @param x
 * @param y
 * @returns {[null,null]}
 */
function bar(x = y, y = 2) {
    return [x, y];
}

//bar();  //報錯

function bar(x = 2, y = x) {
    return [x, y];
}
bar();

/**
 * let不允許在相同作用域內(nèi), 重復(fù)聲明同一個變量,都會報錯
 */
// function () {
//     let a = 10;
//     var a = 1;
// }
//
// function () {
//     let a = 10;
//     let a = 1;
// }

結(jié)果為:


let結(jié)果

const

  1. const聲明一個只讀的常量区丑。 一旦聲明拧粪, 常量的值就不能改變。
  2. const聲明的變量不得改變值沧侥, 這意味著可霎, const一旦聲明變量, 就必須立即初始化宴杀, 不能留到以后賦值癣朗。
  3. onst的作用域與let命令相同: 只在聲明所在的塊級作用域內(nèi)有效。
  4. const命令聲明的常量也是不提升婴氮, 同樣存在暫時性死區(qū)斯棒, 只能在聲明的位置后面使用
  5. const聲明的常量, 也與let一樣不可重復(fù)聲明
const PI=3.1415;

console.log(PI);
/**
 * 量a是一個數(shù)組主经, 這個數(shù)組本身是可寫的荣暮, 但是如果將另一個數(shù)組賦值給a, 就會報錯
 * @type {Array}
 */
const a=[];
a.push('hello');
//a = ['Dave'];

全局對象的屬性

  1. var命令和function命令聲明的全局變量罩驻, 依舊是全局對象的屬性
  2. let命令穗酥、 const命令、 class命令聲明的全局變量惠遏, 不屬于全局對象的屬性
var a = 1;
// 如果在Node的REPL環(huán)境砾跃, 可以寫成global.a
// 或者采用通用方法, 寫成this.a
this.a // 1
let b = 1;
//window.b // undefined

變量的解構(gòu)賦值

數(shù)組的解構(gòu)賦值

  1. ES6允許按照一定模式节吮, 從數(shù)組和對象中提取值抽高, 對變量進(jìn)行賦值, 這被稱為解構(gòu)( Destructuring)
/**
 * 同時給abc賦值可以用一下方式
 */
var [a, b, c] = [1, 2, 3];
console.log('a='+a+'  b='+b+'  c='+c);

/**
 * 這種寫法屬于“模式匹配”透绩, 只要等號兩邊的模式相同翘骂, 左邊的變量就會被賦予對應(yīng)的值
 */
let [foo, [[bar], baz]] = [1, [[2], 3]];
console.log('foo='+foo+'  bar='+bar+'  baz='+baz);

let [ , , third] = ["foo", "bar", "baz"];
console.log('third='+third);

let [x, , y] = [1, 2, 3];
console.log('x='+x+'  y='+y);

let [head, ...tail] = [1, 2, 3, 4];
console.log('head='+head+' tail='+tail);

/**
 *另一種情況是不完全解構(gòu)壁熄, 即等號左邊的模式, 只匹配一部分的等號右邊的數(shù)組
 */
let [x2, y2] = [1, 2, 3];
console.log('x2='+x2+'  y2='+y2);

let [a2, [b2], c2] = [1, [2, 3], 4];
console.log('a2='+a2+'  b='+b2+'  c2='+c2);

結(jié)果為:


數(shù)組的解構(gòu)賦值

注:

  • 只要某種數(shù)據(jù)結(jié)構(gòu)具有Iterator接口碳竟, 都可以采用數(shù)組形式的解構(gòu)賦值
  • 解構(gòu)賦值允許指定默認(rèn)值,ES6內(nèi)部使用嚴(yán)格相等運算符( ===) 草丧, 判斷一個位置是否有值。 所以莹桅, 如果一個數(shù)組成員不嚴(yán)格等于undefined昌执, 默認(rèn)值是不會生效的

對象的解構(gòu)賦值

解構(gòu)不僅可以用于數(shù)組, 還可以用于對象

  1. 對象的解構(gòu)與數(shù)組有一個重要的不同诈泼。 數(shù)組的元素是按次序排列的懂拾, 變量的取值由它的位置決定; 而對象的屬性沒有次序铐达, 變量必須與屬性同名委粉, 才
    能取到正確的值。
  2. 對象的解構(gòu)也可以指定默認(rèn)值娶桦。默認(rèn)值生效的條件是, 對象的屬性值嚴(yán)格等于undefined汁汗。
var {foo, bar} = {foo: "aaa", bar: "bbb"};
console.log('foo=' + foo + ' bar=' + bar);

var {foo2: foo2, bar2: bar2} = {foo2: "aaa", bar2: "bbb"};
console.log('foo2=' + foo2 + ' bar2=' + bar2);
/**
 * 真正被賦值的是變量baz衷畦, 而不是模式foo。
 */
var {foo: baz} = {foo: "aaa", bar: "bbb"};
console.log(baz);

/**
 * 和數(shù)組一樣知牌, 解構(gòu)也可以用于嵌套結(jié)構(gòu)的對象
 * 這時p是模式祈争, 不是變量, 因此不會被賦值
 * @type {{p: [string,null]}}
 */
var obj = {
    p: [
        'Hello',
        {y: 'World'}
    ]
};
var {p: [x, {y}]} = obj;
console.log('x=' + x + ' y=' + y);
/**
 * line和column是變量角寸, loc和start都是模式菩混, 不會被賦值
 * @type {{loc: {start: {line: number, column: number}}}}
 */
var node = {
    loc: {
        start: {
            line: 1,
            column: 5
        }
    }
};
var {loc: {start: {line, column}}} = node;
console.log('line=' + line + ' column=' + column);

/**
 * 嵌套賦值
 *  let命令下面一行的圓括號是必須的, 否則會報錯扁藕。 因為解析器會將起首的大括號沮峡, 理解成一個代碼塊, 而不是賦值語句亿柑。
 * @type {{}}
 */
let obj2 = {};
let arr = [];
({foo: obj2.prop, bar: arr[0]} = {foo: 123, bar: true});
console.log('obj2.prop='+obj2.prop+' arr='+arr);

/**
 * 對象的解構(gòu)賦值邢疙, 可以很方便地將現(xiàn)有對象的方法, 賦值到某個變量
 * 將Math對象的對數(shù)望薄、 正弦冀痕、 余弦三個方法斋配, 賦值到對應(yīng)的變量上
 */
let { log, sin, cos } = Math;

結(jié)果為:


對象解析賦值

字符串的解構(gòu)賦值

字符串也可以解構(gòu)賦值。 這是因為此時, 字符串被轉(zhuǎn)換成了一個類似數(shù)組的對象

注:類似數(shù)組的對象都有一個length屬性劝篷, 因此還可以對這個屬性解構(gòu)賦值

let [a, b, c, d, e] = 'hello';
let {length : len} = 'hello';
console.log(a+b+c+d+e+'  length='+len);

結(jié)果:

hello  length=5

函數(shù)參數(shù)的解構(gòu)賦值

函數(shù)的參數(shù)也可以使用解構(gòu)賦值。

函數(shù)add的參數(shù)表面上是一個數(shù)組全陨, 但在傳入?yún)?shù)的那一刻痴晦, 數(shù)組參數(shù)就被解構(gòu)成變量x和y

function add([x, y]) {
    return x + y;
}

console.log(add([1, 2]));

/**
 * 使用默認(rèn)值
 * @param x
 * @param y
 * @returns {[null,null]}
 */
function move({x = 0, y = 0} = {}) {
    return [x, y];
}

圓括號問題

只要有可能導(dǎo)致解構(gòu)的歧義儒陨, 就不得使用圓括號

不能使用圓括號的情況

  1. 變量聲明語句中, 不能帶有圓括號
  2. 函數(shù)參數(shù)中板熊, 模式不能帶有圓括號框全。
  3. 賦值語句中, 不能將整個模式干签, 或嵌套模式中的一層津辩, 放在圓括號之中。
// 全部報錯
var [(a)] = [1];
var {x: (c)} = {};
var ({x: c}) = {};
var {(x: c)} = {};
var {(x): c} = {};
var { o: ({ p: p }) } = { o: { p: 2 } };

// 報錯
function f([(z)]) { return z; }

// 全部報錯
({ p: a }) = { p: 42 };
([a]) = [5];

可以使用圓括號的情況

賦值語句的非模式部分容劳, 可以使用圓括號喘沿。

[(b)] = [3]; // 正確
({ p: (d) } = {}); // 正確
[(parseInt.prop)] = [3]; // 正確

變量的解構(gòu)賦值用途

交換變量的值

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

從函數(shù)返回多個值

// 返回一個數(shù)組
function example() {
    return [1, 2, 3];
}

var [a, b, c] = example();

// 返回一個對象
function example() {
    return {
        foo: 1,
        bar: 2
    };
}

var {foo, bar} = example();

提取JSON數(shù)據(jù)

var jsonData = {
    id: 42,
    status: "OK",
    data: [867, 5309]
};
let {id, status, data: number} = jsonData;

遍歷Map結(jié)構(gòu)

任何部署了Iterator接口的對象, 都可以用for...of循環(huán)遍歷竭贩。 Map結(jié)構(gòu)原生支持Iterator接口蚜印, 配合變量的解構(gòu)賦值, 獲取鍵名和鍵值就非常方便留量。

var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
    console.log(key + " is " + value);
}

如果只想獲取鍵名窄赋, 或者只想獲取鍵值, 可以寫成下面這樣

// 獲取鍵名
for (let [key] of map) {
    // ...
} 
// 獲取鍵值
for (let [,value] of map) {
    // ...
}

字符串

字符串的遍歷器接口

字符串可以被for...of循環(huán)遍歷

for (let codePoint of 'hello') {
    console.log(codePoint)
}

常用的新方法

includes(), startsWith(), endsWith()

  • includes(): 返回布爾值楼熄, 表示是否找到了參數(shù)字符串
  • startsWith(): 返回布爾值忆绰, 表示參數(shù)字符串是否在源字符串的頭部
  • endsWith(): 返回布爾值, 表示參數(shù)字符串是否在源字符串的尾部可岂。
var s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
  • 三個方法都支持第二個參數(shù)错敢, 表示開始搜索的位置
var s = 'Hello world!';
s.startsWith('world', 6) // true 
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

repeat()

repeat 方法返回一個新字符串, 表示將原字符串重復(fù)n 次缕粹。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

模板字符串

模板字符串( template string) 是增強(qiáng)版的字符串稚茅, 用反引號( `) 標(biāo)識。 它可以當(dāng)作普通字符串使用平斩, 也可以用來定義多行字符串亚享, 或者在字符串中
嵌入變量

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);

// 字符串中嵌入變量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

//可以放入任意的JavaScript表達(dá)式, 可以進(jìn)行運算
var x = 1;
var y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"

//調(diào)用函數(shù)
function fn() {
    return "Hello World";
} 
`foo ${fn()} bar`

注:

  • 如果在模板字符串中需要使用反引號双戳, 則前面要用反斜杠轉(zhuǎn)義
  • 如果使用模板字符串表示多行字符串虹蒋, 所有的空格和縮進(jìn)都會被保留在輸出之中
  • 模板字符串中嵌入變量, 需要將變量名寫在${}之中
  • 大括號內(nèi)部可以放入任意的JavaScript表達(dá)式飒货, 可以進(jìn)行運算魄衅, 以及引用對象屬性
  • 模板字符串之中還能調(diào)用函數(shù)

數(shù)值擴(kuò)展

Math對象的擴(kuò)展

Math.trunc()

Math.trunc方法用于去除一個數(shù)的小數(shù)部分, 返回整數(shù)部分

Math.sign()

Math.sign方法用來判斷一個數(shù)到底是正數(shù)塘辅、 負(fù)數(shù)晃虫、 還是零

Math.cbrt()

Math.cbrt方法用于計算一個數(shù)的立方根

數(shù)組的擴(kuò)展

Array.from()

Array.from方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組: 類似數(shù)組的對象( array-like object) 和可遍歷( iterable) 的對象( 包括ES6新增的數(shù)據(jù)結(jié)構(gòu)Set和
Map) 。

//常見的類似數(shù)組的對象是DOM操作返回的NodeList集合
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
    console.log(p);
});

//只要是部署了Iterator接口的數(shù)據(jù)結(jié)構(gòu)扣墩, Array.from都能將其轉(zhuǎn)為數(shù)組
Array.from('hello')
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']

數(shù)組實例的find()和findIndex()

數(shù)組實例的find方法哲银, 用于找出第一個符合條件的數(shù)組成員扛吞。 它的參數(shù)是一個回調(diào)函數(shù), 所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù)荆责, 直到找出第一個返回值
為true的成員滥比, 然后返回該成員。 如果沒有符合條件的成員做院, 則返回undefined盲泛。

數(shù)組實例的fill()

fill方法使用給定值, 填充一個數(shù)組

  • fill方法還可以接受第二個和第三個參數(shù)键耕, 用于指定填充的起始位置和結(jié)束位置

數(shù)組實例的entries()寺滚, keys()和values()

可以用for...of循環(huán)進(jìn)行遍歷, 唯一的區(qū)別是keys()是對鍵名的遍歷屈雄、 values()是對鍵值的遍歷村视, entries()是對鍵值對的遍歷。

for (let index of ['a', 'b'].keys()) {
    console.log(index);
} 
// 0
// 1
for (let elem of ['a', 'b'].values()) {
    console.log(elem);
} 
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
    console.log(index, elem);
} 
// 0 "a"
// 1 "b"

//如果不使用for...of循環(huán)酒奶, 可以手動調(diào)用遍歷器對象的next方法蚁孔, 進(jìn)行遍歷
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

數(shù)組實例的includes()

表示某個數(shù)組是否包含給定的值, 與字符串的includes方法類似

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, NaN].includes(NaN); // true

該方法的第二個參數(shù)表示搜索的起始位置惋嚎, 默認(rèn)為0勒虾。 如果第二個參數(shù)為負(fù)數(shù), 則表示倒數(shù)的位置瘸彤, 如果這時它大于數(shù)組長度( 比如第二個參數(shù)為-4,
但數(shù)組長度為3) 笛钝, 則會重置為從0開始

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

函數(shù)的擴(kuò)展

函數(shù)參數(shù)的默認(rèn)值

ES6允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值质况, 即直接寫在參數(shù)定義的后面

function log(x, y = 'World') {
    console.log(x, y);
} 
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

rest參數(shù)

ES6引入rest參數(shù)( 形式為“...變量名”) , 用于獲取函數(shù)的多余參數(shù)玻靡, 這樣就不需要使用arguments對象了结榄。 rest參數(shù)搭配的變量是一個數(shù)組, 該變量將多余的參數(shù)放入數(shù)組中

//利用rest參數(shù)囤捻, 可以向該函數(shù)傳入任意數(shù)目的參數(shù)
function add(...values) {
    let sum = 0;
    for (var val of values) {
        sum += val;
    } 
    return sum;
} 

add(2, 5, 3) // 10

//rest參數(shù)中的變量代表一個數(shù)組臼朗, 所以數(shù)組特有的方法都可以用于這個變量
function push(array, ...items) {
    items.forEach(function(item) {
        array.push(item);
        console.log(item);
    });
}
var a = [];
push(a, 1, 2, 3)

注:rest參數(shù)之后不能再有其他參數(shù)( 即只能是最后一個參數(shù))

擴(kuò)展運算符

擴(kuò)展運算符( spread) 是三個點( ...) 。 它好比rest參數(shù)的逆運算蝎土, 將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列视哑。

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

//合并數(shù)組
[1, 2, ...more]
[...arr1, ...arr2, ...arr3]

//轉(zhuǎn)為真正的數(shù)組。
var nodeList = document.querySelectorAll('div');
var array = [...nodeList];

//map轉(zhuǎn)數(shù)組
let map = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]

箭頭函數(shù)

使用“箭頭”( =>) 定義函數(shù)

var f = v => v;
//等同于
// var f = function (v) {
//     return v;
// };

//如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù)誊涯, 就使用一個圓括號代表參數(shù)部分
var f2 = () => 5;
// 等同于
// var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
// var sum = function(num1, num2) {
//     return num1 + num2;
// };

//如果箭頭函數(shù)的代碼塊部分多于一條語句挡毅, 就要使用大括號將它們括起來, 并且使用return語句返回
var sum = (num1, num2) => {
    return num1 + num2;
}

//由于大括號被解釋為代碼塊暴构, 所以如果箭頭函數(shù)直接返回一個對象跪呈, 必須在對象外面加上括號
var getTempItem = id => ({id: id, name: "Temp"});
console.log(getTempItem(5));

const full = ({ first, last }) => first + ' ' + last;
// 等同于
// function full(person) {
//     return person.first + ' ' + person.last;
// }

console.log([1,2,3].map(x => x * x));

注:

  • 函數(shù)體內(nèi)的this對象段磨, 就是定義時所在的對象, 而不是使用時所在的對象耗绿。
  • 不可以當(dāng)作構(gòu)造函數(shù)苹支, 也就是說, 不可以使用new命令误阻, 否則會拋出一個錯誤

函數(shù)綁定

箭頭函數(shù)可以綁定this對象债蜜, 大大減少了顯式綁定this對象的寫法( call、 apply堕绩、 bind) 策幼。

函數(shù)綁定運算符是并排的兩個雙冒號( ::) , 雙冒號左邊是一個對象奴紧, 右邊是一個函數(shù)特姐。 該運算符會自動將左邊的對象, 作為上下文環(huán)境( 即this對象) 黍氮, 綁定到右邊的函數(shù)上面唐含。

foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

//如果雙冒號左邊為空, 右邊是一個對象的方法沫浆, 則等于將該方法綁定在該對象上面捷枯。
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
let log = ::console.log;
// 等同于
var log = console.log.bind(console);

尾調(diào)用優(yōu)化

尾調(diào)用

某個函數(shù)的最后一步是調(diào)用另一個函數(shù)

function f(x){
    return g(x);
}
//函數(shù)f的最后一步是調(diào)用函數(shù)g, 這就叫尾調(diào)用

function f(x) {
    if (x > 0) {
        return m(x)
    } 
    return n(x);
}
//函數(shù)m和n都屬于尾調(diào)用专执, 因為它們都是函數(shù)f的最后一步操作

Set和Map數(shù)據(jù)結(jié)構(gòu)

Set

提供了新的數(shù)據(jù)結(jié)構(gòu)Set淮捆。 它類似于數(shù)組, 但是成員的值都是唯一的本股, 沒有重復(fù)的值

//Set本身是一個構(gòu)造函數(shù)攀痊, 用來生成Set數(shù)據(jù)結(jié)構(gòu)
let s = new Set();
[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));
for (let i of s) {
    console.log(i);
}

//Set函數(shù)接受數(shù)組作為參數(shù)
//Set函數(shù)可以接受一個數(shù)組( 或類似數(shù)組的對象) 作為參數(shù), 用來初始化
var set = new Set([1, 2, 3, 4, 4]);

// 例二
var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(items.size) // 5

//接受類似數(shù)組的對象作為參數(shù)
// 例三
function divs() {
    return [...document.querySelectorAll('div')];
}

var set2 = new Set(divs());
console.log(set2.size); // 56
// 類似于
divs().forEach(div => set.add(div));
console.log(set2.size); // 56

Set實例的屬性和方法

屬性
  • Set.prototype.constructor: 構(gòu)造函數(shù)拄显, 默認(rèn)就是Set函數(shù)
  • Set.prototype.size: 返回Set實例的成員總數(shù)苟径。
方法
  1. 操作方法:
  • add(value): 添加某個值, 返回Set結(jié)構(gòu)本身
  • delete(value): 刪除某個值躬审, 返回一個布爾值棘街, 表示刪除是否成功
  • has(value): 返回一個布爾值, 表示該值是否為Set的成員
  • clear(): 清除所有成員承边, 沒有返回值
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
  1. 遍歷方法
  • keys(): 返回鍵名的遍歷器
  • values(): 返回鍵值的遍歷器
  • entries(): 返回鍵值對的遍歷器
  • forEach(): 使用回調(diào)函數(shù)遍歷每個成員
let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
    console.log(item);
} 
// red
// green
// blue

for (let item of set.values()) {
    console.log(item);
} 
// red
// green
// blue

for (let item of set.entries()) {
    console.log(item);
} 
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(value * 2) )
// 2
// 4
// 6

注:由于Set結(jié)構(gòu)沒有鍵名遭殉, 只有鍵值( 或者說鍵名和鍵值是同一個值) , 所以key方法和value方法的行為完全一致博助。

  1. 應(yīng)用
//擴(kuò)展運算符( ...) 內(nèi)部使用for...of循環(huán)恩沽, 所以也可以用于Set結(jié)構(gòu)
let set = new Set(['red', 'green', 'blue']);
let arr = [...set];
// ['red', 'green', 'blue']

//去除數(shù)組的重復(fù)成員。
let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];
// [3, 5, 2]

//數(shù)組的map和filter方法也可以用于Set
let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2));
// 返回Set結(jié)構(gòu): {2, 4, 6}
let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0));
// 返回Set結(jié)構(gòu): {2, 4}

//并集( Union) 翔始、 交集( Intersect) 和差集( Difference)
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}

Map

Map結(jié)構(gòu)提供了“值—值”的對應(yīng)罗心, 是一種更完善的Hash結(jié)構(gòu)實現(xiàn)里伯。 如果你需要“鍵值對”的
數(shù)據(jù)結(jié)構(gòu),請使用Map

var m = new Map();
var o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false

// Map也可以接受一個數(shù)組作為參數(shù)渤闷。 該數(shù)組的成員是一個個表示鍵值對的數(shù)組
var map = new Map([
    ['name', '張三'],
    ['title', 'Author']
]);

console.log(map); //Map { 'name' => '張三', 'title' => 'Author' }
map.size // 2
map.has('name') // true
map.get('name') // "張三"
map.has('title') // true
map.get('title') // "Author"

//如果對同一個鍵多次賦值疾瓮, 后面的值將覆蓋前面的值
let map = new Map();
map
    .set(1, 'aaa')
    .set(1, 'bbb');
map.get(1) // "bbb"

屬性和操作方法

size屬性

size屬性返回Map結(jié)構(gòu)的成員總數(shù)。

let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
set(key, value)

set方法設(shè)置key所對應(yīng)的鍵值飒箭, 然后返回整個Map結(jié)構(gòu)狼电。 如果key已經(jīng)有值, 則鍵值會被更新弦蹂, 否則就新生成該鍵

var m = new Map();
m.set("edition", 6) // 鍵是字符串
m.set(262, "standard") // 鍵是數(shù)值
m.set(undefined, "nah") // 鍵是undefined
get(key)

get方法讀取key對應(yīng)的鍵值肩碟, 如果找不到key, 返回undefined

var m = new Map();
var hello = function() {console.log("hello");}
m.set(hello, "Hello ES6!") // 鍵是函數(shù)
m.get(hello) // Hello ES6!
has(key)

has方法返回一個布爾值凸椿, 表示某個鍵是否在Map數(shù)據(jù)結(jié)構(gòu)中

var m = new Map();
m.set("edition", 6);
m.set(262, "standard");
m.set(undefined, "nah");
m.has("edition") // true
m.has("years") // false
m.has(262) // true
m.has(undefined) // true
delete(key)

delete方法刪除某個鍵削祈, 返回true。 如果刪除失敗脑漫, 返回false髓抑。

var m = new Map();
m.set(undefined, "nah");
m.has(undefined) // true
m.delete(undefined)
m.has(undefined) // false
clear()

clear方法清除所有成員, 沒有返回值

let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0

遍歷

  • keys(): 返回鍵名的遍歷器优幸。
  • values(): 返回鍵值的遍歷器
  • entries(): 返回所有成員的遍歷器吨拍。
  • forEach(): 遍歷Map的所有成員
let map = new Map([
    ['F', 'no'],
    ['T', 'yes'],
]);
for (let key of map.keys()) {
    console.log(key);
} 
// "F"
// "T"
for (let value of map.values()) {
    console.log(value);
} 
// "no"
// "yes"
for (let item of map.entries()) {
    console.log(item[0], item[1]);
} 
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
    console.log(key, value);
} 
// 等同于使用map.entries()
for (let [key, value] of map) {
    console.log(key, value);
}

map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});

結(jié)合數(shù)組的map方法、 filter方法网杆, 可以實現(xiàn)Map的遍歷和過濾( Map本身沒有map和filter方法)

let map0 = new Map()
    .set(1, 'a')
    .set(2, 'b')
    .set(3, 'c');
let map1 = new Map(
    [...map0].filter(([k, v]) => k < 3)
);
// 產(chǎn)生Map結(jié)構(gòu) {1 => 'a', 2 => 'b'}
let map2 = new Map(
    [...map0].map(([k, v]) => [k * 2, '_' + v])
);
// 產(chǎn)生Map結(jié)構(gòu) {2 => '_a', 4 => '_b', 6 => '_c'}

map與其他數(shù)據(jù)結(jié)構(gòu)的互相轉(zhuǎn)換:

  1. Map轉(zhuǎn)為數(shù)組
    Map轉(zhuǎn)為數(shù)組最方便的方法羹饰, 就是使用擴(kuò)展運算符( ...) 。
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
  1. 數(shù)組轉(zhuǎn)為Map
    將數(shù)組轉(zhuǎn)入Map構(gòu)造函數(shù)碳却, 就可以轉(zhuǎn)為Map
new Map([[true, 7], [{foo: 3}, ['abc']]])
// Map {true => 7, Object {foo: 3} => ['abc']}
  1. Map轉(zhuǎn)為JSON
    Map轉(zhuǎn)為JSON要區(qū)分兩種情況:
  • Map的鍵名都是字符串严里, 這時可以選擇轉(zhuǎn)為對象JSON
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap));
} 
let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'
  • Map的鍵名有非字符串, 這時可以選擇轉(zhuǎn)為數(shù)組JSON
function mapToArrayJson(map) {
return JSON.stringify([...map]);
} 
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
  1. JSON轉(zhuǎn)為Map
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
} 
jsonToStrMap('{"yes":true,"no":false}')
// Map {'yes' => true, 'no' => false}

Generator 函數(shù)

Generator函數(shù)是ES6提供的一種異步編程解決方案追城, 語法行為與傳統(tǒng)函數(shù)完全不同。

執(zhí)行Generator函數(shù)會返回一個遍歷器對象燥撞, 也就是說座柱, Generator函數(shù)除了狀態(tài)機(jī),還是一個遍歷器對象生成函數(shù)物舒。 返回的遍歷器對象色洞, 可以依次遍歷Generator函數(shù)內(nèi)部的每一個狀態(tài)。

形式上冠胯, Generator函數(shù)是一個普通函數(shù)火诸, 但是有兩個特征。

  • function關(guān)鍵字與函數(shù)名之間有一個星號荠察;
  • 函數(shù)體內(nèi)部使用yield語句置蜀, 定義不同的內(nèi)部狀態(tài)( yield語句在英語里的意思就是“產(chǎn)出”) 奈搜。

Generator函數(shù)的調(diào)用方法與普通函數(shù)一樣, 也是在函數(shù)名后面加上一對圓括號盯荤。 不同的是馋吗, 調(diào)用Generator函數(shù)后, 該函數(shù)并不執(zhí)行秋秤, 返回的也不是函數(shù)運行結(jié)果宏粤, 而是一個指向內(nèi)部狀態(tài)的指針對象, 也就是上一章介紹的遍歷器對象( Iterator Object)

必須調(diào)用遍歷器對象的next方法灼卢, 使得指針移向下一個狀態(tài)绍哎。 也就是說, 每次調(diào)用next方法鞋真, 內(nèi)部指針就從函數(shù)頭部或上一次停下來的地方開始執(zhí)行崇堰, 直到遇到下一個yield語句( 或return語句) 為止。 換言之灿巧, Generator函數(shù)是分段執(zhí)行的赶袄,yield語句是暫停執(zhí)行的標(biāo)記, 而next方法可
以恢復(fù)執(zhí)行抠藕。

function* helloWorldGenerator() {
    yield 'hello';
    yield 'world';
    return 'ending';
} 
var hw = helloWorldGenerator();

hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }

yield語句

遍歷器對象的next方法的運行邏輯如下

  • 遇到y(tǒng)ield語句饿肺, 就暫停執(zhí)行后面的操作, 并將緊跟在yield后面的那個表達(dá)式的值盾似, 作為返回的對象的value屬性值
  • 下一次調(diào)用next方法時敬辣, 再繼續(xù)往下執(zhí)行, 直到遇到下一個yield語句零院。
  • 如果沒有再遇到新的yield語句溉跃, 就一直運行到函數(shù)結(jié)束, 直到return語句為止告抄, 并將return語句后面的表達(dá)式的值撰茎, 作為返回的對象的value屬性值。
  • 如果該函數(shù)沒有return語句打洼, 則返回的對象的value屬性值為undefined

注: yield語句不能用在普通函數(shù)中龄糊, 否則會報錯

Promise對象

所謂Promise, 簡單說就是一個容器募疮, 里面保存著某個未來才會結(jié)束的事件( 通常是一個異步操作) 的結(jié)果炫惩。 從語法上說, Promise是一個對象阿浓, 從它可以獲取異步操作的消息

Promise對象有以下兩個特點:

  • 對象的狀態(tài)不受外界影響他嚷。 Promise對象代表一個異步操作, 有三種狀態(tài): Pending( 進(jìn)行中) 、 Resolved( 已完成筋蓖, 又稱Fulfilled)和Rejected( 已失斝对拧) 。 只有異步操作的結(jié)果扭勉, 可以決定當(dāng)前是哪一種狀態(tài)鹊奖, 任何其他操作都無法改變這個狀態(tài)。 這也是Promise這個名字的由來涂炎, 它的英語意思就是“承諾”忠聚, 表示其他手段無法改變
  • 一旦狀態(tài)改變, 就不會再變唱捣, 任何時候都可以得到這個結(jié)果两蟀。 Promise對象的狀態(tài)改變, 只有兩種可能: 從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected震缭。 只要這兩種情況發(fā)生赂毯, 狀態(tài)就凝固了, 不會再變了拣宰, 會一直保持這個結(jié)果党涕。 就算改變已經(jīng)發(fā)生了, 你再對Promise對象添加回調(diào)函數(shù)巡社, 也會立即得到這個結(jié)果膛堤。 這與事件( Event) 完全不同, 事件的特點是晌该, 如果你錯過了它肥荔, 再去監(jiān)聽, 是得不到結(jié)果的朝群。
//創(chuàng)造了一個Promise實例燕耿。
var promise = new Promise(function(resolve, reject) {
    // ... some code
    if (/* 異步操作成功 */){
        resolve(value);
    } else {
        reject(error);
    }
});

//可以用then方法分別指定Resolved狀態(tài)和Reject狀態(tài)的回調(diào)函數(shù)
promise.then(function(value) {
    // success
}, function(error) {
    // failure
});

//用Promise對象實現(xiàn)的Ajax操作的例子
var getJSON = function (url) {
    var promise = new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();

        function handler() {
            if (this.readyState !== 4) {
                return;
            }
            if(this.status === 200)
            {
                resolve(this.response);
            }
        else
            {
                reject(new Error(this.statusText));
            }
        };
    });
    return promise;
};
getJSON("/posts.json").then(function (json) {
    console.log('Contents: ' + json);
}, function (error) {
    console.error('出錯了', error);
});

async函數(shù)

async函數(shù)就是Generator函數(shù)的語法糖。

async函數(shù)就是將Generator函數(shù)的星號( *) 替換成async姜胖, 將yield替換成await誉帅, 僅此而已

//async函數(shù)返回一個Promise對象, 可以使用then方法添加回調(diào)函數(shù)右莱。 當(dāng)函數(shù)執(zhí)行的時候蚜锨, 一旦遇到await就會先返回, 等到觸發(fā)的異步操作完成隧出, 再接著執(zhí)行函數(shù)體內(nèi)后面的語句。
async function getStockPriceByName(name) {
    var symbol = await getStockSymbol(name);
    var stockPrice = await getStockPrice(symbol);
    return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
    console.log(result);
});

//指定多少毫秒后輸出一個值
function timeout(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

async function asyncPrint(value, ms) {
    await timeout(ms);
    console.log(value)
}

asyncPrint('hello world', 50);

//Async函數(shù)有多種使用形式阀捅。
// 函數(shù)聲明
async function foo() {}
// 函數(shù)表達(dá)式
const foo = async function () {};
// 對象的方法
let obj = { async foo() {} };
// 箭頭函數(shù)
const foo = async () => {};

Class

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

    toString() {
        return `x=${this.x}    y=${this.y}`;
    }
}

let point = new Point(1, 2);
console.log(point.toString()); //x=1    y=2

注:定義“類”的方法的時候胀瞪, 前面不需要加上function這個關(guān)鍵字, 直接把函數(shù)定義放進(jìn)去了就可以了。 另外凄诞, 方法之間不需要逗號分隔圆雁, 加了會報錯。

constructor方法

constructor方法是類的默認(rèn)方法帆谍, 通過new命令生成對象實例時伪朽, 自動調(diào)用該方法。 一個類必須有constructor方法汛蝙, 如果沒有顯式定義烈涮, 一個空的constructor方法會被默認(rèn)添加

class Foo {
    constructor() {
        return Object.create(null);
    }
} 
new Foo() instanceof Foo

Class的繼承

class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y); // 調(diào)用父類的constructor(x, y)
        this.color = color;
    }

    toString() {
        return this.color + ' ' + super.toString(); // 調(diào)用父類的toString()
    }
}

注:

  • 子類必須在constructor方法中調(diào)用super方法, 否則新建實例時會報錯窖剑。 這是因為子類沒有自己的this對象坚洽, 而是繼承父類的this對象, 然后對其進(jìn)行加工
  • 在子類的構(gòu)造函數(shù)中西土, 只有調(diào)用super之后讶舰, 才可以使用this關(guān)鍵字, 否則會報錯

Module

模塊功能主要由兩個命令構(gòu)成: export和import需了。 export命令用于規(guī)定模塊的對外接口跳昼, import命令用于輸入其他模塊提供的功能。
一個模塊就是一個獨立的文件肋乍。 該文件內(nèi)部的所有變量鹅颊, 外部無法獲取。 如果你希望外部能夠讀取模塊內(nèi)部的某個變量住拭, 就必須使用export關(guān)鍵字輸出該變量

export

//用export命令對外部輸出了三個變量
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

//另外一種寫法
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};

//export命令除了輸出變量挪略, 還可以輸出函數(shù)或類( class) 。
export function multiply(x, y) {
    return x * y;
};

//export輸出的變量就是本來的名字滔岳, 但是可以使用as關(guān)鍵字重命名杠娱。
function v1() { ... }
function v2() { ... }
export {
    v1 as streamV1,
    v2 as streamV2,
    v2 as streamLatestVersion
};

//export命令規(guī)定的是對外的接口, 必須與模塊內(nèi)部的變量建立一一對應(yīng)關(guān)系
// 寫法一
export var m = 1;
// 寫法二
var m = 1;
export {m};
// 寫法三
var n = 1;
export {n as m};

import

使用export命令定義了模塊的對外接口以后谱煤, 其他JS文件就可以通過import命令加載這個模塊( 文件)
import命令接受一個對象( 用大括號表示) 摊求, 里面指定要從其他模塊導(dǎo)入的變量名。 大括號里面的變量名刘离, 必須與被導(dǎo)入模塊( profile.js) 對外接口的名稱相同室叉。

//從profile中導(dǎo)入firstName, lastName, year
import {firstName, lastName, year} from './profile';
function setName(element) {
    element.textContent = firstName + ' ' + lastName;
}

//import命令要使用as關(guān)鍵字, 將輸入的變量重命名
import { lastName as surname } from './profile';

模塊的整體加載

除了指定加載某個輸出值硫惕, 還可以使用整體加載茧痕, 即用星號( *) 指定一個對象, 所有輸出值都加載在這個對象上面

// circle.js
export function area(radius) {
    return Math.PI * radius * radius;
}
export function circumference(radius) {
    return 2 * Math.PI * radius;
}

//整體加載
import * as circle from './circle';
console.log('圓面積: ' + circle.area(4));
console.log('圓周長: ' + circle.circumference(14));

export default命令

為模塊指定默認(rèn)輸出

// export-default.js
//默認(rèn)輸出是一個函數(shù)恼除。
export default function () {
    console.log('foo');
}

// import-default.js
//import命令可以為該匿名函數(shù)指定任意名字踪旷。
import customName from './export-default';
customName(); // 'foo'

注:一個模塊只能有一個默認(rèn)輸出曼氛, 因此export deault命令只能使用一次。 所以令野, import命令后面才不用加大括號舀患, 因為只可能對應(yīng)一個方法。

跨模塊常量

const聲明的常量只在當(dāng)前代碼塊有效气破。 如果想設(shè)置跨模塊的常量( 即跨多個文件) 聊浅, 可以采用下面的寫法

// constants.js 模塊
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模塊
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模塊
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市现使,隨后出現(xiàn)的幾起案子低匙,更是在濱河造成了極大的恐慌,老刑警劉巖朴下,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件努咐,死亡現(xiàn)場離奇詭異,居然都是意外死亡殴胧,警方通過查閱死者的電腦和手機(jī)渗稍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來团滥,“玉大人竿屹,你說我怎么就攤上這事【逆ⅲ” “怎么了拱燃?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長力惯。 經(jīng)常有香客問我碗誉,道長,這世上最難降的妖魔是什么父晶? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任哮缺,我火速辦了婚禮,結(jié)果婚禮上甲喝,老公的妹妹穿的比我還像新娘尝苇。我一直安慰自己,他們只是感情好埠胖,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布糠溜。 她就那樣靜靜地躺著,像睡著了一般直撤。 火紅的嫁衣襯著肌膚如雪非竿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天谋竖,我揣著相機(jī)與錄音红柱,去河邊找鬼侮东。 笑死,一個胖子當(dāng)著我的面吹牛豹芯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播驱敲,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铁蹈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了众眨?” 一聲冷哼從身側(cè)響起握牧,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎娩梨,沒想到半個月后沿腰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡狈定,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年颂龙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纽什。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡措嵌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芦缰,到底是詐尸還是另有隱情企巢,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布让蕾,位于F島的核電站浪规,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏探孝。R本人自食惡果不足惜笋婿,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望再姑。 院中可真熱鬧萌抵,春花似錦、人聲如沸元镀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栖疑。三九已至讨永,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遇革,已是汗流浹背卿闹。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工揭糕, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锻霎。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓著角,卻偏偏與公主長得像,于是被迫代替她去往敵國和親旋恼。 傳聞我的和親對象是個殘疾皇子吏口,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容