JSPatch 基礎用法

原文: JSPatch 基礎用法

JSPatch Platform


索引

  1. Request
  2. 調用 OC 方法
    2.1 調用類方法
    2.2 調用實例方法
    2.3 參數傳遞
    2.4 Property
    2.5 方法名轉換
  3. defineClass
    3.1 API
    3.2 覆蓋方法
    3.3 覆蓋類方法
    3.4 覆蓋 Category 方法
    3.5 Super
    3.6 Property
    3.7 獲取/修改 OC 定義的 Property
    3.8 動態新增 Property
    3.9 私有成員變量
    3.10 添加新方法
    3.11 Protocol
  4. 特殊類型
    4.1 Struct
    4.2 Selector
    4.3 nil
  5. NSArray / NSString / NSDictionary
  6. Block
    6.1 Block 傳遞
    6.2 Block 里使用 Self 變量
    6.3 限制
  7. __weak / __strong
  8. GCD
  9. 傳遞 id* 參數
  10. 常亮, 枚舉, 宏定義, 全局變量
    10.1 常量, 枚舉
    10.2 宏定義
    10.3 全局變量
  11. Swift
  12. 加載動態庫
  13. 調試

<br />

Require


在使用 Objective-C 類之前需要調用 require('className’):

require('UIView')
var view = UIView.alloc().init()

可以使用逗號,分隔, 一次性導入多個類:

require('UIView, UIColor')
var view = UIView.alloc().init()
var red = UIColor.redColor()

或者直接在使用時才調用 require():

require('UIView').alloc().init()

<br />

調用 OC 方法


調用類方法
var redColor = UIColor.redColor();
調用實例方法
var view = UIView.alloc().init();
view.setNeedsLayout();
參數傳遞

跟在 OC 一樣傳遞參數:

var view = UIView.alloc().init();
var superView = UIView.alloc().init()
superView.addSubview(view)
Property

獲取/修改 Property 等于調用這個 Property 的 getter / setter 方法, 獲取時記得加():

view.setBackgroundColor(redColor);
var bgColor = view.backgroundColor();
方法名轉換

多參數方法名使用 _ 分隔, 參數使用 , 分隔:

var indexPath = require('NSIndexPath').indexPathForRow_inSection(0, 1);

若原 OC 方法名里包含下劃線_, 在 JS 使用雙下劃線 __ 代替:

// Obj-C: [JPObject _privateMethod];
JPObject.__privateMethod()

<br />

defineClass


API

defineClass(classDeclaration, [properties,] instanceMethods, classMethods)
@param classDeclaration: 字符串,類名/父類名和Protocol
@param properties: 新增property,字符串數組,可省略
@param instanceMethods: 要添加或覆蓋的實例方法
@param classMethods: 要添加或覆蓋的類方法

覆蓋方法

在 defineClass 里面定義 OC 已存在的方法即可覆蓋, 方法名規則與調用規則一樣,使用 _ 分隔:

// OC
@implementation JPTestObject
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  // Code Here
}
@end
// JS
defineClass("JPTableViewController", { 
  tableView_didSelectRowAtIndexPath: function(tableView, indexPath) { 
    ... 
  },
})

使用雙下劃線 __ 代表原 OC 方法名里的下劃線 _:

// OC
@implementation JPTableViewController
- (NSArray *) _dataSource {
  // Code Here
}
@end
// JS
defineClass("JPTableViewController", { 
  __dataSource: function() {
      // Code Here
   },
})

在方法名前加 ORIG 即可調用未覆蓋之前的 OC 原方法:

// OC
@implementation JPTableViewController
- (void)viewDidLoad {
  // Code Here
}
@end
// JS
defineClass("JPTableViewController", { 
  viewDidLoad: function() { 
    self.ORIGviewDidLoad(); 
  },
})
覆蓋類方法

defineClass() 第三個參數就是要添加或覆蓋的類方法,規則與上述覆蓋實例方法一致:

// OC
@implementation JPTestObject
+ (void)shareInstance {
  // Code Here
}
@end
// JS
defineClass("JPTableViewController", 
{
        //實例方法
}, 
{ 
        //類方法 
      shareInstance: function() {
         ... 
      },
})
覆蓋 Category 方法

覆蓋 Category 方法與覆蓋普通方法一樣:

@implementation UIView (custom)
- (void)methodA {
  // Code Here
}

+ (void)clsMethodB {
  // Code Here
}
@end
defineClass('UIView', 
{ 
  methodA: function() { 
    // Code Here
  }
},
 { 
  clsMethodB: function() {
    // Code Here
  }
});
Super

