javascript基础知识问答——变量和类型

  • 1.JavaScript规定了几种语言类型
  • 2.JavaScript对象的底层数据结构是什么
  • 3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol
  • 4.JavaScript中的变量在内存中的具体存储形式
  • 5.基本类型对应的内置对象,以及他们之间的装箱拆箱操作
  • 6.理解值类型和引用类型
  • 7.nullundefined的区别
  • 8.至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
  • 9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
  • 10.出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法

1.JavaScript规定了几种语言类型

JavaScript中的每一个值都有它自己的类型,JavaScript规定了七种语言类型:

  1. Undefined:未定义,声明了变量但是没有赋值
var m;
console.log(s);   //undefined  ,如果一个没有声明的变量,会报错
  1. Null:空
var m=null;
console.log(m);          //null
console.log(typeof(m));  //object
//咦?为什么是object?这实际上是涉及到JavaScript内部的存储机制,js中的数据在底层是以二进制存储
//如果前三位为0,那么就会判定为object,而null的所有都为0,自然也就是object类型了,记住就好了,特殊情况特殊处理。
  1. Boolean:布尔值(true/false)
var m=true;
console.log(typeof(m));    //boolean
  1. String :表示字符串类型
var test0='test';
console.log(typeof(test0));//返回  string
var test1=new String('test');
console.log(typeof(test1));//返回   object
  1. Number:表示数字
var m=1;
console.log(typeof(m));     //输出 number
  1. Symbol:(在 ECMAScript 6 中新添加的类型))一种数据类型,它的实例是唯一且不可改变的
var  s=Symbol('添加描述');
console.log(s);    //Symbol(添加描述)
console.log(typeof(s)); //symbol
let s1 = Symbol('foo'); let s2 = Symbol('foo');
s1 === s2 // false
  1. Object
var obj={};
function fun(){};
var arr=[];
var date=new Date();
var er=new Error('ssss');
console.log(typeof(obj));//object
console.log(typeof(fun));//function
console.log(typeof(fun.prototype));//object
console.log(typeof(arr));//object
console.log(typeof(date));//object
console.log(er);//Error:ssss

2. JavaScript对象的底层数据结构是什么

引擎层的 Object 类,然后 HeapObject 往下派生,每个 heap object 都有个 map 来记录相关信息。
所有js对象都是基于一个名为HeapObject的类生成的。生成对象的时候,引擎通过这个类向Heap申请空间。这个分配过程基本都是引擎自己实现,而不会调用malloc,因为要实现精确GC

3. Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol

  • 场景一:使用Symbol来作为对象属性名key
    特性:Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外。
let obj = {
   [Symbol('name')]: 'zzz',
   age: 18,
   title: 'Engineer'
}
Object.keys(obj)   // ['age', 'title']
for (let p in obj) {
   console.log(p)   // 分别会输出:'age' 和 'title'
}
Object.getOwnPropertyNames(obj)   // ['age', 'title']
JSON.stringify(obj)  // {"age":18,"title":"Engineer"}

好处:我们可以利用这一特点来更好的设计我们的数据对象,让“对内操作”和“对外选择性输出”变得更加优雅
会有一些专门针对Symbol的API获取以Symbol方式定义的对象属性

// 使用Object的API
Object.getOwnPropertySymbols(obj) // [Symbol(name)]
// 使用新增的反射API
Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']
  • 场景二:使用Symbol代替常量
/*
const TYPE_AUDIO = 'AUDIO'
const TYPE_VIDEO = 'VIDEO'
const TYPE_IMAGE = 'IMAGE'
*/
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()//保证变量的唯一性,但是这种场景意义不是很大,少了一些特定变量名称的粘贴复制吧,const声明的就是唯一值了
function handleFileResource(resource) {
  switch(resource.type) {
    case TYPE_AUDIO:
      playAudio(resource)
      break
    case TYPE_VIDEO:
      playVideo(resource)
      break
    case TYPE_IMAGE:
      previewImage(resource)
      break
    default:
      throw new Error('Unknown type of resource')
  }
}
  • 场景三:使用Symbol定义类的私有属性/方法
    在JavaScript中,是没有如Java等面向对象语言的访问控制关键字private的,类上所有定义的属性或方法都是可公开访问的。因此这对我们进行API的设计时造成了一些困扰。
    而有了Symbol以及模块化机制,类的私有属性和方法才变成可能。例如:
    在文件 a.js中:
const PASSWORD = Symbol()
class Login {
  constructor(username, password) {
    this.username = username
    this[PASSWORD] = password
  }
  checkPassword(pwd) {
      return this[PASSWORD] === pwd
  }
}
export default Login

在文件b中:

import Login from './a'
const login = new Login('admin', '123456')
login.checkPassword('123456')  // true
login.PASSWORD  // oh!no!
login[PASSWORD] // oh!no!
login["PASSWORD"] // oh!no!

