深拷貝實(shí)踐

前言:js如何實(shí)現(xiàn)一個(gè)深拷貝

這是一個(gè)老生常談的問題理张,也是在求職過程中的高頻面試題剩彬,考察的知識點(diǎn)十分豐富浮庐,本文將對淺拷貝和深拷貝的區(qū)別唁奢、實(shí)現(xiàn)等做一個(gè)由淺入深的梳理

賦值霎挟、淺拷貝與深拷貝的區(qū)別

在js中,變量類型分為基本類型和引用類型麻掸。對變量直接進(jìn)行賦值拷貝:

  • 對于基本類型酥夭,拷貝的是存儲在中的值
  • 對于引用類型,拷貝的是存儲在棧中的指針脊奋,指向中該引用類型數(shù)據(jù)的真實(shí)地址

直接拷貝引用類型變量熬北,只是復(fù)制了變量的指針地址,二者指向的是同一個(gè)引用類型數(shù)據(jù)诚隙,對其中一個(gè)執(zhí)行操作都會引起另一個(gè)的改變讶隐。

關(guān)于淺拷貝和深拷貝:

  • 淺拷貝是對于原數(shù)據(jù)的精確拷貝,如果子數(shù)據(jù)為基本類型久又,則拷貝值巫延;如果為引用類型,則拷貝地址地消,二者共享內(nèi)存空間炉峰,對其中一個(gè)修改也會影響另一個(gè)
  • 深拷貝則是開辟新的內(nèi)存空間,對原數(shù)據(jù)的完全復(fù)制

因此脉执,淺拷貝與深拷貝根本上的區(qū)別是 是否共享內(nèi)存空間 疼阔。簡單來講,深拷貝就是對原數(shù)據(jù)遞歸進(jìn)行淺拷貝。

三者的簡單比較如下:

是否指向原數(shù)據(jù) 子數(shù)據(jù)為基本類型 子數(shù)據(jù)包含引用類型
賦值 改變時(shí)原數(shù)據(jù)改變 改變時(shí)原數(shù)據(jù)改變
淺拷貝 改變時(shí)原數(shù)據(jù) 不改變 改變時(shí)原數(shù)據(jù)改變
深拷貝 改變時(shí)原數(shù)據(jù) 不改變 改變時(shí)原數(shù)據(jù) 不改變

原生淺拷貝方法

數(shù)組和對象中常見的淺拷貝方法有以下幾種:

  • Array.prototype.slice
  • Array.prototype.concat
  • Array.from<br />
  • Object.assign
  • ES6解構(gòu)

使用下面的 用例 1.test.js 進(jìn)行測試:

const arr = ['test', { foo: 'test' }]
const obj = {
  str: 'test',
  obj: {
    foo: 'test'
  }
}

const arr1 = arr.slice()
const arr2 = arr.concat()
const arr3 = Array.from(arr)
const arr4 = [...arr]

const obj1 = Object.assign({}, obj)
const obj2 = {...obj}

//修改arr
arr[0] = 'test1'
arr[1].foo = 'test1'

// 修改obj
obj.str = 'test1'
obj.obj.foo = 'test1'

結(jié)果如下:

image.png

可以看到經(jīng)過淺拷貝以后婆廊,我們?nèi)バ薷脑瓕ο蠡驍?shù)組中的基本類型數(shù)據(jù)迅细,拷貝后的相應(yīng)數(shù)據(jù)未發(fā)生改變;而修改原對象或數(shù)組中的引用類型數(shù)據(jù)否彩,拷貝后的數(shù)據(jù)會發(fā)生相應(yīng)變化疯攒,它們共享同一內(nèi)存空間

深拷貝實(shí)現(xiàn)

這里我們列舉常見的深拷貝方法并嘗試自己手動(dòng)實(shí)現(xiàn),最后對它們做一個(gè)總結(jié)列荔、比較

1. JSON序列化快速實(shí)現(xiàn)

