JS定時器,DOM操作(1)

1搓幌、 單線程、任務隊列的概念

單線程

  • JavaScript是一個單線程語言,瀏覽器只會分配一個javascript引擎線程來執(zhí)行任務迅箩,這也就意味所有任務需要排隊溉愁,前一個任務結(jié)束,才會執(zhí)行后一個任務饲趋。
  • 瀏覽器是多線程的拐揭。javascript引擎線程是瀏覽器多個線程中的一個,它本身是單線程的奕塑。瀏覽器還包括很多其他線程堂污,如界面渲染線程,瀏覽器事件觸發(fā)線程龄砰,Http請求線程等盟猖。

為什么是JavaScript單線程讨衣,不能有多個線程呢?
JavaScript的單線程式镐,與它的用途有關(guān)反镇。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動娘汞,以及操作DOM歹茶。這決定了它只能是單線程,否則會帶來很復雜的同步問題你弦。比如惊豺,假定JavaScript同時有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容禽作,另一個線程刪除了這個節(jié)點尸昧,這時瀏覽器應該以哪個線程為準?
所以领迈,為了避免復雜性彻磁,從一誕生,JavaScript就是單線程狸捅,這已經(jīng)成了這門語言的核心特征

單線程模型帶來的問題衷蜓?
單線程即任務是串行的,后一個任務需要等待前一個任務的執(zhí)行尘喝,這就可能出現(xiàn)長時間的等待磁浇,造成瀏覽器失去響應(假死)。比如ajax網(wǎng)絡請求朽褪、setTimeout時間延遲置吓、DOM事件的用戶交互等,這些任務并不消耗 CPU缔赠,是一種空等衍锚,資源浪費。
所以嗤堰,瀏覽器為這些耗時任務開辟了另外的線程戴质,主要包括http請求線程,瀏覽器定時觸發(fā)器踢匣,瀏覽器事件觸發(fā)線程告匠,這些任務是異步的

同步任務,異步任務离唬?

  • 同步任務指的是后专,在主線程上排隊執(zhí)行的任務,只有前一個任務執(zhí)行完畢输莺,才能執(zhí)行后一個任務
  • 異步任務:
    疑惑:對于異步任務戚哎,我開始始終無法理解裸诽。所謂異步,就是做一件事的同事建瘫,也在干另一件事崭捍,兩件事并發(fā)進行。如果異步任務指的是那些被加入到了任務隊列中的代碼塊(也就是所謂的回調(diào)函數(shù))啰脚,那些代碼塊只是延遲了執(zhí)行殷蛇,并沒有做到和JS主線程并行執(zhí)行代碼,如何能叫異步任務橄浓?
    自己的理解:
    ①我理解的異步任務指的是http請求的過程粒梦,setTimeout設置相應時間的等待的過程,onclick等待點擊的過程等荸实,這些是由瀏覽器的其他的線程去執(zhí)行的匀们,這些過程才和JS主線程是異步的。并不是回調(diào)函數(shù)
    舉個列子:setTimeout設置等待10秒后console.log("haha")准给,這個等10秒的過程是瀏覽器的其他線程執(zhí)行的泄朴,是異步的)
    ②至于回調(diào)函數(shù),異步任務執(zhí)行結(jié)束后露氮,需要把結(jié)果祖灰,或者后續(xù)的處理交給JS主線程執(zhí)行,這是通過回調(diào)函數(shù)實現(xiàn)的
    接著上面的列子::console.log("haha")需要JS主線程執(zhí)行畔规,就是通過回調(diào)函數(shù)的方式供JS主線程調(diào)用)
    ③那么JS主線程如何拿到異步任務的回調(diào)函數(shù)呢局扶?JS設計了一個任務隊列,異步任務會將相關(guān)回調(diào)函數(shù)添加到任務隊列中叁扫,因此準確的應該是叫做callback queue(回調(diào)函數(shù)隊列)三妈。最后主線程執(zhí)行這些回調(diào)函數(shù)仍然是一個一個同步執(zhí)行的。所以異步任務的回調(diào)函數(shù)并沒有異步執(zhí)行莫绣,只是掛起畴蒲,延遲了執(zhí)行