由于Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),因此这个PASSWORD的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。

  • 场景四:注册和获取全局Symbol
    通常情况下,我们在一个浏览器窗口中(window),使用Symbol()函数来定义和Symbol实例就足够了。但是,如果你的应用涉及到多个window(最典型的就是页面中使用了<iframe>),并需要这些window中使用的某些Symbol是同一个,那就不能使用Symbol()函数了,因为用它在不同window中创建的Symbol实例总是唯一的,而我们需要的是在所有这些window环境下保持一个共享的Symbol。这种情况下,我们就需要使用另一个API来创建或获取Symbol,那就是Symbol.for(),它可以注册或获取一个window间全局的Symbol实例
let gs1 = Symbol.for('global_symbol_1')  //注册一个全局Symbol
let gs2 = Symbol.for('global_symbol_1')  //获取全局Symbol
gs1 === gs2  // true

4. JavaScript中的变量在内存中的具体存储形式

栈内存和堆内存

JavaScript中的变量分为基本类型和引用类型

基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问

引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用

let a1 = 0; // 栈内存
let a2 = "this is string" // 栈内存
let a3 = null; // 栈内存
let b = { x: 10 }; // 变量b存在于栈中,{ x: 10 }作为对象存在于堆中
let c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3]作为对象存在于堆中
image.png
栈内存 堆内存
存储基础数据类型 存储引用数据类型
按值访问 按引用访问
存储的值大小固定 存储的值大小不定,可动态调整
由系统自动分配内存空间 由代码进行指定分配
空间小,运行效率高 空间大,运行效率相对较低
先进后出,后进先出 无序存储,可根据引用直接获取

5. 基本类型对应的内置对象,以及他们之间的装箱拆箱操作

基本类型的内置对象有String()Number()Boolean()Symbol()
引用类型的内置对象有RegExp()Date()Array()Object()Function()Error()
装箱操作:隐式转换 值类型 =》 对象类型
拆箱操作:显式转换 对象类型 =》 值类型

装箱,就是把基本类型转变为对应的对象。装箱分为隐式和显示。
隐式装箱
每当读取一个基本类型的值时,后台会创建一个该基本类型所对应的对象。在这个基本类型上调用方法,其实是在这个基本类型对象上调用方法。这个基本类型的对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后立刻被销毁。

  num.toFixed(2); // '123.00'
//上方代码在后台的真正步骤为
    var c = new Number(123);//创建一个 Number 类型的实例。
    c.toFixed(2);//在实例上调用方法。
    c = null;//销毁实例

显式装箱
通过内置对象 Boolean、Object、String 等可以对基本类型进行显示装箱。

   var obj = new String('123');

拆箱。拆箱与装箱相反,把对象转变为基本类型的值。拆箱过程内部调用了抽象操作 ToPrimitive 。该操作接受两个参数,第一个参数是要转变的对象,第二个参数 PreferredType 是对象被期待转成的类型。第二个参数不是必须的,默认该参数为 number,即对象被期待转为数字类型。有些操作如 String(obj) 会传入 PreferredType 参数。有些操作如 obj + " " 不会传入 PreferredType
具体转换过程是这样的。默认情况下,ToPrimitive 先检查对象是否有 valueOf 方法,如果有则再检查 valueOf 方法是否有基本类型的返回值。如果没有 valueOf 方法或 valueOf 方法没有返回值,则调用 toString 方法。如果 toString 方法也没有返回值,产生 TypeError 错误。

PreferredType 影响 valueOf 与 toString 的调用顺序。如果 PreferrenType 的值为 string。则先调用 toString ,再调用 valueOf。

var obj = {
    valueOf : () => {console.log("valueOf"); return []},
    toString : () => {console.log("toString"); return []}
}

String(obj)
// toString
// valueOf
// Uncaught TypeError: Cannot convert object to primitive value

obj+' '
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value

Number(obj)
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value

6. 理解值类型和引用类型

基本类型的内置对象有String()Number()Boolean()Symbol()
引用类型的内置对象有RegExp()Date()Array()Object()Function()Error()

  • 值类型
    1、占用空间固定,保存在栈中(当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;栈中存储的是基础变量以及一些对象的引用变量,基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或者对象的地址,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量。)
    2、保存与复制的是值本身
    3、使用typeof检测数据的类型
    4、基本类型数据是值类型

  • 引用类型
    1、占用空间不固定,保存在堆中(当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。)
    2、保存与复制的是指向对象的一个指针
    3、使用instanceof检测数据类型
    4、使用new()方法构造出的对象是引用型

7. null和undefined的区别

null == undefined //  true
null === undefined // false
!!null // false
!!undefined // false
Number(null) //0
Number(undefined) //NaN
/*null更多表示引用语义, 它表示一个值被定义了,定义为“空值”。
undefined更多表示值语义,它表示根本不存在定义。
所以设置一个值为null是合理的,但是设置一个值为undefined就不合理。*/
typeof null // object
typeof undefined // undefined

