30天學(xué)習(xí)計(jì)劃 js忍者秘籍 第9章 忍者點(diǎn)金術(shù):運(yùn)行時(shí)代碼求值

9.1.1 用eval()方法進(jìn)行求值

eval()方法可能是在運(yùn)行時(shí)進(jìn)行代碼求值的最常用方式了。作為定義在全局作用域內(nèi)的eval()方法狞洋,該方法將在當(dāng)前上下文內(nèi)矗漾,執(zhí)行所傳入字符串形式的代碼毅臊。執(zhí)行返回結(jié)果則是最后一個(gè)表達(dá)式的執(zhí)行結(jié)果继低。

1)基本功能

該方法將執(zhí)行傳入代碼的字符串竹宋,在調(diào)用eval()方法的作用域內(nèi)進(jìn)行代碼求值劳澄。

示例9.1 eval()方法的基本測(cè)試

test?suite

#results?.pass{color:green;}

#results?.fail{color:red;}

function?assert(value,desc){

var?li?=?document.createElement('li');

li.className?=?value???'pass'?:?'fail';

li.appendChild(document.createTextNode(desc));

document.getElementById('results').appendChild(li);

}

assert(eval('5+5')?===?10,'5?and?5?is?10');

assert(eval('var?ninja?=?5;')?===?undefined,'no?value?was?returned.');

assert(ninja?===?5,'The?variable?ninja?was?created');

(function(){

eval('var?ninja?=?6;');

assert(ninja?===?6,'evaluated?within?the?current?scope.');

})()

assert(window.ninja?===?5,'this?global?scope?was?unaffected.');

assert(ninja?===?5,'the?global?scope?was?unaffected.');

2)求值結(jié)果

eval()方法將返回傳入字符串中最后一個(gè)表達(dá)式的執(zhí)行結(jié)果。

eval('3+4;5+6') 結(jié)果將返回11

任何不是簡(jiǎn)單變量蜈七、原始值秒拔、賦值語(yǔ)句的內(nèi)容都需要在外面包裝一個(gè)括號(hào),以便返回正確的結(jié)果飒硅。

var o = eval('({ninja:1})')

示例9.2 測(cè)試eval()的返回結(jié)果

test?suite

#results?.pass{color:green;}

#results?.fail{color:red;}

function?assert(value,desc){

var?li?=?document.createElement('li');

li.className?=?value???'pass'?:?'fail';

li.appendChild(document.createTextNode(desc));

document.getElementById('results').appendChild(li);

}

var?ninja?=?eval('({name:"ninja"})');

assert(ninja?!=?undefined,'the?ninja?was?created');

assert(ninja.name?===?'ninja','and?with?the?expected?property');

var?fn?=?eval('(function(){return?"ninja";})');

assert(typeof?fn?===?'function','the?function?as?created');

assert(fn()?===?'ninja','and?returns?expected?value');

var?ninja2?=?eval('{name:"ninja"}');

assert(ninja2?!=?undefined,'ninja2?was?created.');

assert(ninja2.name?===?'ninja','and?with?the?expected?property');

最后一個(gè)測(cè)試失敗了砂缩,因?yàn)閷?duì)象沒(méi)有按照預(yù)期進(jìn)行創(chuàng)建。

就像我們用普通方式在特定作用域內(nèi)創(chuàng)建函數(shù)一樣三娩,eval()創(chuàng)建的函數(shù)會(huì)繼承該作用域的閉包——局部作用域內(nèi)執(zhí)行eval()的衍生結(jié)果庵芭。

9.1.2 用函數(shù)構(gòu)造器進(jìn)行求值

js中所有的函數(shù)都是Function的實(shí)例,可以通過(guò)像function name(){}這樣的語(yǔ)法創(chuàng)建命名函數(shù)雀监,或者省略名稱創(chuàng)建匿名函數(shù)双吆。

也可以直接使用Function構(gòu)造器來(lái)實(shí)例化函數(shù)。

var add = new Function('a','b','return a+b');

assert(add(3,4)===7,'Function created and working!);

Function構(gòu)造器可變參數(shù)列表的最后一個(gè)參數(shù)滔悉,始終是要?jiǎng)?chuàng)建函數(shù)的函數(shù)體內(nèi)容伊诵。前面的參數(shù)則表示函數(shù)的形參名稱。

上邊代碼等價(jià)于: var add = function(a,b){return a+b}