任務隊列
1.主線程之外,還存在一個"任務隊列"(callback queue)对室。用于存放異步任務的回調(diào)函數(shù)饿凛。它一個先進先出的數(shù)據(jù)結(jié)構(gòu),排在前面的事件優(yōu)先被主線程讀取软驰。所以對于“定時器”,雖然到了設定的時間心肪,定時器的回調(diào)函數(shù)被加入到了任務隊列中锭亏,但是前面如果還有其他的事件沒執(zhí)行完,此時就要等待硬鞍,那么執(zhí)行的時間就不一定是設定的時間了
2.回調(diào)函數(shù)放置時機:
異步操作會將相關(guān)回調(diào)函數(shù)添加到任務隊列中慧瘤。而不同的異步操作添加到任務隊列的時機也不同戴已,如 onclick, setTimeout, ajax 處理的方式都不同,這些異步操作是由瀏覽器內(nèi)核的 webcore 來執(zhí)行的锅减,webcore 包含上圖中的3種 webAPI糖儡,分別是 DOM Binding、network怔匣、timer模塊握联。

  • onclick 由瀏覽器內(nèi)核的 DOM Binding 模塊來處理,當事件觸發(fā)的時候每瞒,回調(diào)函數(shù)會立即添加到任務隊列中金闽。
  • setTimeout 會由瀏覽器內(nèi)核的 timer 模塊來進行延時處理,當時間到達的時候剿骨,才會將回調(diào)函數(shù)添加到任務隊列中代芜。
  • ajax 則會由瀏覽器內(nèi)核的 network 模塊來處理,在網(wǎng)絡請求完成返回之后浓利,才將回調(diào)添加到任務隊列中挤庇。

事件循環(huán)?
主線程從"任務隊列"中讀取事件,這個過程是循環(huán)不斷的贷掖,所以整個的這種運行機制又稱為Event Loop(事件循環(huán))

回調(diào)函數(shù)"(callback)"
就是那些會被主線程掛起來的代碼嫡秕。這些被掛起來的代碼會被異步任務添加到任務隊列中,等到主線程中的同步代碼都執(zhí)行完畢羽资,這些回調(diào)函數(shù)就會被一一執(zhí)行淘菩。異步任務必須指定回調(diào)函數(shù)

圖解


圖片來自Philip Roberts的演講《Help, I'm stuck in an event-loop》

  • 主線程就是有虛線組成的那一部分,堆(heap)和棧(stack)共同組成了js主線程屠升;任務隊列就是callback queue 潮改;瀏覽器為異步任務單獨開辟的線程可以統(tǒng)一理解為WebAPIs
  • 函數(shù)的執(zhí)行就是通過進棧和出棧實現(xiàn)的,比如圖中有一個foo()函數(shù)腹暖,主線程把它推入棧中汇在,在執(zhí)行函數(shù)體時,發(fā)現(xiàn)還需要執(zhí)行上面的那幾個函數(shù)脏答,所以又把這幾個函數(shù)推入棧中糕殉,等到函數(shù)執(zhí)行完,就讓函數(shù)出棧殖告。
  • 等到stack清空時阿蝶,說明一個任務已經(jīng)執(zhí)行完了,這時就會從callback queue中尋找下一個(其實就是回調(diào)函數(shù))推入棧中(這個尋找的過程黄绩,叫做event loop羡洁,因為它總是循環(huán)的查找任務隊列里是否還有任務)。

2爽丹、下面這段代碼輸出結(jié)果是? 為什么?