8. 至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型

判断数据类型的方法一般可以通过:typeof、instanceof、constructor、Object.prototype.toString.call();四种常用方法

  1. typeof
    typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function、Symbol6种数据类型。

对于引用类型,返回的都是object,其实返回object也没有错,因为所有对象的原型链最终都指向了Object,Object是所有对象的祖宗。 但当我们需要知道某个对象的具体类型时,typeof 就显得有些力不从心了。

注意:typeof null也是返回object

  1. instanceof
    判断对象和构造函数在原型链上是否有关系,如果有关系,返回真,否则返回假.
function Aaa(){
}
var a1 = new Aaa();
console.log( a1 instanceof Aaa);  
//true 判断a1和Aaa是否在同一个原型链上,是的话返回真,否则返回假
var arr = [];
console.log( arr instanceof Aaa);//false
 var str = 'hello';
    alert(str instanceof String);//false
    var bool = true;
    alert(bool instanceof Boolean);//false
    var num = 123;
    alert(num instanceof Number);//false
    var nul = null;
    alert(nul instanceof Object);//false
    var und = undefined;
    alert(und instanceof Object);//false
    var oDate = new Date();
    alert(oDate instanceof Date);//true
    var json = {};
    alert(json instanceof Object);//true
    var arr = [];
    alert(arr instanceof Array);//true
    var reg = /a/;
    alert(reg instanceof RegExp);//true
    var fun = function(){};
    alert(fun instanceof Function);//true
    var error = new Error();
    alert(error instanceof Error);//true

从上面的运行结果我们可以看到,基本数据类型是没有检测出他们的类型,但是我们使用下面的方式创建num、str、boolean,是可以检测出类型的:

var num = new Number(123);
var str = new String('abcdef');
var boolean = new Boolean(true);
console.log(num instanceof Number)//true
console.log(str instanceof String)//true
  1. constructor:查看对象对应的构造函数
    constructor 在其对应对象的原型下面,是自动生成的。当我们写一个构造函数的时候,程序会自动添加:构造函数名.prototype.constructor = 构造函数名
function Aaa(){
}
//Aaa.prototype.constructor = Aaa;   //每一个函数都会有的,都是自动生成的

判断数据类型的方法

  var str = 'hello';
    alert(str.constructor == String);//true
    var bool = true;
    alert(bool.constructor == Boolean);//true
    var num = 123;
    alert(num.constructor ==Number);//true
   // var nul = null;
   // alert(nul.constructor == Object);//报错
    //var und = undefined;
    //alert(und.constructor == Object);//报错
    var oDate = new Date();
    alert(oDate.constructor == Date);//true
    var json = {};
    alert(json.constructor == Object);//true
    var arr = [];
    alert(arr.constructor == Array);//true
    var reg = /a/;
    alert(reg.constructor == RegExp);//true
    var fun = function(){};
    alert(fun.constructor ==Function);//true
    var error = new Error();
    alert(error.constructor == Error);//true

从上面的测试中我们可以看到,undefined和null是不能够判断出类型的,并且会报错。因为null和undefined是无效的对象,因此是不会有constructor存在的
同时我们也需要注意到的是:使用constructor是不保险的,因为constructor属性是可以被修改的,会导致检测出的结果不正确
备注:使用Object.create()创建的js对象,没有constructor

  1. Object.prototype.toString(可以说不管是什么类型,它都可以立即判断出)
    toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型

格式为[object xxx],xxx是具体的数据类型,其中包括:

String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。

    var str = 'hello';
    console.log(Object.prototype.toString.call(str));//[object String]
    var bool = true;
    console.log(Object.prototype.toString.call(bool))//[object Boolean]
    var num = 123;
    console.log(Object.prototype.toString.call(num));//[object Number]
    var nul = null;
    console.log(Object.prototype.toString.call(nul));//[object Null]
    var und = undefined;
    console.log(Object.prototype.toString.call(und));//[object Undefined]
    var oDate = new Date();
    console.log(Object.prototype.toString.call(oDate));//[object Date]
    var json = {};
    console.log(Object.prototype.toString.call(json));//[object Object]
    var arr = [];
    console.log(Object.prototype.toString.call(arr));//[object Array]
    var reg = /a/;
    console.log(Object.prototype.toString.call(reg));//[object RegExp]
    var fun = function(){};
    console.log(Object.prototype.toString.call(fun));//[object Function]
    var error = new Error();
    console.log(Object.prototype.toString.call(error));//[object Error]