使用 JSON.parse(JSON.stringify(data)) 來實(shí)現(xiàn)深拷貝敬尺,這種方法基本可以涵蓋90%的使用場景,但它也有其不足之處贴浙,涉及到下面這幾種情況下時(shí)則需要考慮使用其他方法來實(shí)現(xiàn)深拷貝:

  • JSON.parse 只能序列化能夠被處理為JSON格式的數(shù)據(jù)砂吞,因此無法處理以下數(shù)據(jù)
    • 特殊數(shù)據(jù)例如 undefinedNaN 崎溃、 Infinity
    • 特殊對象如時(shí)間對象蜻直、正則表達(dá)式、函數(shù)袁串、Set概而、Map等
    • 對于循環(huán)引用(例如環(huán))等無法處理,會直接報(bào)錯(cuò)
  • JSON.parse 只能序列化對象可枚舉的自身屬性囱修,因此會丟棄構(gòu)造函數(shù)的 constructor

使用下面的 用例 2.test.js 來對基本類型進(jìn)行驗(yàn)證:

const data = {
  a: 1,
  b: 'str',
  c: true,
  d: null,
  e: undefined,
  f: NaN,
  g: Infinity,
}

const dataCopy = JSON.parse(JSON.stringify(data))

可以看到 NaN 赎瑰、 Infinity 在序列化的過程中被轉(zhuǎn)化為了 null ,而 undefined 則丟失了:

image.png

再使用 用例 3.test.js 對引用類型進(jìn)行測試:

const data = {
  a: [1, 2, 3],
  b: {foo: 'obj'},  
  c: new Date('2019-08-28'),
  d: /^abc$/g,
  e: function() {},
  f: new Set([1, 2, 3]),
  g: new Map([['foo', 'map']]),
}

const dataCopy = JSON.parse(JSON.stringify(data))

對于引用類型數(shù)據(jù)破镰,在序列化與反序列化過程中餐曼,只有數(shù)組和對象被正常拷貝鲜漩,其中時(shí)間對象被轉(zhuǎn)化為了字符串源譬,函數(shù)會丟失,其他的都被轉(zhuǎn)化為了空對象:

image.png

利用 用例 4.test.js 對構(gòu)造函數(shù)進(jìn)行驗(yàn)證:

function Person(name) {
  // 構(gòu)造函數(shù)實(shí)例屬性name
  this.name = name
  // 構(gòu)造函數(shù)實(shí)例方法getName
  this.getName = function () { 
    return this.name
  }
}
// 構(gòu)造函數(shù)原型屬性age
Person.prototype.age = 18

const person = new Person('xxx')
const personCopy = JSON.parse(JSON.stringify(person))

在拷貝過程中只會序列化對象可枚舉的自身屬性孕似,因此無法拷貝 Person 上的原型屬性 age 踩娘;由于序列化的過程中構(gòu)造函數(shù)會丟失,所以 personCopyconstructor 會指向頂層的原生構(gòu)造函數(shù) Object 而不是自定義構(gòu)造函數(shù)Person

image.png

2. 手動(dòng)實(shí)現(xiàn)深拷貝方法

簡單版

我們先來實(shí)現(xiàn)一個(gè)簡單版的深拷貝喉祭,思路是养渴,判斷data類型,若不是引用類型臂拓,直接返回;如果是引用類型习寸,然后判斷data是數(shù)組還是對象胶惰,并對data進(jìn)行遞歸遍歷,如下:

function cloneDeep(data) {
  if(!data || typeof data !== 'object') return data
  const retVal = Array.isArray(data) ? [] : {}
  for(let key in data) {
    retVal[key] = cloneDeep(data[key])
  }
  return retVal
}

執(zhí)行 用例 clone1.test.js

const data = {
  str: 'test',
  obj: {
    foo: 'test'
  },
  arr: ['test', {foo: 'test'}]
}

const dataCopy = cloneDeep(data)

可以看到對于對象和數(shù)組能夠?qū)崿F(xiàn)正確的拷貝

image.png

首先是只考慮了對象和數(shù)組這兩種類型霞溪,其他引用類型數(shù)據(jù)依然與原數(shù)據(jù)共享同一內(nèi)存空間孵滞,有待完善中捆;其次,對于自定義的構(gòu)造函數(shù)而言坊饶,在拷貝的過程中會丟失實(shí)例對象的 constructor 泄伪,因此其構(gòu)造函數(shù)會變?yōu)槟J(rèn)的 Object

處理其他數(shù)據(jù)類型