使用 self.super() 接口代表 super 關鍵字, 調用 super 方法:

// JS
defineClass("JPTableViewController", 
{ 
  viewDidLoad: function()
  { 
      self.super().viewDidLoad();
  }
})
Property
獲取/修改 OC 定義的 Property

用調用 getter / setter 的方式獲取/修改已在 OC 定義的 Property

// OC
@interface JPTableViewController

@property (nonatomic) NSArray *data;

@end

@implementation JPTableViewController

@end
// JSdefineClass("JPTableViewController", 
{
   viewDidLoad: function() {
     var data = self.data(); //get property value 
     self.setData(data.toJS().push("JSPatch"));  //set property value 
  },
})
動態新增 Property

可以在 defineClass() 第二個參數為類新增 property,格式為字符串數組,使用時與 OC property 接口一致:

defineClass("JPTableViewController", ['data', 'totalCount'],
{ 
  init: function() 
  { 
    self = self.super().init() 
    self.setData(["a", "b"]) //添加新的 Property (id data) 
    self.setTotalCount(2) 
    return self 
  },
  viewDidLoad: function() 
  { 
    var data = self.data() //獲取 Property 值 
    var totalCount = self.totalCount() 
  },
})
私有成員變量

使用 valueForKey()setValue_forKey() 獲取/修改私有成員變量:

// OC
@implementation JPTableViewController
{ 
  NSArray *_data;
}
@end
// JS
defineClass("JPTableViewController",
 { 
  viewDidLoad: function()
   { 
    var data = self.valueForKey("_data") //get member variables 
    self.setValue_forKey(["JSPatch"], "_data") //set member variables
  },
})
添加新方法

可以給一個類隨意添加 OC 未定義的方法,但所有的參數類型都是 id :

// OC
@implementation JPTableViewController
- (void)viewDidLoad 
{ 
  NSString* data = [self dataAtIndex:@(1)]; 
  NSLog(@"%@", data); //output: Patch
}
@end
// JS
var data = ["JS", "Patch"]
defineClass("JPTableViewController", 
{ 
  dataAtIndex: function(idx) 
  { 
    return idx < data.length ? data[idx]: "" 
  }
})
Protocol

可以在定義時讓一個類實現某些 Protocol 接口,寫法跟 OC 一樣:

defineClass("JPViewController: UIViewController<UIScrollViewDelegate, UITextViewDelegate>", 
{
  // Code Here
})

這樣做的作用是,當添加 Protocol 里定義的方法,而類里沒有實現的方法時,參數類型不再全是 id ,而是自動轉為 Protocol 里定義的類型:

@protocol UIAlertViewDelegate <NSObject>
...
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
...
@end
defineClass("JPViewController: UIViewController <UIAlertViewDelegate>", 
{
   viewDidAppear: function(animated) 
  { 
    var alertView = require('UIAlertView') 
        .alloc() 
        .initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles( 
          "Alert", 
          self.dataSource().objectAtIndex(indexPath.row()), 
          self, 
          "OK", 
          null ) 
     alertView.show()
  } 

  alertView_clickedButtonAtIndex: function(alertView, buttonIndex) 
  { 
    console.log('clicked index ' + buttonIndex) 
  }
})

<br />

特殊類型


Struct

JSPatch原生支持 CGRect / CGPoint / CGSize / NSRange 這四個 struct 類型,用 JS 對象表示:

// Obj-C
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
[view setCenter:CGPointMake(10,10)];
[view sizeThatFits:CGSizeMake(100, 100)];
CGFloat x = view.frame.origin.x;
NSRange range = NSMakeRange(0, 1);
// JS
var view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100})
view.setCenter({x: 10, y: 10})
view.sizeThatFits({width: 100, height:100})
var x = view.frame().x
var range = {location: 0, length: 1}

其他 Struct 類型的支持請參照 添加 Struct 類型支持

Selector

在JS使用字符串代表 Selector:

/Obj-C
[self performSelector:@selector(viewWillAppear:) withObject:@(YES)];
//JS
self.performSelector_withObject("viewWillAppear:", 1)
nil

JS 上的 nullundefined 都代表 OC 的 nil ,如果要表示 NSNull , 用 nsnull 代替,如果要表示 NULL , 也用 null 代替:

//Obj-C
@implemention JPTestObject
+ (BOOL)testNull(NSNull *null) 
{ 
  return [null isKindOfClass:[NSNull class]];
}
@end
//JS
require('JPTestObject').testNull(nsnull) //return 1
require('JPTestObject').testNull(null) //return 0