类型 typeof instanceof constructor Object.prototype.toString.call
优点 使用简单 能检测出引用类型 基本能检测所有的类型(除了null和undefined) 检测出所有类型
缺点 只能检测出基本类型(出null) 能检测出基本类型,且不能跨iframe constructor易被修改,也不能跨iframe IE6下,undefined和null均为Object

例:跨页面判断是否是数组

window.onload = function(){
    var oF = document.createElement('iframe');
    document.body.appendChild( oF );
    var ifArray = window.frames[0].Array;
    var arr = new ifArray();
    //alert( arr.constructor == Array );  //false
    //alert( arr instanceof Array );  //false
    alert( Object.prototype.toString.call(arr) == '[object Array]' );  //true  
};

9. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用

js数据类型隐式转换主要分为三种情况
1.转换为string
2.转换为number
3.转换为boolean

原始值 转换目标 结果
number 布尔值 除了0,-0,NaN都为true
string 布尔值 除了空字符串都为true
defined、null 布尔值 false
引用类型 布尔值 false
number 字符串 5=>'5'
Boolean、函数、Symbol 字符串 'true'
数组 字符串 [1,2]=>'1,2'
对象 字符串 '[object object]'
string 数字 '1'=>1,'a'=>NaN
数组 数字 空数组为0,存在一个元素且为数字转数字,其他情况NaN
null 数字 0
除了数组的引用类型 数字 NaN
Symbol 数字 抛错

隐式类型转换。Javascript默认自动转换,没有任何警告,隐式类型转换常见场景

  • 自动转换Boolean
    例如if语句或者其他需要Boolean的地方
if (表达式){}
  • 在非 Numeber 类型进行数学运算符 - * / 时,会先将非 Number 转换成 Number 类型。 +运算符要考虑字符串的情况,在操作数中存在字符串时,优先转换成字符串
    +运算符其中一个操作数是字符串的话,会进行连接字符串的操作
1+'2' // '12'

+操作符的执行顺序是:
当一侧操作数为 String 类型,会优先将另一侧转换为字符串类型。
当一侧操作数为 Number 类型,另一侧为原始类型,则将原始类型转换为 Number 类型。
当一侧操作数为 Number 类型,另一侧为引用类型,将引用类型和 Number 类型转换成字符串后拼接。

  • 对象。只有在 JavaScript 表达式或语句中需要用到数字或字符串时,对象才被隐式转换。
    当需要将对象转换为数字时,需要三个步骤
3*{valueOf:function () { return 5 }} // 15,调用 valueOf()。如果结果是原始值(不是一个对象),则将其转换为一个数字。
3*{toString:function () { return 5 }} // 15,否则,调用 toString() 方法。如果结果是原始值,则将其转换为一个数字
3*{toString:function () { return {} }} //TypeError: Cannot convert object to primitive value
//否则,抛出一个类型错误。

10. 出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法

   var x  = 0.3 - 0.2; //30美分减去20美分
    var y =  0.2 - 0.1; //20美分减去10美分
    x == y;             // =>false,两值不相等
    x == 0.1;           // =>false,真实值为:0.09999999999999998
    y == 0.1;           // =>true

这个问题并不只是在Javascript中才会出现,任何使用二进制浮点数的编程语言都会有这个问题,只不过在 C++/C#/Java 这些语言中已经封装好了方法来避免精度的问题,而 JavaScript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型,所以精度误差的问题就显得格外突出
解决办法

//加法
Number.prototype.add = function(arg) {
  var r1, r2, m;
  try {
    r1 = this.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = arg.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2));
  return (this * m + arg * m) / m;
};
//减法
Number.prototype.sub = function(arg) {
  return this.add(-arg);
};

//乘法
Number.prototype.mul = function(arg) {
  var m = 0,
    s1 = this.toString(),
    s2 = arg.toString();
  try {
    m += s1.split(".")[1].length;
  } catch (e) {}
  try {
    m += s2.split(".")[1].length;
  } catch (e) {}
  return (
    (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
    Math.pow(10, m)
  );
};

//除法
Number.prototype.div = function(arg) {
  var t1 = 0,
    t2 = 0,
    r1,
    r2;
  try {
    t1 = this.toString().split(".")[1].length;
  } catch (e) {}
  try {
    t2 = arg.toString().split(".")[1].length;
  } catch (e) {}
  with (Math) {
    r1 = Number(this.toString().replace(".", ""));
    r2 = Number(arg.toString().replace(".", ""));
    return (r1 / r2) * pow(10, t2 - t1);
  }
};

参考链接:https://blog.csdn.net/Donspeng/java/article/details/76019815
https://www.jianshu.com/p/f40a77bbd74e
https://www.jianshu.com/p/80bb5a01857a
https://blog.csdn.net/xieamy/java/article/details/89892451
https://www.jianshu.com/p/d66cf6f711a1
https://blog.csdn.net/weixin_43618932/java/article/details/103109718
https://juejin.im/post/5cda9178f265da0379419ad1

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