在上一步我們實(shí)現(xiàn)的簡單深拷貝,只考慮了對象和數(shù)組這兩種引用類型數(shù)據(jù)匿级,接下來將對其他常用數(shù)據(jù)結(jié)構(gòu)進(jìn)行相應(yīng)的處理

定義通用方法

我們首先定義一個(gè)方法來正確獲取數(shù)據(jù)的類型蟋滴,這里利用了 Object 原型對象上的 toString 方法,它返回的值為 [object type] 痘绎,我們截取其中的type即可津函。然后定義了數(shù)據(jù)類型集合的常量,如下:

const getType = (data) => {
  return Object.prototype.toString.call(data).slice(8, -1)
}

const TYPE = {
  Object: 'Object',
  Array: 'Array',
  Date: 'Date',
  RegExp: 'RegExp',
  Set: 'Set',
  Map: 'Map',
}
主函數(shù)實(shí)現(xiàn)

接著我們完善對于其他類型的處理孤页,根據(jù)不同的 data 類型尔苦,對 data 進(jìn)行不同的初始化操作,然后進(jìn)行相應(yīng)的遞歸遍歷行施,如下:

const cloneDeep = (data) => {
  if (!data || typeof data !== 'object') return data
  let cloneData = data
  const Constructor = data.constructor;
  const dataType = getType(data)
  // data 初始化
  if (dataType === TYPE.Array) {
    cloneData = []
  } else if (dataType === TYPE.Object) {
    // 獲取原對象的原型
    cloneData = Object.create(Object.getPrototypeOf(data))
  } else if (dataType === TYPE.Date) {
    cloneData = new Constructor(data.getTime())
  } else if (dataType === TYPE.RegExp) {
    const reFlags = /\w*$/
    // 特殊處理regexp允坚,拷貝過程中l(wèi)astIndex屬性會丟失
    cloneData = new Constructor(data.source, reFlags.exec(data))
    cloneData.lastIndex = data.lastIndex
  } else if (dataType === TYPE.Set || dataType === TYPE.Map) {
    cloneData = new Constructor()
  }
  
  // 遍歷 data
  if (dataType === TYPE.Set) {
    for (let value of data) {
      cloneData.add(cloneDeep(value))
    }
  } else if (dataType === TYPE.Map) {
    for (let [mapKey, mapValue] of data) {
      // Map的鍵、值都可以是引用類型蛾号,因此都需要拷貝
      cloneData.set(cloneDeep(mapKey), cloneDeep(mapValue))
    }
  } else {
    for (let key in data) {
      // 不考慮繼承的屬性
      if (data.hasOwnProperty(key)) {
        cloneData[key] = cloneDeep(data[key])
      }
    }
  }
  return cloneData
}

上面的代碼完整版可以參考 clone2.js 稠项,接下來使用 用例 clone2.test.js 進(jìn)行驗(yàn)證:

const data = {
    obj: {},
  arr: [],
  reg: /reg/g,
  date: new Date('2019'),
  person: new Person('lixx'),
  set: new Set([{test: 'set'}]),
  map: new Map([[{key: 'map'}, {value: 'map'}]])
}

function Person(name) {
    this.name = name
}

const dataClone = cloneDeep(data)

可以看到對于不同類型的引用數(shù)據(jù)都能夠?qū)崿F(xiàn)正確拷貝,結(jié)果如下:

image.png
關(guān)于函數(shù)

函數(shù)的拷貝我這里沒有實(shí)現(xiàn)须教,兩個(gè)對象中的函數(shù)使用同一個(gè)內(nèi)存空間并沒有什么問題皿渗。實(shí)際上,查看了 lodash/cloneDeep 的相關(guān)實(shí)現(xiàn)后轻腺,對于函數(shù)它是直接返回的:

image.png

到這一步乐疆,我們的深拷貝方法已經(jīng)初具雛形,實(shí)際上需要特殊處理的數(shù)據(jù)類型遠(yuǎn)不止這些贬养,還有 Error 挤土、 BufferElement 等误算,有興趣的小伙伴可以繼續(xù)探索實(shí)現(xiàn)一下~

處理循環(huán)引用

目前為止深拷貝能夠處理絕大部分常用的數(shù)據(jù)結(jié)構(gòu)仰美,但是當(dāng)數(shù)據(jù)中出現(xiàn)了循環(huán)引用時(shí)它就束手無策了