雖然這些代碼在功能上是等同的回官,但采用Function構(gòu)造器方式有一個(gè)明顯的區(qū)別曹宴,函數(shù)體由運(yùn)行時(shí)的字符串所提供。

另外一個(gè)極其重要的實(shí)現(xiàn)區(qū)別是歉提,使用Function構(gòu)造器創(chuàng)建函數(shù)的時(shí)候笛坦,不會(huì)創(chuàng)建閉包。在不想承擔(dān)任何不相關(guān)的閉包的開(kāi)銷時(shí)苔巨,這可能是一件好事版扩。

9.1.3 用定時(shí)器進(jìn)行求值

通過(guò)定時(shí)器可以讓代碼字符串進(jìn)行求值,而且是異步的侄泽。

我們通常給定時(shí)器傳遞一個(gè)內(nèi)聯(lián)函數(shù)或函數(shù)引用礁芦。這是setTimeout()和setInterval()方法推薦使用的方式,但是這些方法也可以接受字符串的傳入悼尾,從而在定時(shí)器觸發(fā)的時(shí)候進(jìn)行求值柿扣。

var tick = window.setTimeout('alert("hi")',100)

這種方式使用情況不多,除非要求值的代碼必須是運(yùn)行時(shí)字符串闺魏。

9.1.4全局作用域內(nèi)的求值操作

示例9.3 在全局作用域內(nèi)求值代碼

test?suite

#results?.pass{color:green;}

#results?.fail{color:red;}

function?assert(value,desc){

var?li?=?document.createElement('li');

li.className?=?value???'pass'?:?'fail';

li.appendChild(document.createTextNode(desc));

document.getElementById('results').appendChild(li);

}

function?globalEval(data){

data?=?data.replace(/^\s*|\s*$/g,'');

if(data){

var?head?=?document.getElementsByTagName('head')[0]||document.documentElement,

script?=?document.createElement('script');

script.type?=?'text/javascript';

script.text?=?data;

head.appendChild(script);

head.removeChild(script);

}

}

window.onload?=?function(){

(function(){

globalEval('var?test=5;');

})()

assert(test===5,'The?code?was?evaluated?globally.')

}

9.1.5 安全的代碼求值

一個(gè)命名為Caja的谷歌項(xiàng)目未状,嘗試創(chuàng)建一個(gè)js翻譯器,以便將js轉(zhuǎn)換成一種更安全且免受惡意攻擊的形式析桥。

http://code.google.com/p/google-caja/

9.2 函數(shù)反編譯

示例9.4 將函數(shù)反編譯成字符串

function?test(a){return?a+a;}

assert(test.toString()==='function?test(a){return?a+a;}','function?decompiled')

toString()的返回值包含原始聲明的所有空格司草,包括行結(jié)束符艰垂。請(qǐng)注意,在反編譯函數(shù)的時(shí)候埋虹,需要考慮空格和函數(shù)體的格式猜憎。

反編譯行為有很多潛在的用途,尤其是在宏指令和代碼重寫(xiě)的時(shí)候搔课。在Prototype js庫(kù)中拉宗,有一個(gè)比較有趣的應(yīng)用是,將函數(shù)進(jìn)行反編譯從而讀取該函數(shù)的參數(shù)辣辫,然后將這些參數(shù)名稱保存到一個(gè)數(shù)組中旦事。通常用于確定函數(shù)想得到什么樣的參數(shù)值。

示例9.5 查找函數(shù)參數(shù)名稱的函數(shù)

test?suite

#results?.pass{color:green;}

#results?.fail{color:red;}

function?assert(value,desc){

var?li?=?document.createElement('li');

li.className?=?value???'pass'?:?'fail';

li.appendChild(document.createTextNode(desc));

document.getElementById('results').appendChild(li);

}