var a = 1;
setTimeout(function(){
    a = 2;
    console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);

輸出:

1
3
2

原理:

  • setTimeout是異步執(zhí)行的任務筑煮,它的回調(diào)函數(shù)會在被設定的時間到達時加入到任務隊列辛蚊,等待JS主線程所有代碼執(zhí)行完成后,才會進行Event Loop真仲,從任務隊列中讀取回調(diào)函數(shù)并且執(zhí)行
  • setTimeout(f,0)袋马,指定時間為0,表示的是立刻將回調(diào)函數(shù)加入到任務隊列中秸应,但是任務隊列中的回調(diào)函數(shù)需要等到JS主線程的所有代碼都執(zhí)行完了虑凛,才會開始執(zhí)行,這也就解釋了為什么先輸出1和3灸眼,最后在輸出回調(diào)函數(shù)中的2
  • 所以setTimeout(f,0)的作用是卧檐,盡可能早地執(zhí)行指定的任務,及等到JS主線程的同步任務和“任務隊列”中已有的事件全都執(zhí)行完后立即執(zhí)行

3焰宣、下面這段代碼輸出結(jié)果是? 為什么?

var flag = true;
setTimeout(function(){
    flag = false;
},0)
while(flag){}
console.log(flag);

結(jié)果:死循環(huán)沒有任何結(jié)果
原理:setTimeout中設定的函數(shù)霉囚,需要等到同步代碼都執(zhí)行完才執(zhí)行,而flag的初始值是true匕积,因此while會運行盈罐,而while循環(huán)中又沒有任何內(nèi)容,因此會死循環(huán)沒有任何結(jié)果

4闪唆、實現(xiàn)一個節(jié)流函數(shù)

首先理解什么是函數(shù)節(jié)流
函數(shù)節(jié)流簡單講就是讓一個函數(shù)無法在很短的時間間隔內(nèi)連續(xù)執(zhí)行盅粪,只有當上一次函數(shù)執(zhí)行后過了你規(guī)定的時間間隔,才能進行下一次該函數(shù)的調(diào)用悄蕾。
函數(shù)節(jié)流有什么用呢票顾?
一定程度上能優(yōu)化性能。例如帆调,當調(diào)整瀏覽器大小的時候奠骄,onresize 事件會連續(xù)觸發(fā)。在onresize 事件處理程序內(nèi)部如果嘗試進行DOM 操作番刊,其高頻率的更改可能會讓瀏覽器崩潰含鳞。所以可以設置個函數(shù)節(jié)流,只有當調(diào)整窗口停下來歇會才開始觸發(fā)onresize 事件芹务。
實現(xiàn)原理
第一次調(diào)用函數(shù)蝉绷,設置一個定時器,在指定的時間間隔之后運行代碼枣抱。如果在這個時間間隔內(nèi)又調(diào)用這個函數(shù)熔吗,那我們就clear掉原來的定時器,再setTimeout一個新的定時器延遲一會執(zhí)行佳晶。
代碼:

function throttle(fn, delay) {
    var timer = null
    return function() {
        clearTimeout(timer)
        timer = setTimeout(function() {
            fn()
        }, delay)
    }
}

function hiFrequency() {
    console.log("do something")
}

var result = throttle(hiFrequency, 3000)
result()
result()
result()

5磁滚、列出DOM 元素選取的 API

  • getElementById():返回匹配指定ID屬性的元素節(jié)點。如果沒有發(fā)現(xiàn)匹配的節(jié)點,則返回null
<div id="box">
  <div class="color red"></div>
  <div class="color green"></div>
  <p id="content"></p>
</div>
var elem = document.getElementById("box");
console.log(elem)
/*  輸出結(jié)果
  <div id="box">
    <div class="color red"></div>
    <div class="color green"></div>
    <p id="content"></p>
  </div> */
  • getElementsByClassName():返回一個類似數(shù)組的對象(HTMLCollection類型的對象)垂攘,包括了所有class名字符合指定條件的元素(搜索范圍包括本身),元素的變化實時反映在返回結(jié)果中淤刃。任何元素節(jié)點上可以調(diào)用晒他。一個參數(shù),包含一個或多個類名的字符串(類名通過空格分隔逸贾,指的是一個元素同時包括多個class)陨仅。
<div id="box">
  <div class="color red"></div>
  <div class="color green"></div>
  <div class="color blue"></div>
  <div class="color yellow"></div>
  <div class="color pink"></div>
  <p id="content"></p>
</div>
var elements = document.getElementsByClassName('color');
console.log(elements)                                     //[div.color.red, div.color.green, div.color.blue, div.color.yellow, div.color.pink]
console.log(document.getElementsByClassName('color')[0])  //前面取出來的是個HTMLCollection類型的對象,想要獲取元素還需要這樣索引一下或者elements[x] 輸出結(jié)果:<div class="color red"></div>

var elements2 = document.getElementsByClassName('red color');   
console.log(elements2)                                    // [div.color.blue]

var elements3 = document.getElementById('box').getElementsByClassName('yellow'); 
console.log(elements3)                                    //寫法可以級聯(lián)铝侵,box元素節(jié)點上也可以調(diào)用灼伤,結(jié)果[div.color.yellow]
  • getElementsByTagName():返回所有指定標簽的元素(搜索范圍包括本身)。返回值是一個HTMLCollection對象咪鲜,也就是說狐赡,搜索結(jié)果是一個動態(tài)集合,任何元素的變化都會實時反映在返回的集合中疟丙。任何元素節(jié)點上可以調(diào)用
<div id="box">
  <div class="color red"></div>
  <p id="content"></p>
  <p></p>
</div>
var paras = document.getElementsByTagName("p");
console.log(paras[0])  // <p id="content"></p>
  • getElementsByName():用于選擇擁有name屬性的HTML元素颖侄,比如form、img享郊、frame览祖、embed和object,返回一個NodeList格式的對象炊琉,不會實時反映元素的變化展蒂。
// 假定有一個表單是<form name="x"></form>
var forms = document.getElementsByName("x");
console.log(forms[0])         // <form name="x"></form>
console.log(forms[0].tagName) // "FORM"

注:在IE瀏覽器使用這個方法,會將沒有name屬性苔咪、但有同名id屬性的元素也返回锰悼,所以name和id屬性最好設為不一樣的值。

  • querySelector():ES5的元素選擇方法悼泌。querySelector方法返回匹配指定的CSS選擇器的元素節(jié)點松捉。如果有多個節(jié)點滿足匹配條件,則返回第一個匹配的節(jié)點馆里。如果沒有發(fā)現(xiàn)匹配的節(jié)點隘世,則返回null。
var el1 = document.querySelector(".myclass");
var el2 = document.querySelector('#myParent > [ng-click]');

注:參數(shù)的寫法和css寫法一致鸠踪。querySelector方法無法選中CSS偽元素丙者。

  • querySelectorAll():ES5的元素選擇方法。querySelectorAll方法返回匹配指定的CSS選擇器的所有節(jié)點营密,返回的是NodeList類型的對象械媒。NodeList對象不是動態(tài)集合,所以元素節(jié)點的變化無法實時反映在返回結(jié)果中
elementList = document.querySelectorAll(selectors);

querySelectorAll方法的參數(shù)纷捞,可以是逗號分隔的多個CSS選擇器痢虹。

var matches = document.querySelectorAll("div.note, div.alert"); 

6、創(chuàng)建元素主儡、添加元素

創(chuàng)建元素

  • createElement():生成HTML元素節(jié)點奖唯。生成的節(jié)點是存在于內(nèi)存中的,還沒如被加入到DOM中糜值。
var newDiv = document.createElement("div");

createElement方法的參數(shù)為元素的標簽名丰捷,即元素節(jié)點的tagName屬性。如果傳入大寫的標簽名寂汇,會被轉(zhuǎn)為小寫病往。如果參數(shù)帶有尖括號(即<和>)或者是null,會報錯骄瓣。

  • createTextNode():生成文本節(jié)點停巷,參數(shù)為所要生成的文本節(jié)點的內(nèi)容。
var newContent = document.createTextNode("Hello");
  • createDocumentFragment():生成一個DocumentFragment對象累贤。DocumentFragment對象是一個存在于內(nèi)存的DOM片段叠穆,但是不屬于當前文檔,常常用來生成較復雜的DOM結(jié)構(gòu)臼膏,然后插入當前文檔硼被。這樣做的好處在于,因為DocumentFragment不屬于當前文檔渗磅,對它的任何改動嚷硫,都不會引發(fā)網(wǎng)頁的重新渲染,比直接修改當前文檔的DOM有更好的性能表現(xiàn)始鱼。
    有什么用呢仔掸?
    舉個列子:向ul中添加5個li
<ul class="navbar"></ul>
//方法一,這個方法最差,相當于操作了5次DOM
    var navbarNode = document.querySelector(".navbar")
    for(var i = 0; i < 5; i++){
        var child = document.createElement("li")
        var text = document.createTextNode("hello" + i)
        child.appendChild(text)
        navbarNode.appendChild(child)
    }
//方法二医清,先將li全部放入一個div中起暮,最后一次性加入到DOM節(jié)點中,這個雖然只和DOM交互了一次会烙,但是不符合初衷负懦,外層多了一個div
    var navbarNode = document.querySelector(".navbar")
    var container = document.createElement("div") 
    for(var i = 0; i < 5; i++){
        var child = document.createElement("li")
        var text = document.createTextNode("hello" + i)
        child.appendChild(text)
        container.appendChild(child)
    }
    navbarNode.appendChild(container)
//方法三,最優(yōu)的方法柏腻。先將li全部放入一個fragment對象中纸厉,最后一次性添加進相應的DOM節(jié)點中,fragment相當于一個隱形的元素五嫂,不會顯示在DOM中 
    var navbarNode = document.querySelector(".navbar")
    var fragment = document.createDocumentFragment() 
    for(var i = 0; i < 5; i++){
        var child = document.createElement("li")
        var text = document.createTextNode("hello" + i)
        child.appendChild(text)
        fragment.appendChild(child)
    }
    navbarNode.appendChild(fragment)

innerHTML也可以添加元素颗品,不需要通過創(chuàng)建節(jié)點肯尺,在appendChild的方式添加到DOM中,只需要HTML結(jié)構(gòu)的字符串就可以添加

<ul class="navbar"></ul>
  var navData = [1, 2, 3]
  var html = ""
  navData.forEach(function(item){
    html += "<li>" + item + "</li>"
  })                               //html結(jié)果:"<li>1</li><li>2</li><li>3</li>"
  document.querySelector(".navbar").innerHTML = html

因此也可以用 document.querySelector(".navbar").innerHTML直接獲取某個節(jié)點中的HTML結(jié)構(gòu)的字符串
和innerText的區(qū)別躯枢?

  var navData = [1, 2, 3]
  var html = ""
  navData.forEach(function(item){
    html += "<li>" + item + "</li>"
  })                               //html結(jié)果:"<li>1</li><li>2</li><li>3</li>"
  document.querySelector(".navbar").innerText = html

相當于在類名為navbar的ul中添加了<li>1</li><li>2</li><li>3</li>這行文字则吟,不會轉(zhuǎn)換為HTML結(jié)構(gòu),因此會在頁面中顯示這行文字
所以innerText也可以用來獲取元素內(nèi)包含的文本內(nèi)容锄蹂,在多層次的時候會按照元素由淺到深的順序拼接其內(nèi)容
Ex:

<div>
    <p>
        123
        <span>456</span>
    </p>
</div>

外層div的innerText返回內(nèi)容是 "123456"
注意:讓用戶輸入的內(nèi)容可以用innerText逾滥,不要用innerHTML,因為如果用戶輸入的html結(jié)構(gòu)的字符串中包含惡意的JS代碼败匹,innerHTML會執(zhí)行,容易招受攻擊

修改元素

  • appendChild():在元素末尾添加元素
var child = document.createElement("div")
var Text = document.createTextNode("哈哈")
child.appendChild(Text)                             
document.body.appendChild(child)
  • insertBefore():在當前節(jié)點的某個子節(jié)點之前再插入一個子節(jié)點讥巡。
  <ul id="menu">
    <li id="item"></li>
  </ul>
var item1 = document.createElement("li")
var item2 = document.getElementById("item2")
var menu = item2.parentNode
menu.insertBefore(item1, item2)

注:想要插入到某個子節(jié)點之后掀亩,沒有 insertAfter方法』肚辏可以使用 insertBefore方法和 nextSibling來模擬它槽棍。

var item3 = document.createElement("li")
var item2 = document.getElementById("item2")
var menu = item2.parentNode
menu.insertBefore(item3, item2.nextSibling)
  • replaceChild():用指定的節(jié)點替換當前節(jié)點的一個子節(jié)點,并返回被替換掉的節(jié)點抬驴。
replacedNode = parentNode.replaceChild(newChild, oldChild);
//newChild 用來替換 oldChild 的新節(jié)點炼七。如果該節(jié)點已經(jīng)存在于DOM樹中,則它會被從原始位置刪除布持。
//replacedNode 和oldChild相等豌拙。
  • removeChild():刪除元素
parentNode.removeChild(childNode);
  • cloneNode():克隆元素,方法有一個布爾值參數(shù)题暖,傳入true的時候會深復制按傅,也就是會復制元素及其子元素(IE還會復制其事件),false的時候只復制元素本身
node.cloneNode(true);

參考
js的單線程和異步
JavaScript 運行機制詳解:再談Event Loop

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胧卤,一起剝皮案震驚了整個濱河市唯绍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枝誊,老刑警劉巖况芒,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叶撒,居然都是意外死亡绝骚,警方通過查閱死者的電腦和手機俏橘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門侧但,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铣揉,你說我怎么就攤上這事哪审《昶牵” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滴须。 經(jīng)常有香客問我舌狗,道長,這世上最難降的妖魔是什么扔水? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任痛侍,我火速辦了婚禮,結(jié)果婚禮上魔市,老公的妹妹穿的比我還像新娘主届。我一直安慰自己,他們只是感情好待德,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布君丁。 她就那樣靜靜地躺著,像睡著了一般将宪。 火紅的嫁衣襯著肌膚如雪绘闷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天较坛,我揣著相機與錄音印蔗,去河邊找鬼。 笑死丑勤,一個胖子當著我的面吹牛华嘹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播确封,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼除呵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了爪喘?” 一聲冷哼從身側(cè)響起颜曾,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秉剑,沒想到半個月后泛豪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡侦鹏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年诡曙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片略水。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡价卤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渊涝,到底是詐尸還是另有隱情慎璧,我是刑警寧澤床嫌,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站胸私,受9級特大地震影響厌处,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜岁疼,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一阔涉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捷绒,春花似錦瑰排、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至它碎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間显押,已是汗流浹背扳肛。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乘碑,地道東北人挖息。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像兽肤,于是被迫代替她去往敵國和親套腹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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