const a = {}
a.a = a

cloneDeep(a)

可以看到,對于循環(huán)引用儿礼,在進(jìn)行遞歸調(diào)用的時(shí)候會變成死循環(huán)而導(dǎo)致棧溢出:

image.png

那么如何破解呢咖杂?

拋開循環(huán)引用不談,我們先來看看基本的 引用 問題蚊夫,前文所實(shí)現(xiàn)的深拷貝方法以及 JSON 序列化拷貝都會解除原引用類型對于其他數(shù)據(jù)的引用诉字,來看下面這個(gè)例子

const temp = {}
const data = {
    a: temp,
  b: temp,
}
const dataJson = JSON.parse(JSON.stringify(data))
const dataClone = cloneDeep(data)

驗(yàn)證一下引用關(guān)系:

image.png

如果解除這種引用關(guān)系是你想要的,那完全ok。如果你想保持?jǐn)?shù)據(jù)之間的引用關(guān)系壤圃,那么該如何去實(shí)現(xiàn)呢陵霉?

一種做法是可以用一個(gè)數(shù)據(jù)結(jié)構(gòu)將已經(jīng)拷貝過的內(nèi)容存儲起來,然后在每次拷貝之前進(jìn)行查詢伍绳,如果發(fā)現(xiàn)已經(jīng)拷貝過了踊挠,直接返回存儲的拷貝值即可保持原有的引用關(guān)系。

因?yàn)槟軌虮徽_拷貝的數(shù)據(jù)均為引用類型冲杀,所以我們需要一個(gè) key-valuekey 可以是引用類型的數(shù)據(jù)結(jié)構(gòu)效床,自然想到可以利用 Map/WeakMap 來實(shí)現(xiàn)。

這里我們利用一個(gè) WeakMap 的數(shù)據(jù)結(jié)構(gòu)來保存已經(jīng)拷貝過的結(jié)構(gòu)漠趁, WeakMapMap 最大的不同扁凛,就是它的鍵是弱引用的,它對于值的引用不計(jì)入垃圾回收機(jī)制闯传,也就是說谨朝,當(dāng)其他引用都解除時(shí),垃圾回收機(jī)制會釋放該對象的內(nèi)存甥绿;假如使用強(qiáng)引用的 Map 字币,除非手動(dòng)解除引用,否則這部分內(nèi)存不會得到釋放共缕,容易造成內(nèi)存泄漏洗出。

具體的實(shí)現(xiàn)如下:

const cloneDeep = (data, hash = new WeakMap()) => {
  if (!data || typeof data !== 'object') return data
  // 查詢是否已拷貝
  if(hash.has(data)) return hash.get(data)
  let cloneData = data
  const Constructor = data.constructor;
  const dataType = getType(data)
  // data 初始化
  if (dataType === TYPE.Array) {
    cloneData = []
  } else if (dataType === TYPE.Object) {
    // 獲取原對象的原型
    cloneData = Object.create(Object.getPrototypeOf(data))
  } else if (dataType === TYPE.Date) {
    cloneData = new Constructor(data.getTime())
  } else if (dataType === TYPE.RegExp) {
    const reFlags = /\w*$/
    // 特殊處理regexp,拷貝過程中l(wèi)astIndex屬性會丟失
    cloneData = new Constructor(data.source, reFlags.exec(data))
    cloneData.lastIndex = data.lastIndex
  } else if (dataType === TYPE.Set || dataType === TYPE.Map) {
    cloneData = new Constructor()
  }
  // 寫入 hash
  hash.set(data, cloneData)
  // 遍歷 data
  if (dataType === TYPE.Set) {
    for (let value of data) {
      cloneData.add(cloneDeep(value, hash))
    }
  } else if (dataType === TYPE.Map) {
    for (let [mapKey, mapValue] of data) {
      // Map的鍵图谷、值都可以是引用類型翩活,因此都需要拷貝
      cloneData.set(cloneDeep(mapKey, hash), cloneDeep(mapValue, hash))
    }
  } else {
    for (let key in data) {
      // 不考慮繼承的屬性
      if (data.hasOwnProperty(key)) {
        cloneData[key] = cloneDeep(data[key], hash)
      }
    }
  }
  return cloneData
}