function?argumentNames(fn){

var?found?=?/^[\s\(]*function[^(]*\(\s*([^)]*?)\s*\)/.exec(fn.toString());

return?found?&&?found[1]???found[1].split(/,\s*/)?:?[];

}

assert(argumentNames(function(){}).length?===?0,'works?on?zero-arg?functions.')

assert(argumentNames(function(x){})[0]?===?'x','single?argument?working.')

var?results?=?argumentNames(function(a,b,c,d,e){});

assert(results[0]?==?'a'?&&?results[1]?==?'b'?&&?results[2]?==?'c'?&&?results[3]?==?'d'?&&?results[4]?==?'e','multiple?arguments?working.')

該函數(shù)反編譯了傳入的函數(shù)急灭,并使用正則表達(dá)式姐浮,將這些參數(shù)從逗號(hào)分隔的參數(shù)列表中抽取出來(lái)。

9.3 代碼求值實(shí)戰(zhàn)

9.3.1 JSON轉(zhuǎn)化

示例9.6 將JSON字符串轉(zhuǎn)化成js對(duì)象

var?json?=?'{"name":"ninja"}';

var?object?=?eval('('+json+')');

assert(object.name?===?'ninja','my?name?is?ninja!');

使用eval()做JSON解析時(shí)需要注意的主要是:通常葬馋,JSON數(shù)據(jù)來(lái)自于遠(yuǎn)程服務(wù)器卖鲤,盲目執(zhí)行遠(yuǎn)程服務(wù)器上不可信代碼,基本是不可取的畴嘶。

最受歡迎的JSON轉(zhuǎn)換器腳本是由JSON標(biāo)記的創(chuàng)造者所編寫(xiě)的蛋逾,在該轉(zhuǎn)換器中,他做了一些初步的JSON字符串解析窗悯,以防止任何惡意信息通過(guò)区匣。代碼地址:https://github.com/douglascrockford/JSON-js

他寫(xiě)的函數(shù)在實(shí)際求值之前,執(zhí)行一些重要的預(yù)處理操作蒋院。

.防范一些可能在某些瀏覽器上引起問(wèn)題的Unicode字符亏钩。

.防范惡意顯示的非JSON內(nèi)容,包括賦值運(yùn)算符和new操作符欺旧。

.確保只包含了符合JSON規(guī)范的字符姑丑。

9.3.2 導(dǎo)入有命名空間的代碼

對(duì)于將命名空間導(dǎo)入到當(dāng)前上下文,base2庫(kù)提供了一個(gè)非常有趣的解決方案辞友。因?yàn)闆](méi)有辦法將該問(wèn)題進(jìn)行自動(dòng)化操作栅哀,因此我們可以利用運(yùn)行時(shí)求值讓該實(shí)現(xiàn)變得簡(jiǎn)單。

每當(dāng)一個(gè)新類或模塊添加到base2包的時(shí)候称龙,構(gòu)造可執(zhí)行代碼的字符串留拾,對(duì)其進(jìn)行求值,可以將產(chǎn)生的函數(shù)引入到當(dāng)前上下文中茵瀑,示例如下间驮,假設(shè)已經(jīng)加載了base2躬厌。

示例9.7 測(cè)試base2的命名空間導(dǎo)入是如何工作的马昨。

base2.namespace?==??????????????????????????????????????????//#1

"var?Base=base2.Base;var?Package=base2.Package;"?+

"var?Abstract=base2.Abstract;var?Module=base2.Module;"?+

"var?Enumerable=base2.Enumerable;var?Map=base2.Map;"?+

"var?Collection=base2.Collection;var?RegGrp=base2.RegGrp;"?+

"var?Undefined=base2.Undefined;var?Null=base2.Null;"?+

"var?This=base2.This;var?True=base2.True;var?False=base2.False;"?+

"var?assignID=base2.assignID;var?detect=base2.detect;"?+

"var?global=base2.global;var?lang=base2.lang;"?+

"var?JavaScript=base2.JavaScript;var?JST=base2.JST;"?+

"var?JSON=base2.JSON;var?IO=base2.IO;var?MiniWeb=base2.MiniWeb;"?+

"var?DOM=base2.DOM;var?JSB=base2.JSB;var?code=base2.code;"?+

"var?doc=base2.doc;";

assert(typeof?This?===?"undefined",??????????????????????????//#2

"The?This?object?doesn't?exist."?);

eval(base2.namespace);???????????????????????????????????????//#3

assert(typeof?This?===?"function",???????????????????????????//#4

"And?now?the?namespace?is?imported."?);

assert(typeof?Collection?===?"function",

"Verifying?the?namespace?import."?);

這是一個(gè)用于解決復(fù)雜問(wèn)題的非常巧妙的方法竞帽。

9.3.3 JS壓縮和混淆

最好是將代碼寫(xiě)得越清晰越好,然后再進(jìn)行壓縮傳輸鸿捧。

壓縮js代碼的工具 packerhttp://dean.edwards.name/packer/使用eval()進(jìn)行大規(guī)模的重寫(xiě)和解壓

下載和求值之間的組合對(duì)頁(yè)面的性能才是最重要的屹篓。

加載時(shí)間 = 下載時(shí)間+求值時(shí)間

使用簡(jiǎn)單壓縮性能是最好的,如果要用代碼混淆匙奴,可以使用packer