在JS里面判斷是否為空要判斷false:

var url = "";
var rawData = NSData.dataWithContentsOfURL(NSURL.URLWithString(url));
if (rawData != null) {} //這樣判斷是錯誤的

// 應該如下判斷:
if (!rawData){}
// 在JSPatch.js源碼里_formatOCToJS方法對undefined,null,isNil轉換成了false。

<br />

NSArray / NSString / NSDictionary


NSArray / NSString / NSDictionary 不會自動轉成對應的JS類型,像普通 NSObject 一樣使用它們:

//Obj-C
@implementation JPObject
+ (NSArray *)data
{ 
  return @[[NSMutableString stringWithString:@"JS"]];
}

+ (NSMutableDictionary *)dict
{ 
  return [[NSMutableDictionary alloc] init];
}

@end
// JS
require('JPObject')var 
ocStr = JPObject.data().objectAtIndex(0)
ocStr.appendString("Patch")

var dict = JPObject.dict()
dict.setObject_forKey(ocStr, 'name')
console.log(dict.objectForKey('name')) //output: JSPatch

如果要把 NSArray / NSString / NSDictionary 轉為對應的 JS 類型,使用 .toJS() 接口:

// JS
var data = require('JPObject').data().toJS()
//data instanceof Array === true
data.push("Patch")

var dict = JPObject.dict()
dict.setObject_forKey(data.join(''), 'name')
dict = dict.toJS()
console.log(dict['name']) //output: JSPatch

<br />

Block


Block 傳遞

當要把 JS 函數作為 block 參數給 OC時,需要先使用 block(paramTypes, function) 接口包裝:

// Obj-C
@implementation JPObject
+ (void)request:(void(^)(NSString *content, BOOL success))callback
{ 
  callback(@"I'm content", YES);
}
@end
// JS
require('JPObject').request(block("NSString *, BOOL", function(ctn, succ)
{ 
  if (succ) log(ctn) //output: I'm content
}))

這里 block 里的參數類型用字符串表示,寫上這個 block 各個參數的類型,用逗號分隔。NSObject 對象如 NSString *, NSArray *等可以用 id 表示,但 block 對象要用 NSBlock* 表示。
<br />
從 OC 返回給 JS 的 block 會自動轉為 JS function,直接調用即可:

// Obj-C
@implementation JPObject

typedef void (^JSBlock)(NSDictionary *dict);

+ (JSBlock)genBlock
{ 
  NSString *ctn = @"JSPatch";
   JSBlock block = ^(NSDictionary *dict) { 
    NSLog(@"I'm %@, version: %@", ctn, dict[@"v"]) ;
  }; 
  return block;
}
+ (void)execBlock:(JSBlock)blk
{
  // Code Here
}
@end
// JS
var blk = require('JPObject').genBlock();
blk({v: "0.0.1"}); //output: I'm JSPatch, version: 0.0.1

若要把這個從 OC 傳過來的 block 再傳回給 OC,同樣需要再用 block() 包裝,因為這里 blk 已經是一個普通的 JS function,跟我們上面定義的 JS function 沒有區別:

// JS
var blk = require('JPObject').genBlock();
blk({v: "0.0.1"}); //output: I'm JSPatch, version: 0.0.1
require('JPObject').execBlock(block("id", blk));

總結:JS 沒有 block 類型的變量,OC 的 block 對象傳到 JS 會變成 JS function,所有要從 JS 傳 block 給 OC 都需要用 block() 接口包裝。

Block 里使用 Self 變量

在 block 里無法使用 self 變量,需要在進入 block 之前使用臨時變量保存它:

defineClass("JPViewController", 
{ 
  viewDidLoad: function() 
  { 
    var slf = self; 
    require("JPTestObject").callBlock(block(function() 
    { 
      //`self` is not available here, use `slf` instead. 
      slf.doSomething(); 
    }); 
  }
}
限制

從 JS 傳 block 到 OC,有兩個限制:
A. block 參數個數最多支持6個。(若需要支持更多,可以修改源碼)
B. block 參數類型不能是 double
<br />
另外不支持 JS 封裝的 block 傳到 OC 再傳回 JS 去調用 (原因: issue #155)

- (void)callBlock:(void(^)(NSString *str))block
{
  // Code Here
}
defineClass('JPTestObject', 
{ 
  run: function() 
  { 
    self.callBlock(block('NSString*', function(str) 
    { 
      console.log(str); 
    })); 
  }, 

  callBlock: function(blk) 
  { 
    //blk 這個 block 是上面的 run 函數里 JS 傳到 OC 再傳過來的,無法調用。 
    blk("test block"); 
  }
});

<br />

__weak / __strong


可以在 JS 通過 __weak() 聲明一個 weak 變量,主要用于避免循環引用。
例如我們在 OC 里為了避免 block 導致的循環引用,經常這樣寫:

- (void)test 
{ 
  __weak id weakSelf = self; 
  [self setCompleteBlock:^() { 
    [weakSelf blabla]; 
  }]
}

在 JS 對應的可以這樣寫:

var weakSelf = __weak(self)
self.setCompleteBlock(block(function() { 
  weakSelf.blabla();
}))

若要在使用 weakSelf 時把它變成 strong 變量,可以用 __strong() 接口:

var weakSelf = __weak(self)
self.setCompleteBlock(block(function() { 
  var strongSelf = __strong(weakSelf) 
  strongSelf.blabla();
}))

<br />

GCD


使用 dispatch_after() dispatch_async_main() dispatch_sync_main() dispatch_async_global_queue() 接口調用GCD方法:

// Obj-C
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
  // do something
});

dispatch_async(dispatch_get_main_queue(), ^{ 
  // do something
});
// JS
dispatch_after(1.0, function() { 
  // do something
})

dispatch_async_main(function() { 
  // do something
})

dispatch_sync_main(function() { 
  // do something
})

dispatch_async_global_queue(function() { 
  // do something
})

<br />

傳遞 id* 參數


如果你需要傳遞 id* 參數,像 NSURLConnection 里的這個接口里的 NSError ** :

+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;

這里傳入的是一個指向 NSObject 對象的指針,在方法里可以修改這個指針指向的對象,調用后外部可以拿到新指向的對象,對于這樣的參數,首先需要引入 JPMemory 擴展,然后按以下步驟進行傳遞和獲取:

  1. 使用 malloc(sizeof(id)) 創建一個指針
  2. 把指針作為參數傳給方法
  3. 方法調用完, 使用 pval() 拿到指針新指向的對象
  4. 使用完后調用 releaseTmpObj() 釋放這個對象
  5. 使用 free() 釋放指針

舉個例子:

//OC
- (void)testPointer:(NSError **)error { 
  NSError *err = [[NSError alloc]initWithDomain:@"com.jspatch" code:42 userInfo:nil];
  *error = err;
}
//JS
//malloc() pval() free() is provided by JPMemory extension
require('JPEngine').addExtensions(['JPMemory'])

var pError = malloc(sizeof("id"))
self.testPointer(pError)
var error = pval(pError)

if (!error) { 
  console.log("success")
} else { 
  console.log(error)
}

releaseTmpObj(pError)
free(pError)

若反過來你想在 JS 替換上述 -testPointer: 方法,構建 NSError 對象賦給傳進來的指針,可以這樣寫:

defineClass('JPClassName', 
{ 
  testPointer: function(error)
  { 
    var tmp = require('NSError').errorWithDomain_code_userInfo("test", 1, null); 
    var newErrorPointer = getPointer(tmp) 
    memcpy(error, newErrorPointer, sizeof('id')) 
  }
});

<br />

常亮, 枚舉, 宏定義, 全局變量


常量, 枚舉

Objective-C 里的常量/枚舉不能直接在 JS 上使用,可以直接在 JS 上用具體值代替:

//OC
[btn addTarget:self action:@selector(handleBtn) forControlEvents:UIControlEventTouchUpInside];
//UIControlEventTouchUpInside的值是1<<6
btn.addTarget_action_forControlEvents(self, "handleBtn", 1<<6);

或者在 JS 上重新定義同名的全局變量:

//js
var UIControlEventTouchUpInside = 1 << 6;
btn.addTarget_action_forControlEvents(self, "handleBtn", UIControlEventTouchUpInside);

有些常量字符串,需要在 OC 用 NSLog 打出看看它的值是什么:

//OC
[[NSAttributedString alloc].initWithString:@"str" attributes:@{NSForegroundColorAttributeName: [UIColor redColor]];

上面代碼中 NSForegroundColorAttributeName 是一個靜態字符串常量,源碼里看不出它的值,可以先用 NSLog 打出它的值再直接寫在 JS 上:

//OC
NSLog(@"%@", NSForegroundColorAttributeName) //output 'NSColor'
NSAttributedString.alloc().initWithString_attributes("無效啊", {'NSColor': UIColor.redColor()});
宏定義

Objective-C 里的宏同樣不能直接在 JS 上使用。若定義的宏是一個值,可以在 JS 定義同樣的全局變量代替,若定義的宏是程序,可以在JS展開宏:

#define TABBAR_HEIGHT 40
#define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.height
[view setWidth:SCREEN_WIDTH height:TABBAR_HEIGHT];
//JS
view.setWidth_height(UIScreen.mainScreen().bounds().height, 40);

若宏的值是某些在底層才能獲取到的值,例如 CGFLOAT_MIN ,可以通過在某個類或實例方法里將它返回,或者用添加擴展的方式提供支持:

@implementation JPMacroSupport
+ (void)main:(JSContext *)context
{ 
  context[@"CGFLOAT_MIN"] = ^CGFloat() { 
    return CGFLOAT_MIN; 
  }
}
@end
全局變量

在類里定義的 static 全局變量無法在 JS 上獲取到,若要在 JS 拿到這個變量,需要在 OC 有類方法或實例方法把它返回:

static NSString *name;
@implementation JPTestObject
+ (NSString *)name
{ 
  return name;
}
@end
var name = JPTestObject.name() //拿到全局變量值

<br />

Swift


使用 defineClass() 覆蓋 Swift 類時,類名應為 項目名.原類名 ,例如項目 demo 里用 Swift 定義了 ViewController 類,在 JS 覆蓋這個類方法時要這樣寫:

defineClass('demo.ViewController', {})

對于調用已在 swift 定義好的類,也是一樣:

require('demo.ViewController')

需要注意幾點:

  1. 只支持調用繼承自 NSObject 的 Swift 類
  2. 繼承自 NSObject 的 Swift 類,其繼承自父類的方法和屬性可以在 JS 調用,其他自定義方法和屬性同樣需要加 dynamic 關鍵字才行。
  3. 若方法的參數/屬性類型為 Swift 特有(如 Character / Tuple),則此方法和屬性無法通過 JS 調用。
  4. Swift 項目在 JSPatch 新增類與 OC 無異,可以正常使用。
    詳見這篇文章

<br />

加載動態庫


對于 iOS 內置的動態庫,若原 APP 里沒有加載,可以通過以下方式動態加載,以加載 SafariServices.framework 為例:

var bundle = NSBundle.bundleWithPath("/System/Library/Frameworks/SafariServices.framework");
bundle.load();

加載后就可以使用 SafariServices.framework 了。

<br />

調試


可以使用 console.log() 打印一個對象,作用相當于 NSLog() ,會直接在 XCode 控制臺打出。console.log() 支持任意參數,但不支持像 NSLog 這樣 NSLog(@"num:%f", 1.0) 的拼接:

var view = UIView.alloc().init();
var str = "test";
var num = 1;
console.log(view, str, num)
console.log(str + num);   //直接在JS拼接字符串

也可以通過 Safari 的調試工具對 JS 進行斷點調試,詳見 JS 斷點調試


Lemon龍說:

如果您在文章中看到了錯誤 或 誤導大家的地方, 請您幫我指出, 我會盡快更改

如果您有什么疑問或者不懂的地方, 請留言給我, 我會盡快回復您

如果您覺得本文對您有所幫助, 您的喜歡是對我最大的鼓勵

如果您有好的文章, 可以投稿給我, 讓更多的 iOS Developer 在簡書這個平臺能夠更快速的成長

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容

  • JSPatch 1.打印console.log(); 2.Protocol 3.Masonry使用JSPatch如...
    BestJoker閱讀 1,559評論 5 9
  • 6.1殷丹種子實踐 感恩: 感恩葉武濱老師講的時間管理。最近逐步的應用在生活和工作中,感覺思路清晰了很多 感恩同事...
    殷丹閱讀 199評論 0 0
  • 不要說古詩詞離我們有多遠,平時用不上,誰說的!這不,中秋佳節又至,與家人親朋團聚把盞言歡、賞月吃餅之際,不正是吟誦...
    可聞桃杏香閱讀 3,076評論 2 6
  • 今天下午去給女兒送飯看見女兒捂著肚子說不舒服(女兒生理期)我認為那動作聲音都有點過,再加上看見她外套也不穿就又些控...
    Q3陳翠玲閱讀 201評論 0 0
  • 在當下這個和平年代,娃娃們最好的挫折教育,我認為就是體育鍛煉! 今天,我要跟大家分享一件我很驕傲的事情,就是我把娃...
    笨媽先飛閱讀 1,695評論 0 0