經(jīng)過改造后的深拷貝函數(shù)能夠保留原數(shù)據(jù)的引用關(guān)系,也可以正確處理不同引用類型的循環(huán)引用,利用下面的用例 clone3.test.js 來進(jìn)行驗(yàn)證:

const temp = {}
const data = {
    a: temp,
  b: temp,
}
const dataClone = cloneDeep(data)

const obj = {}
obj.obj = obj

const arr = []
arr[0] = arr

const set = new Set()
set.add(set)

const map = new Map()
map.set(map, map)

結(jié)果如下:

image.png

思考:使用非遞歸

在前面的深拷貝實(shí)現(xiàn)方法中,均是通過遞歸的方式來進(jìn)行遍歷谣沸,當(dāng)遞歸的層級過深時(shí)妄壶,也會出現(xiàn)棧溢出的情況落恼,我們使用下面的 create 方法創(chuàng)建深度為10000,廣度為100的示例數(shù)據(jù):

function create(depth, breadth) {
  const data = {}
  let temp = data
  let i = j = 0
  while(i < depth) {
    temp = temp['data'] = {}
    while(j < breadth) {
      temp[j] = j
      j++
    }
    i++
  }
  return data
}

const data = create(10000, 100)
cloneDeep(data)

結(jié)果如下:

image.png

那么假如不使用遞歸,我們應(yīng)該如何實(shí)現(xiàn)呢?

以對象為例隘梨,存在下面這樣一個(gè)數(shù)據(jù)結(jié)構(gòu):

const data = {
  left: 1,
  right: {
    left: 1,
    right: 2,
  }
}

那么換個(gè)角度看,其實(shí)它就是一個(gè)類樹形結(jié)構(gòu):

image.png

我們對該對象進(jìn)行遍歷實(shí)際上相當(dāng)于模擬對樹的遍歷舷嗡。樹的遍歷主要分為深度優(yōu)先遍歷和廣度優(yōu)先遍歷轴猎,前者一般借助來實(shí)現(xiàn),后者一般借助隊(duì)列來實(shí)現(xiàn)进萄。

這里模擬了樹的深度優(yōu)先遍歷捻脖,僅考慮對象和非對象烦秩,利用棧來實(shí)現(xiàn)一個(gè)不使用遞歸的簡單深拷貝方法:

function cloneDeep(data) {
  const retVal = {}
    const stack = [{
    target: retVal,
    source: data,
  }]
  // 循環(huán)整個(gè)stack
  while(stack.length > 0) {
    // 棧頂節(jié)點(diǎn)出棧
    const node = stack.pop()
    const { target, source } = node
    // 遍歷當(dāng)前節(jié)點(diǎn)
    for(let item in source) {
      if (source.hasOwnProperty(item)) {
        if (Object.prototype.toString.call(source[item]) === '[object Object]') {
          target[item] = {}
            // 子節(jié)點(diǎn)如果是對象,將該節(jié)點(diǎn)入棧
          stack.push({
            target: target[item],
            source: source[item],
          })
        } else {
            // 子節(jié)點(diǎn)如果不是對象郎仆,直接拷貝
            target[item] = source[item]
        }
      }
    }
  }
  return retVal
}

關(guān)于完整的深拷貝非遞歸實(shí)現(xiàn),可以參考 clone4.js 兜蠕,對應(yīng)的測試用例為 用例 clone4.test.js 扰肌,這里就不給出了

3. 深拷貝方法比較

這里列舉了常見的幾種深拷貝方法,并進(jìn)行簡單比較

  • JSON.parse(JSON.stringify(data))
  • jQuery中的$.extend
  • 我們這里自己實(shí)現(xiàn)的clone3.js中的cloneDeep
  • loadsh中的_.cloneDeep

關(guān)于耗時(shí)比較熊杨,采用前文的 create 方法創(chuàng)建了一個(gè)廣度曙旭、深度均為1000的數(shù)據(jù),在 node v10.14.2 環(huán)境下循環(huán)執(zhí)行以下方法各10000次晶府,這里的耗時(shí)取值為運(yùn)行十次測試用例的平均值桂躏,如下:

基本類型 數(shù)組、對象 特殊引用類型 循環(huán)引用 耗時(shí)
JSON 無法處理 NaN 川陆、 Infinity 剂习、 Undefined 丟失對象原型 ? ? 7280.6ms
$.extend 無法處理 Undefined 丟失對象原型、拷貝原型屬性 ?<br />(使用同一引用) ? 5550.6ms
cloneDeep ?? ?? ??(待完善) ?? 5035.3ms
_.cloneDeep ?? ?? ?? ?? 5854.5ms

在日常的使用過程中较沪,如果你確定你的數(shù)據(jù)中只有數(shù)組鳞绕、對象等常見類型,你大可以放心使用JSON序列化的方式來進(jìn)行深拷貝尸曼,其它情況下還是推薦引入 loadsh/cloneDeep 來實(shí)現(xiàn)

小結(jié)

深拷貝的水很“深”们何,淺拷貝也不“淺”,小小的深拷貝里面蘊(yùn)含的知識點(diǎn)十分豐富:

  • 考慮問題是否全面控轿、嚴(yán)謹(jǐn)
  • 基礎(chǔ)知識冤竹、api熟練程度
  • 對深拷貝、淺拷貝的認(rèn)識
  • 對數(shù)據(jù)類型的理解
  • 遞歸/非遞歸(循環(huán))
  • Set茬射、Map/WeakMap等

我相信鹦蠕,要是面試官愿意挖掘的話,能考查的知識點(diǎn)遠(yuǎn)不止這么多躲株,這個(gè)時(shí)候就要考驗(yàn)?zāi)阕约旱幕竟σ约爸R面的深廣度了片部,而這些都離不開平時(shí)的積累。千里之行霜定,積于跬步档悠,萬里之船,成于羅盤

本文如有錯(cuò)誤望浩,還請各位批評指正~

參考

原文鏈接:深拷貝實(shí)踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辖所,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子磨德,更是在濱河造成了極大的恐慌缘回,老刑警劉巖吆视,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異酥宴,居然都是意外死亡啦吧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門拙寡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來授滓,“玉大人,你說我怎么就攤上這事肆糕“愣眩” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵诚啃,是天一觀的道長淮摔。 經(jīng)常有香客問我,道長始赎,這世上最難降的妖魔是什么和橙? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮造垛,結(jié)果婚禮上胃碾,老公的妹妹穿的比我還像新娘。我一直安慰自己筋搏,他們只是感情好仆百,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奔脐,像睡著了一般俄周。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上髓迎,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天峦朗,我揣著相機(jī)與錄音,去河邊找鬼排龄。 笑死波势,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的橄维。 我是一名探鬼主播尺铣,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼争舞!你這毒婦竟也來了凛忿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤竞川,失蹤者是張志新(化名)和其女友劉穎店溢,沒想到半個(gè)月后叁熔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡床牧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年荣回,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戈咳。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驹马,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出除秀,到底是詐尸還是另有隱情,我是刑警寧澤算利,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布册踩,位于F島的核電站,受9級特大地震影響效拭,放射性物質(zhì)發(fā)生泄漏暂吉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一缎患、第九天 我趴在偏房一處隱蔽的房頂上張望慕的。 院中可真熱鬧,春花似錦挤渔、人聲如沸肮街。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫉父。三九已至,卻和暖如春眼刃,著一層夾襖步出監(jiān)牢的瞬間绕辖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工擂红, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仪际,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓昵骤,卻偏偏與公主長得像树碱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子变秦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,090評論 1 32
  • 問題 對一個(gè)由自定義類型對象組成的數(shù)組進(jìn)行copy操作赴恨,得到一個(gè)新的數(shù)組,如果改變新數(shù)組中某個(gè)元素的值伴栓,原有數(shù)組中...
    樂鷹神騎駿閱讀 1,025評論 0 1
  • 寫在前面 各類技術(shù)論壇關(guān)于深拷貝的博客有很多,有些寫的也比我好,那為什么我還要堅(jiān)持寫這篇博客呢,之前看到的一篇博客...
    心_c2a2閱讀 21,137評論 3 18
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,124評論 0 3
  • 爸爸媽媽家里的親戚每次要到過年的時(shí)候都在打電話的時(shí)候問我們過年要不要回家伦连,早點(diǎn)回家雨饺。每次問到這個(gè)問題的時(shí)候我都會說...
    jinma0606閱讀 352評論 2 0