9.3.4 動(dòng)態(tài)重寫(xiě)代碼

由于我們可以使用函數(shù)的toString()方法反編譯現(xiàn)有的js函數(shù)堆巧,可以從現(xiàn)有函數(shù)中提取并加工原有函數(shù)的內(nèi)容,從而創(chuàng)建一個(gè) 新函數(shù)泼菌。

單元測(cè)試庫(kù)Screw.Unit(https://github.com/nkallen/screw-unit)谍肤,就是一個(gè)這樣的案例。

Screw.Unit使用庫(kù)中提供的函數(shù)哗伯,將現(xiàn)有測(cè)試函數(shù)中的內(nèi)容進(jìn)行了動(dòng)態(tài)重寫(xiě)荒揣。

describe('Matchers',function(){

it('invokes?the?provided?matcher?on?a?call?to?expect',function(){

expect(true).to(equal,true);

expect(true).to_not(equal,false);

})

})

describe(),it()以及expect(),這些方法在全局作用域內(nèi)都不存在焊刹。Screw.Unit重寫(xiě)了這段代碼系任,使用多個(gè)width(){}語(yǔ)句,將函數(shù)內(nèi)部的內(nèi)容注入到需要執(zhí)行的函數(shù)中虐块。

var?contents?=?fn.toString().match(/^[^{]*{((.*\n*)*)}/m)[1];

var?fn?=?new?Function('matchers','specifications','with(specifications){width(matchers){'+contents+'}}')

fn.call(this.Screw.Matchers,Screw.specifications);

這是一個(gè)讓測(cè)試開(kāi)發(fā)人員在無(wú)需將變量引入到全局作用域的情況下俩滥,利用代碼求值就可以提供簡(jiǎn)潔用戶體驗(yàn)功能的場(chǎng)景。

9.3.5 面向切面的腳本標(biāo)簽

AOP贺奠,面向方面編程霜旧。

AOP技術(shù)可以在運(yùn)行時(shí)將代碼進(jìn)行注入并執(zhí)行一些“橫切”代碼,如日志記錄儡率、緩存颁糟、安全性檢查等。AOP引擎將在運(yùn)行時(shí)添加日志代碼喉悴,而不是在原有代碼中添加大量的日志語(yǔ)句棱貌,以便讓開(kāi)發(fā)人員在開(kāi)發(fā)期間不用關(guān)注這些事情。

定義自定義腳本類型是非常簡(jiǎn)單的箕肃,因?yàn)闉g覽器會(huì)忽略任何無(wú)法識(shí)別的腳本類型婚脱。通過(guò)使用一個(gè)不標(biāo)準(zhǔn)的類型值,我們可以強(qiáng)制瀏覽器完全忽視一個(gè)腳本塊勺像。

...

注意障贸,我們使用統(tǒng)一約定的“x”表示自定義類型。我們打算用這樣的塊來(lái)包含正常的js代碼吟宦,以便在頁(yè)面加載時(shí)進(jìn)行執(zhí)行篮洁,而不是通常的內(nèi)聯(lián)執(zhí)行。

示例9.8 創(chuàng)建一個(gè)在頁(yè)面加載后才執(zhí)行的腳本標(biāo)簽類型

test?suite

#results?.pass{color:green;}

#results?.fail{color:red;}

function?assert(value,desc){

var?li?=?document.createElement('li');

li.className?=?value???'pass'?:?'fail';

li.appendChild(document.createTextNode(desc));

document.getElementById('results').appendChild(li);

}

function?globalEval(data){

data?=?data.replace(/^\s*|\s*$/g,'');

if(data){

var?head?=?document.getElementsByTagName('head')[0]||document.documentElement,

script?=?document.createElement('script');

script.type?=?'text/javascript';

script.text?=?data;

head.appendChild(script);

head.removeChild(script);

}

}

window.onload?=?function(){

var?scripts?=?document.getElementsByTagName('script');

for(var?i=0;?i

if(scripts[i].type?==?'x/onload'){

globalEval(scripts[i].innerHTML)

}

}

}

assert(true,'Executed?on?page?load')

在本例中殃姓,我們提供一個(gè)瀏覽器忽略執(zhí)行的自定義腳本塊袁波。在頁(yè)面的onload處理程序中瓦阐,查詢所有的腳本塊,再篩選自定義類型的腳本塊篷牌,最后用本章前面開(kāi)發(fā)的globalEval()函數(shù)睡蟋,在全局作用域內(nèi)對(duì)腳本塊的內(nèi)容進(jìn)行求值。

這種技術(shù)有更復(fù)雜更有意義的用途枷颊。例如戳杀,將自定義腳本塊和jQuery.tmpl()方法一起使用,用于提供運(yùn)行時(shí)模板夭苗。利用它可以在用戶界面上執(zhí)行腳本信卡,或者在準(zhǔn)備操作DOM的時(shí)候,甚至是相鄰元素上執(zhí)行腳本题造。

9.3.6 元語(yǔ)言和領(lǐng)域特定語(yǔ)言

關(guān)于運(yùn)行時(shí)代碼求值的一個(gè)最重要示例坐求,可以在構(gòu)建于js之上的其他編程語(yǔ)言實(shí)現(xiàn)中看到:元語(yǔ)言∩卫妫可以將其動(dòng)態(tài)轉(zhuǎn)換成js源代碼并求值桥嗤。通常,這種定制語(yǔ)言非常特定于開(kāi)發(fā)人員的業(yè)務(wù)需求仔蝌,并且已經(jīng)創(chuàng)建了領(lǐng)域特定語(yǔ)言(DSL)這樣的名字泛领。

Processing.js

Processing.js是Processing(http://processing.org/)可視化語(yǔ)言的一部分,該可視化語(yǔ)言通常使用java實(shí)現(xiàn)敛惊。js的實(shí)現(xiàn)運(yùn)行在HTML5的Canvas元素上渊鞋,由John Resig創(chuàng)建。

這種實(shí)現(xiàn)是一種完整的編程語(yǔ)言瞧挤,可以用來(lái)操作繪圖區(qū)域的視覺(jué)顯示锡宋。

通過(guò)使用Processing.js語(yǔ)言,我們獲得一些使用js時(shí)所沒(méi)有的直接好處特恬。

.從Processing高級(jí)語(yǔ)言特性中獲益(如類和繼承)

.獲取Processing的簡(jiǎn)單但強(qiáng)大的繪圖API

.可以使用Processing現(xiàn)有的文檔和示例执俩。

所以這些高級(jí)處理代碼,都可以通過(guò)js語(yǔ)言的代碼求值功能來(lái)實(shí)現(xiàn)癌刽。

Objective-J

是Objective-C編程語(yǔ)言的js實(shí)現(xiàn)役首,被用于280Slides產(chǎn)品。

Objective-J解析程序显拜,是由js編寫(xiě)的衡奥,并可以在運(yùn)行階段轉(zhuǎn)換Objective-J代碼,它們使用輕量級(jí)表達(dá)式進(jìn)行匹配并處理Objective-C語(yǔ)法代碼远荠,而不會(huì)干擾現(xiàn)有的js代碼矮固。其處理結(jié)果是一個(gè)js代碼字符串,用于在運(yùn)行時(shí)進(jìn)行求值操作譬淳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末档址,一起剝皮案震驚了整個(gè)濱河市盹兢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辰晕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件确虱,死亡現(xiàn)場(chǎng)離奇詭異含友,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)校辩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)窘问,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人宜咒,你說(shuō)我怎么就攤上這事惠赫。” “怎么了故黑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵儿咱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我场晶,道長(zhǎng)混埠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任诗轻,我火速辦了婚禮钳宪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扳炬。我一直安慰自己吏颖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布恨樟。 她就那樣靜靜地躺著半醉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪劝术。 梳的紋絲不亂的頭發(fā)上奉呛,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音夯尽,去河邊找鬼瞧壮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匙握,可吹牛的內(nèi)容都是我干的咆槽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼圈纺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼秦忿!你這毒婦竟也來(lái)了麦射?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤灯谣,失蹤者是張志新(化名)和其女友劉穎潜秋,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胎许,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡峻呛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辜窑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钩述。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖穆碎,靈堂內(nèi)的尸體忽然破棺而出牙勘,到底是詐尸還是另有隱情,我是刑警寧澤所禀,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布方面,位于F島的核電站,受9級(jí)特大地震影響色徘,放射性物質(zhì)發(fā)生泄漏葡幸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一贺氓、第九天 我趴在偏房一處隱蔽的房頂上張望蔚叨。 院中可真熱鬧,春花似錦辙培、人聲如沸蔑水。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)搀别。三九已至,卻和暖如春尾抑,著一層夾襖步出監(jiān)牢的瞬間歇父,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工再愈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榜苫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓翎冲,卻偏偏與公主長(zhǎng)得像垂睬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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