每日一题 (二)

本博客转自:「作者:若愚链接:https://zhuanlan.zhihu.com/p/22361337来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1、什么是异步?

什么样的代码是异步代码?
我们先不深入异步概念,先从「表象」来看看怎么样的代码是异步代码:
书写顺序与执行顺序不同的代码,是异步代码。(只是从表象上来说,这并不是异步的定义

console.log(1)
setTimeout(function(){
  console.log(2)
},0)
console.log(3)
Paste_Image.png

上面代码的书写顺序是 1 -> 2 -> 3;
但是执行顺序是 1 -> 3 -> 2。
中间的 console.log(2) 就是异步执行的。
你现在知道了「代码的书写顺序和执行顺序居然可以不同!」

什么是异步?
同步:一定要等任务执行完了,得到结果,才执行下一个任务。

var taskSync = function(){
 return '同步任务的返回值'
}
var result = taskSync() // 那么 result 就是同步任务的结果
otherTask() // 然后执行下一个任务

异步:不等任务执行完,直接执行下一个任务。

var taskAsync = function(){
  var result = setTimeout(function(){
  console.log('异步任务的结果')
 }, 3000)
  return result
}
var result = taskAsync() // result 不是异步任务的结果,而是一个 timer id
otherTask() // 立即执行其他任务,不等异步任务结束

Paste_Image.png

聪明的你可能会发现,我们拿到的 result 不是异步执行的结果,而是一个 timer id,那么要怎么拿到异步任务的结果呢?

用回调。

改下代码如下:

Paste_Image.png

所以「回调」经常用于获取「异步任务」的结果。

什么情况下需要用到异步?
现在有三个函数,taskA()、taskB() 和 taskC(),三个任务互不影响。taskA 和 taskC 执行得很快,但是 taskB 执行需要 10 秒钟。

// 同步的写法
function taskB(){
 var response = $.ajax({
  url:"/data.json",
  async: false // 注意这里 async 为 false,表示是同步
 })
 return response // 十秒钟后,返回 response
}
taskA()
taskB()
taskC()

taskC 一定要等 taskB 执行完了才能执行,这就是同步。
执行顺序为:

A -> B -> AJAX 请求 -> C ---------------------------

现在换成异步:

// 异步的写法
function taskB(){
 var result = $.ajax({

  url:"/data.json",

async: true // 异步
  })
  return result // 一定要注意,现在的 result 不是上面的 response
}
taskA()
taskB()
taskC()

这样写之后,执行顺序就是

A -> B -> C ---------------------------------------
    -> AJAX 请求 --------------------------------

就是说 AJAX 请求和任务C 同时执行。但是请注意执行的主体。AJAX 请求是由浏览器的网络请求模块执行的,taskC 是由 JS 引擎执行的。

综上,如果几个任务互相独立,其中一个执行时间较长,那么一般就用异步地方式做这件事。

JS 引擎不能同时做两件事

有些人说异步是同时做两件事,但其实 JS 引擎不会这样。以 setTimeout 为例,setTimeout 里面的代码一定会在当前环境中的任务执行完了「之后」才执行。异步意味着不等待任务结束,并没有强制要求两个任务「同时」进行。但是 AJAX 请求是可以与 JS 代码同时进行的,因为这个请求不是由 JS 引擎负责,而是由浏览器网络模块负责。

以上,就是异步的简介。

2、Callback(回调)是什么?

Callback 是什么?
 callback 是一种特殊的函数,这个函数被作为参数传给另一个参数去调用。这样的函数就是回调函数。
 callback 拆开,就是 call back,在英语里面就是「回拨电话」的意思。那我们就用打电话为例子来说明一下 callback:

  • 1、「我打电话给某某」(I call somebody),那么「打电话」的人就是「我」。
  • 2、「我」在电话里说:你办完某事后,回拨电话给「我」。
  • 3、某某做完事后,就会「回拨电话给我」(calls back to me),那么「打电话」的人就是「某某」。

用编程来解释的话,是这样的:

  • 1、「我调用一个函数 f」(I call a function),那么「调用函数」的人是「我」。代码是 f(c)。
  • 2、「我」让这个函数 f 在执行完后,调用我传给它的另一个函数 c。
  • 3、f 执行完的时候,就会「调用 c」,也叫做「回调 c」(call c back),调用 c 的人是 f。

好了,解释完了:callback 就是(传给另一个函数调用的)函数。把括号里面的内容去掉,简化成:callback 就是一种函数。

Callback 很常见

  $button.on('click', function(){})

click 后面的 function 就是一个回调,因为「我」没有调用过这个函数,是 jQuery 在用户点击 button 时调用的。

  div.addEventListener('click', function(){})

click 后面的 function 也是一个回调,因为「我」没有调用过这个函数,是浏览器在用户点击 button 时调用的。
一般来说,只要参数是一个函数,那么这个函数就是回调。

Callback 有点反直觉
  很多初学者不明白 callback 的用法,因为 callback 有一点「反直觉」。比如说我们用代码做一件事情,分为两步:step1( ) 和 step2( )。符合人类直觉的代码是:

step1()
step2()

callback 的写法却是这样的:

step1(step2)

为什么要这样写?或者说在什么情况下应该用这个「反直觉」的写法?
一般(注意我说了一般),在 step1 是一个异步任务的时候,就会使用 callback。

3、什么是 HTML 5?

其实这个题目的意图是想知道你「会不会搜索」。人人都在说 HTML 5,你却不知道 HTML 5 是什么。为什么会这样?因为你不知道哪里的前端知识是靠谱的,你只能听别人说。
  今天介绍一个靠谱的前端知识来源——MDN(Mozilla Developer Network)。
谷歌搜索「HTML 5 MDN」即可搜到权威介绍

HTML 5 概览

HTML 5 是新版 Web 技术的集合,包含以下八个部分:

  1. 语义升级
  • HTML 5 加了很多新的标签,使 HTML 更富有语义。
  • 升级了 iframe 标签,使其更安全。
  • 新增 MathML,是数学公式可以在 Web 中展现。
  1. 服务器增强新增 Web Sockets
  • 新增 EventSource API
  • 新增 WebRTC
  1. 离线储存
  • 新增 AppCache
  • online 与 offline 事件
  • localStorage 和 sessionStorage
  • IndexedDB
  • File API
  1. 多媒体
  • Web 原生支持音视频播放
  • Camera API 可控制摄像头
  1. 图像绘制
  • Canvas 可绘制图像和文本
  • WebGL 可渲染 3D 影像
  • SVG 可制作矢量图形
  1. 更多集成
  • Web Workers 能够把 JavaScript 计算委托给后台线程
  • XMLHttpRequest 升级
  • History API 允许对浏览器历史记录进行操作
  • 新增 conentEditable 属性
  • 拖放 API、全屏 API、指针锁定 API
  • 可以使用 navigator.registerProtocolHandler() 方法把 web 应用程序注册成一个协议处理程序。
  • requestAnimationFrame 允许控制动画渲染以获得更优性能。
  1. 设备相关 API
  • 你现在可以用 JS 来处理摄像头、触控屏幕、地理位置等设备相关功能了。
  1. 样式
  • CSS 全面升级。

H5 是什么?

  • H5 就是微信里面长得像 PPT、可以一页一页滑动的网页。
    以上,就是 MDN 用法的介绍——获取靠谱知识。
4、你是如何做性能优化的?

为什么要做性能优化?
有些人看到这个题目,一上来就说「减少请求,添加缓存」之类的。不是说你错了,而是说你回答问题的时候没有思路。

首先你要明白一点:做任何事情都是有「目的」的。

吃饭喝水是为了生存,那么做性能优化的「目的」是什么?

想过这个问题么?如果没想过,今后就要刻意问问自己了。

优化的目的可以是:

1、 增强用户体验。但是这样说很虚,具体来说可以是:
 1.1. 加快页面展示速度(慢)
 1.2. 加快页面运行速度(卡)
 
2、节约服务器带宽流量

3、减少服务器压力

什么时候做性能优化?
你有目的了,不代表你马上就要去采取行动。

首先,你应该完成了网页的基本功能后再优化。如果你在前期就花时间优化,那么后期有可能没时间做其他功能。

其次,在没有找到性能瓶颈之前,不要优化!

一个网页的性能到底跟哪几方面有关?你优化的地方属于哪一方面?这是需要首先搞清楚的。

一个网页的大概流程包括:

  • 1、DNS 查询
  • 2、发送请求
  • 3、等待服务器响应
  • 4、下载服务器响应内容
  • 5、解析 HTML、CSS、JS 等
  • 6、渲染 HTML、CSS、JS 和图片等
  • 7、响应用户的点击事件等

如果你的性能瓶颈在「等待服务器响应」这一步,那么你怎么优化 JS、CSS 都没用。
所以再说一遍:在没有找到性能瓶颈之前,不要优化!
怎么优化?
等你找到了瓶颈所在,就可以「对症下药」了。

  • 1、DNS 查询——减少网页所用的域名个数,可可以减少 DNS 查询的时间
  • 2、发送请求——添加缓存、合并文件,都可以减少请求数量
  • 3、等待服务器响应——这一步的优化只能是在 MySQL 和后台方面做考虑了
  • 4、下载服务器响应内容——添加 Etag、Expires 响应头,得到 304 响应,可以降低下载量
  • 5、解析 HTML、CSS、JS 等——去掉无用的 HTML、CSS 和 JS 即可减少解析时间
  • 6、渲染 HTML、CSS、JS 和图片等——避免使用低效的 HTML、CSS 和 JS 即可
  • 7、响应用户的点击事件等——尽量不在前端做复杂的运算等……

整体思路

  • 1、为什么要做?
  • 2、什么时候做?
  • 3、怎么做?
5、Promise 是什么?

window.Promise 已经是 JS 的一个内置对象了。

  1. Promise 有规格文档吗?
  2. 你一般如何使用 Promise。

目前的 Promise 都遵循 Promises/A+ 规范。
英文规范:https://promisesaplus.com/**
中文翻译:图灵社区 : 阅读 : 【翻译】Promises/A+规范
看完规范你可以了解 Promise 的全貌,本文主要讲讲 Promise 的用途。

Promise 之前的时代——回调时代

假设我们用 getUser 来说去用户数据,它接收两个回调 sucessCallback 和 errorCallback:

  function getUser(successCallback, errorCallback){ 
    $.ajax({ 
      url:'/user',
      success: function(response){
         successCallback(response) 
      },
      error: function(xhr){ 
        errorCallback(xhr) 
      }
    })
   }

看起来还不算复杂。

如果我们获取用户数据之后还要获取分组数组、分组详情等,代码就会是这样:

  getUser(function(response){
    getGroup(response.id, function(group){
      getDetails(groupd.id, function(details){
        console.log(details)
      },function(){
        alert('获取分组详情失败')
      })
    }, function(){
      alert('获取分组失败')
    })
    }, function(){
      alert('获取用户信息失败')
  })

三层回调,如果再多一点嵌套,就是「回调地狱」了。

Promise 来了

Promise 的思路呢,就是 getUser 返回一个对象,你往这个对象上挂回调:

var promise = getUser()
promise.then(successCallback, errorCallback)

当用户信息加载完毕,successCallback 和 errorCallback 之一就会被执行。
把上面两句话合并成一句就是这样的:

getUser().then(successCallback, errorCallback)

如果你想在用户信息获取结束后做更多事,可以继续 .then:

getUser().then(success1).then(success2).then(success3)

请求成功后,会依次执行 success1、success2 和 success3。
如果要获取分组信息:

 getUser().then(function(response){
  getGroup(response.id).then(function(group){
    getDetails(group.id).then(function(){ 

    },error3)
  },error2)
 }, error1)

这种 Promise 写法跟前面的回调看起来其实变化不大。真的,Promise 并不能消灭回调地狱,但是它可以使回调变得可控。你对比下面两个写法就知道了。

getGroup(response.id, success2, error2)
getGroup(response.id).then(success2, error2)

用 Promise 之前,你不能确定 success2 是第几个参数;
用 Promise 之后,所有的回调都是

.then(success, error) 

这样的形式。
以上是 Promise 的简介,想完整了解 Promise,请参考下面的自学链接。
Promise对象 -- JavaScript 标准参考教程(alpha)

6、Babel 是什么?

Babel 作为一个工具,其实只要跟着它官网文档过一遍就知道它怎么用了。
一句话,Babel 能把你写的 JS 变成其他版本的 JS。
这样一来,你就可以写 IE 不支持的 JS 语法了,因为最终会被翻译成 IE 支持的语法。
比如你写 ES6

// src/index.js
[1,2,3].map(n => n + 1);

Babel 可以把它翻译成 ES5

// lib/index.js
[1,2,3].map(function(n) {
  return n + 1;
});

如何安装
进入你的项目目录,用这句话安装 Babel:

npm install --save-dev babel-cli babel-preset-latest

然后新建一个文件,命名为 .babelrc,文件内容如下:

{
  "presets": ["es2015"]
}

然后在 package.json 里面添加一个 script:

"scripts": {
"build": "babel src -d lib"
},

然后运行命令

  npm run build

那么 src/index.js 就会被翻译成 lib/index.js。

如何实时翻译
怎么能做到我每次改 src/index.js ,lib/index.js 就自动变化呢?
只需要在上面的 script 里面加一个 --watch 选项即可:

这是 package.json 文件
{
  ...
  "scripts": {
    "build": "babel --watch src -d lib"
  },
  ...
}
7、什么是响应式页面?

前几年火的一个概念:响应式页面。

  1. 什么样的页面是响应式页面?
  2. 响应式页面用到哪些技术?
  3. 响应式页面和自适应页面有什么区别?

什么是响应式页面?
首先你要理解什么是「响应」。

  • 悟空拿着宝瓶,对金角大王叫了「金角大王」,金角大王应了一声。这就是「响应」。
  • 武昌起义成功之后,各地纷纷也开始革命。这也是「响应」。

「响应」就是「你动,我也动」。
「响应式页面」就是「随着设备属性(如宽高)的变化,网页也随着变化。」


Paste_Image.png

如上图,左边是 PC 上页面的样子,右边是手机上页面的样子。

响应式页面用到哪些技术?

  • 多使用 max-width、min-width,不写死宽度
  • 使用 media 查询来响应不同分辨率
  • 使用动态 REM 方案保证手机端的显示效果

响应式页面和自适应页面的区别

自适应页面(流体布局、fluid layout)指的是页面宽度不固定。跟响应式页面没有什么关系。
自适应页面强调「不写死宽度」;响应式页面强调「响应」。
自适应页面可以是响应式的,也可以不是响应式的。
响应式页面可以是自适应的,也可以是不自适应的(也就是定宽的)。

8、简述浏览器缓存是如何控制的

1. 情形1,无缓存
浏览器向服务器请求资源 a.jpg,服务器找到对应资源把内容返回给浏览器。当浏览器再次向服务器请求资源a.jpg时,服务器重新发送完整的数据文件给浏览器。

  • 优点:简单,啥都不用做
  • 缺点:每次请求都查找并返回原始文件,浪费带宽

2. 情形2,有缓存无更新
浏览器第一次请求a.jpg 时服务器会发送完整的文件,浏览器可以把这个文件存到本地(缓存),下次再需要这个文件时直接从本地获取就行了,这样就能省下带宽了。

  • 优点: 省带宽
  • 缺点: 如果服务器上a.jpg的文件内容变了,浏览器每次都从缓存读取无法获取最新文件

3. 情形3, 缓存+更新机制
浏览器第一次请求a.jpg 时服务器会发送完整的文件,服务器在发送文件的时候还附带发送一些额外信息——过期时间,如 Expires: Mon,10 Dec 1990 02:25:22GMT。浏览器可以把这个文件和额外信息存到本地。当再次需要a.jpg的时候浏览器用当前浏览器时间和Expires做个比较,如果当前时间在过期时间以内,就直接使用缓存文件(状态为304);如果在过期时间以外就重新向服务器发送请求要资源(200)。 服务器在每次给资源的时候都会发送新的过期时间

  • 优点:缓存可控制
  • 缺点:控制的功能太单一;这种格式的时间很容易写错

4. 情形4, 缓存+更新机制升级版
比如:浏览器第一次请求a.jpg 时,服务器会发送完整的文件并附带额外信息

Cach-Control: max-age=300;

浏览器把文件和附带信息保存起来。当再次需要a.jpg 时,如果是在300秒以内发起的请求则直接使用缓存(304),否则重新发起网络请求(200)。下面是Cache-Control常见的几个值:

  • Public表示响应可被任何中间节点缓存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的proxy可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要。
  • Private表示中间节点不允许缓存,对于Browser <-- proxy1 <-- proxy2 <-- Server,proxy 会老老实实把Server 返回的数据发送给proxy1,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据。
  • no-cache表示不使用 Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存
  • no-store ,真正的不缓存任何东西。浏览器会直接向服务器请求原始文件,并且请求中不附带 Etag 参数(服务器认为是新请求)。
  • max-age,表示当前资源的有效时间,单位为秒。

优点:缓存控制功能更强大

缺点:假如浏览器再次请求资源a.jpg的时间间隔超过了max-age,这时候向服务器发送请求服务器应该会重新返回a.jpg的完整文件。但如果 a.jpg 在服务器上未做任何修改,发送a.jpg的完整文件就太浪费带宽了,其实只要发送一个「a.jpg未被更改」的短消息标示就好了。

5. 情形5, 缓存+更新机制终极版
比如:浏览器第一次请求a.jpg 时,服务器会发送完整的文件并附带额外信息,其中Etag 是 对a.jpg文件的编码,如果a.jpg在服务端未被修改,这个值就不会变

Cache-Control: max-age=300;
ETag:W/"e-cbxLFQW5zapn79tQwb/g6Q"

浏览器把a.jpg和额外信息保存到本地。假如浏览器在300秒以内再次需要获取a.jpg时,浏览器直接从缓存读取a.jpg(304)。假如浏览器在300秒之后再次需要获取a.jpg时,浏览器发现该缓存的文件已经不新鲜了,于是就向服务器发送请求 重新获取a.jpg, 在发送请求的时候附带刚刚保存的a.jpg的ETag ( If-None-Match:W/"e-cbxLFQW5zapn79tQwb/g6Q")。 服务器在接收到请求后拿浏览器请求的 Etag 和当前文件重新计算后端 Etag 做个比较,如果二者相等表示文件在未修改则发送个短消息(响应头,不包含图片内容),如果二者不等则发送新文件和新的 ETag,浏览器获取新文件并更新该文件的 Etag。

与 ETag 类似功能的是Last-Modified/If-Modified-Since。当资源过期时(max-age超时),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(200);若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 ,告知浏览器继续使用所保存的cache。

9、什么是JS原型链?

我们知道 JS 有对象,比如:

var obj = { name: 'obj' }

我们可以对 obj 进行一些操作,包括

  • 「读」属性
  • 「新增」属性
  • 「更新」属性
  • 「删除」属性

下面我们主要来看一下「读」和「新增」属性。
为什么有 valueOf / toString 属性呢?
在我们没有对 obj 进行任何其他操作之前,发现 obj 已经有几个属性(方法)了:

Paste_Image.png

那么问题来了:valueOf / toString / constructor 是怎么来?我们并没有给 obj.valueOf 赋值呀。
要搞清楚 valueOf / toString / constructor 是怎么来的,就要用到 console.dir 了。

Paste_Image.png

上面这个图有点难懂,我手画一个示意图:

Paste_Image.png

我们发现 console.dir(obj) 打出来的结果是:

  1. obj 本身有一个属性 name(这是我们给它加的)

  2. obj 还有一个属性叫做 proto(它是一个对象)

  3. obj.proto 有很多属性,包括 valueOf、toString、constructor 等

  4. obj.proto 其实也有一个叫做 proto 的属性(console.log 没有显示),值为 null

现在回到我们的问题:obj 为什么会拥有 valueOf / toString / constructor 这几个属性?

答案:
这跟 proto 有关。当我们「读取」 obj.toString 时,JS 引擎会做下面的事情:

  1. 看看 obj 对象本身有没有 toString 属性。没有就走到下一步。
  2. 看看 obj.proto 对象有没有 toString 属性,发现 obj.proto 有 toString 属性,于是找到了
    所以 obj.toString 实际上就是第 2 步中找到的 obj.proto.toString。
    可以想象,
  3. 如果 obj.proto 没有,那么浏览器会继续查看 obj.proto.proto
  4. 如果 obj.proto.proto 也没有,那么浏览器会继续查看 obj.proto.proto.proto__
  5. 直到找到 toString 或者 proto 为 null。

上面的过程,就是「读」属性的「搜索过程」。而这个「搜索过程」,是连着由 proto 组成的链子一直走的。
这个链子,就叫做「原型链」。

共享原型链
现在我们有另一个对象

var obj2 = { name: 'obj2' }

Paste_Image.png

那么 obj.toString 和 obj2.toString 其实是同一个东西,也就是 obj2.proto.toString。

这有什么意义呢?

如果我们改写 obj2.proto.toString,那么 obj.toString 其实也会变!

这样 obj 和 obj2 就是具有某些相同行为的对象,这就是意义所在。

差异化
如果我们想让 obj.toString 和 obj2.toString 的行为不同怎么做呢?直接赋值就好了:

obj.toString = function(){ return '新的 toString 方法' }

Paste_Image.png

总结:
「读」属性时会沿着原型链搜索
「新增」属性时不会去看原型链。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容