引用、淺拷貝及深拷貝 到 Map、Set(含對象assign、freeze方法、WeakMap、WeakSet及數組map、reduce等等方法)

来源:https://www.cnblogs.com/yubingyang/archive/2019/09/24/11576515.html
-Advertisement-
Play Games

從引用聊到深淺拷貝,從深拷貝過渡到ES6新數據結構Map及Set,再到另一個map即Array.map()和與其類似的Array.flatMap(),中間會有其他相關話題,例如Object.freeze()與Object.assign()等等。前言一邊複習一邊學習,分清引用與深淺拷貝的區別,並實現淺 ...



從引用聊到深淺拷貝,從深拷貝過渡到ES6新數據結構Map及Set,再到另一個map即Array.map()和與其類似的Array.flatMap(),中間會有其他相關話題,例如Object.freeze()Object.assign()等等。

前言

一邊複習一邊學習,分清引用與深淺拷貝的區別,並實現淺拷貝與深拷貝,之後通過對深拷貝的瞭解,拓展到ES6新數據結構Map及Set的介紹,再引入對另一個數組的map方法的使用與類似數組遍歷方法的使用。通過一條隱式鏈將一長串知識點串聯介紹,可能會有點雜,但也會有對各知識點不同之處有明顯區分,達到更好的記憶與理解。

引用、淺拷貝及深拷貝

  • 引用

    通常在介紹深拷貝之前,作為引子我們會看見類似以下例子:

    var testObj = {   name: 'currName' } var secObj = testObj secObj.name = 'changedName' console.log(testObj) // { name: 'changedName' } 複製代碼

    這其實就是一種引用,對於複雜數據結構,為了節省存儲資源,符號 “=” 其實並不是將值賦給新建的變數,而是做了一個地址引用,使其指向原來存儲在堆中的數據的地址,此時testObj與secObj都指向同一個地址,因此在修改secObj的數據內容時,即是對其指向的原有數據進行修改。

    對於數組有相似的引用情況,代碼如下:

    var testArr = [0, [1, 2]] var secArr = testArr secArr[0] = 'x' console.log(testArr) // [ 'x', [ 1, 2 ] ] 複製代碼
  • 淺拷貝

    對於淺拷貝,其與引用的區別,我們一邊實現淺拷貝,之後進行對比再解釋,實現如下:

    function shallowCopy (obj) {   var retObj = {}   for (const key in obj) {     if (obj.hasOwnProperty(key)) {       retObj[key] = obj[key];     }   }   return retObj }  var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerobj': 'content'   } } var secObj = shallowCopy(testObj) secObj.name = 'changedName' secObj.nums[0] = '一' secObj.nums[1] = ['二', '三'] console.log(testObj) // { name: 'currName',                      //   nums: [ '一', [ '二', '三' ] ],                      //   objs: { innerObj: 'changedContent' } } console.log(secObj) // { name: 'changedName',                     //   nums: [ '一', [ '二', '三' ] ],                     //   objs: { innerObj: 'changedContent' } } 複製代碼

    從上例可以看出經過淺拷貝後得到的對象,對於第一層數據其修改後已經不能影響之前的數據,但對於內部還存在迭代器的數據屬性,還是有引用情況的存在,所以後者對這些屬性的修改,依舊會影響前者中這些屬性的內容。

    引用與淺拷貝的區別就在於: 對第一層數據是否依舊修改後互相影響。

    淺拷貝相關方法

    • Object.assign()

      assign方法效果類似於在數組中的concat拼接方法,其可以將源對象中可枚舉屬性進行複製到目標對象上,並返回目標對象,該方法中第一個參數便就是目標對象,其他參數為源對象。因此該方法我們定義源對象為空對象時便可以在對拷貝的實現中使用,但需要註意的是Object.assign()其方法自身實行的便是淺拷貝,而不是深拷貝,因此通過該方法實現的拷貝只能是淺拷貝。

      實現淺拷貝代碼如下:

      var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } var secObj = Object.assign({}, testObj) secObj.name = 'changedName' secObj.nums[0] = '一' secObj.nums[1] = ['二', '三'] secObj.objs['innerObj'] = 'changedContent' console.log(testObj) // { name: 'currName',                      //   nums: [ '一', [ '二', '三' ] ],                      //   objs: { innerObj: 'changedContent' } } console.log(secObj) // { name: 'changedName',                     //   nums: [ '一', [ '二', '三' ] ],                     //   objs: { innerObj: 'changedContent' } } 複製代碼
    • Object.freeze()

      freeze方法其效果在有一定程度與淺拷貝相同,但效果上還要比拷貝多上一層,即freeze凍結,但因為該方法自身 內部屬性,該方法的名稱又可以稱為“淺凍結”,對於第一層數據,如淺拷貝一般,不可被新對象改變,但被freeze方法凍結過的對象,其自身也無法添加、刪除或修改其第一層數據,但因為“淺凍結”這名稱中淺的這一明顯屬性,freeze方法對於內部如果存在更深層的數據,是可以被自身修改,且也會被“=”號所引用給新的變數。

      簡單使用如下:

      var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } var secObj = Object.freeze(testObj) secObj.name = 'changedName' secObj.nums[0] = '一' secObj.nums[1] = ['二', '三'] secObj.objs['innerObj'] = 'changedContent' secObj.age = 18 delete secObj.name console.log(testObj) // { name: 'currName',                      //   nums: [ '一', [ '二', '三' ] ],                      //   objs: { innerObj: 'changedContent' } } console.log(secObj) // { name: 'currName',                     //   nums: [ '一', [ '二', '三' ] ],                     //   objs: { innerObj: 'changedContent' } } 複製代碼
  • 深拷貝

    接上面對淺拷貝的介紹,很容易就可以想到深拷貝便是在淺拷貝的基礎上,讓內部存在更深層數據的對象,不止第一層不能改變原有數據,內部更深層次數據修改時也不能使原有數據改變,即消除了數據中所有存在引用的情況。通過對淺拷貝的實現,我們很容易就想到通過遞歸的方法對深拷貝進行實現。

    以下就是通過遞歸實現深拷貝的過程:

    • Version 1: 對於深拷貝,因為存在數組與對象互相嵌套的問題,第一個版本先簡單統一處理對象的深拷貝,不深究數組對象的存在。

      function deepCopy(content) {   var retObj = {}   for (const key in content) {     if (content.hasOwnProperty(key)) {       retObj[key] = typeof content[key] === 'object'        ? deepCopy(content[key])       : content[key];     }   }   return retObj }  var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } var secObj = deepCopy(testObj) secObj.name = 'changedName' secObj.nums[0] = '一' secObj.nums[1] = ['二', '三'] secObj.objs['innerObj'] = 'changedContent' secObj.age = 18 console.log(testObj) // { name: 'currName',                      //   nums: [ 1, [ 2, 3 ] ],                      //   objs: { innerObj: 'content' } } console.log(secObj) // { name: 'changedName',                     //   nums: { '0': '一', '1': [ '二', '三' ] },                     //   objs: { innerObj: 'changedContent' },                     //   age: 18 } 複製代碼
    • Version 2: 完善數組與對象組合嵌套的情況

      此時對於內部存在的數組來說,會被轉化為對象,鍵為數組的下標,值為數組的值,被存儲在新的對象中,因此有了我們完善的第二版。

      function deepCopy (obj) {   var tempTool = Array.isArray(obj) ? [] : {}    for (const key in obj) {     if (obj.hasOwnProperty(key)) {       tempTool[key] = typeof obj[key] === 'object'        ? deepCopy(obj[key])       : Array.isArray(obj) ? Array.prototype.concat(obj[key]) : obj[key];     }   }   return tempTool }  var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } var secObj = deepCopy(testObj) secObj.name = 'changedName' secObj.nums[0] = '一' secObj.nums[1] = ['二', '三'] secObj.objs['innerObj'] = 'changedContent' secObj.age = 18 console.log(testObj) // { name: 'currName',                      //   nums: [ 1, [ 2, 3 ] ],                      //   objs: { innerObj: 'content' } } console.log(secObj) // { name: 'changedName',                     //   nums: [ '一', [ '二', '三' ] ],                     //   objs: { innerObj: 'changedContent' },                     //   age: 18 } 複製代碼

ES6中 Map、Set

  • Map

    對於Hash結構 即 鍵值對的集合,Object對象只能用字元串作為key值,在使用上有很大的限制,ES6提供的新的數據結構Map相對於Object對象,其“鍵”的範圍不限於字元串類型,實現了“值-值”的對應,使用上可以有更廣泛的運用。但Map在賦值時,只能接受如數組一般有lterator介面且每個成員都是雙元素的數組的數據結構作為參數,該數組成員是一個個表示鍵值對的數組,之外就只能通過Map自身set方法添加成員。

    所以以下我們先介紹將對象轉為Map的方法,再對Map自身方法做一個簡單介紹,本節最後介紹一個Map的運用場景

    • Object轉為Map方法:

      function objToMap (object) {   let map = new Map()   for (const key in object) {     if (object.hasOwnProperty(key)) {       map.set(key, object[key])     }   }   return map } var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } let map = objToMap(testObj) map.set('name', 'changedName') console.log(testObj) // { name: 'currName',                      //   nums: [ 1, [ 2, 3 ] ],                      //   objs: { innerObj: 'content' } } console.log(map) // Map {                  // 'name' => 'changedName',                  // 'nums' => [ 1, [ 2, 3 ] ],                  // 'objs' => { innerObj: 'content' } } 複製代碼
    • Map自身方法介紹

      含增刪改查方法:set、get、has、delete;

      遍歷方法:keys、values、entries、forEach;

      其他方法:size、clear。

      需要註意的是forEach方法還可以接受第二個參數,改變第一個參數即回調函數的內部this指向。

      let map = new Map([  ['name', 'currName'],  ['nums', [1, [2, 3]]],  ['objs', {'innerObj': 'content'}] ]) // 增 刪 改 查 map.set('test', 'testContent') map.delete('objs') map.set('name', 'changedName') console.log(map.get('nums')) // [ 1, [ 2, 3 ] ] console.log(map.has('nums')) // true console.log(map) // Map {                 // 'name' => 'changedName',                 // 'nums' => [ 1, [ 2, 3 ] ],                 // 'test' => 'testContent' }  // 遍歷方法 console.log(map.keys()) // [Map Iterator] { 'name', 'nums', 'test' } console.log(map.values()) // [Map Iterator] { 'changedName', [ 1, [ 2, 3 ] ], 'testContent' } console.log(map.entries()) // [Map Iterator] {                           // [ 'name', 'changedName' ],                           // [ 'nums', [ 1, [ 2, 3 ] ] ],                           // [ 'test', 'testContent' ] }  const testObj = {  objName: 'objName' } map.forEach(function (value, key) {  console.log(key, value, this.objName) // name changedName objName                                        // nums [ 1, [ 2, 3 ] ] objName                                        // test testContent objName }, testObj)  // 其他方法 console.log(map.size) // 3  console.log(map) // Map {                 // 'name' => 'changedName',                 // 'nums' => [ 1, [ 2, 3 ] ],                 // 'test' => 'testContent' } map.clear() console.log(map) // Map {} 複製代碼
    • Map應用場景

      對於經典演算法問題中 上樓梯問題:共n層樓梯,一次僅能跨1或2步,總共有多少種走法?

      這一類問題都有一個遞歸過程中記憶體溢出的bug存在,此時就可以運用Map減少遞歸過程中重覆運算的部分,解決記憶體溢出的問題。

      let n = 100 let map = new Map() function upStairs (n) {   if (n === 1) return 1   if (n === 2) return 2   if (map.has(n)) return map.get(n)   let ret = upStairs(n - 1) + upStairs(n - 2)   map.set(n, ret)   return ret } console.log(upStairs(n)) // 573147844013817200000 複製代碼
  • WeakMap

    本節介紹在ES6中,與Map相關且一同發佈的WeakMap數據結構。

    • WeakMap與Map區別

      WeakMap與Map主要有下圖三個區別:

      區別MapWeakMap
      “鍵”類型:任何類型Object對象
      自身方法:基本方法:set、get、has、delete;
      遍歷方法:keys、values、entries、forEach;
      其他方法:size、clear。
      基本方法:set、get、has、delete。
      鍵引用類型:強引用弱引用

      此處我們對強弱引用進行簡單介紹:弱引用在回收機制上比強引用好,在“適當”的情況將會被回收,減少記憶體資源浪費,但由於不是強引用,WeakMap不能進行遍歷與size方法取得內部值數量。

    • WeakMap自身方法

      含增刪改查方法:set、get、has、delete。

      let wMap = new WeakMap() let key = {} let obj = {name: 'objName'}  wMap.set(key, obj) console.log(wMap.get(key)) // { name: 'objName' } console.log(wMap.has(key)) // true  wMap.delete(key) console.log(wMap.has(key)) // false 複製代碼
    • WeakMap應用場景

      WeakMap因為鍵必須為對象,且在回收機制上的優越性,其可以用在以下兩個場景:

      1. 對特定DOM節點添加狀態時。當DOM節點被刪除,將DOM節點作為“鍵”的WeakMap也會自動被回收。

      2. 對類或構造函數中私有屬性綁定定義。當實例被刪除,被作為“鍵”的this消失,WeakMap自動回收。

      示例代碼如下:

      <!--示例一--> let element = document.getElementById('box') let wMap = new WeakMap() wMap.set(element, {clickCount: 0}) element.addEventListener('click', () => {   let countObj = wMap.get(element)   countObj.clickCount++    console.log(wMap.get(element).clickCount) // click -> n+=1 })  <!--示例二--> const _age = new WeakMap() const _fn = new WeakMap() class Girl {   constructor (age, fn) {     _age.set(this, age)     _fn.set(this, fn)   }   changeAge () {     let age = _age.get(this)     age = age >= 18 ? 18 : null     _age.set(this, age)      _age.get(this) === 18     ? _fn.get(this)()     : console.log('error')   } }  const girl = new Girl(25, () => console.log('forever 18 !')) girl.changeAge() // forever 18 ! 複製代碼
  • Set

    介紹完ES6新增的Map與WeakMap數據結構,我們繼續介紹一同新增的Set數據結構。

    Set之於Array,其實有點像Map之於Object,Set是在數組的數據結構基礎上做了一些改變,新出的一種類似於數組的數據結構,Set的成員的值唯一,不存在重覆的值。以下將對Set數據結構作一些簡單的介紹。

    • Set與Array之間的相互轉換

      Set可以將具有Iterable介面的其他數據結構作為參數用於初始化,此處不止有數組,但僅以數組作為例子,單獨講述一下。

      // Set -> Array let arr = [1, 2, 3, 3] let set = new Set(arr) console.log(set) // Set { 1, 2, 3 }  // Array -> Set const arrFromSet1 = Array.from(set) const arrFromSet2 = [...set] console.log(arrFromSet1) // [ 1, 2, 3 ] console.log(arrFromSet2) // [ 1, 2, 3 ] 複製代碼
    • Set自身方法

      Set內置的方法與Map類似

      含增刪查方法:add、has、delete;

      遍歷方法:keys、values、entries、forEach;

      其他方法:size、clear。

      let arr = [1, 2, 3, 3] let set = new Set(arr) // 增刪改查 set.add(4) console.log(set) // Set { 1, 2, 3, 4 } set.delete(3) console.log(set) // Set { 1, 2, 4 } console.log(set.has(4)) // true  // 遍歷方法 因為在Set結構中沒有鍵名只有健值,所以keys方法和values方法完全一致 console.log(set.keys()) // [Set Iterator] { 1, 2, 4 } console.log(set.values()) // [Set Iterator] { 1, 2, 4 }  for (const item of set.entries()) {   console.log(item) //[ 1, 1 ]                     // [ 2, 2 ]                     // [ 4, 4 ] }  const obj = {   name: 'objName' } set.forEach(function (key, value) {   console.log(key, value, this.name) // 1 1 'objName'                                      // 2 2 'objName'                                      // 4 4 'objName' }, obj)  // 其他方法 console.log(set.size) // 3 set.clear() console.log(set) // Set {} 複製代碼
    • Set應用場景

      因為擴展運算符...對Set作用,再通過Array遍歷方法,很容易求得並集、交集及差集,也可以通過間接使用Array方法,構造新的數據賦給Set結構變數。

      let a = new Set([1, 2, 3]) let b = new Set([2, 3, 4])  // 並集 let union = new Set([...a, ...b]) console.log(union) // Set { 1, 2, 3, 4 } // 交集 let intersect = new Set([...a].filter(x => b.has(x))) console.log(intersect) // Set { 2, 3 } // 差集 let difference = new Set([...[...a].filter(x => !b.has(x)), ...[...b].filter(x => !a.has(x))]) console.log(difference) // Set { 1, 4 }  // 賦新值 let aDouble = new Set([...a].map(x => x * 2)) console.log(aDouble) // Set { 2, 4, 6 }  let bDouble = new Set(Array.from(b, x => x * 2)) console.log(bDouble) // Set { 4, 6, 8 } 複製代碼
  • WeakSet

    • WeakSet與Set對比

      WeakSet之於Set,依舊相當於WeakMap之於Map。

      WeakSet與Set之間不同之處,依然是:

      1. WeakSet內的值只能為對象;

      2. WeakSet依舊是弱引用。

    • WeakSet自身方法

      因為弱引用的關係,WeakSet只有簡單的增刪查方法:add、delete、has

      let obj1 = {'name': 1} let obj2 = {'name': 2} let wSet = new WeakSet() wSet.add(obj1).add(obj2) console.log(wSet.has(obj2)) // true wSet.delete(obj2) console.log(wSet.has(obj2)) // false 複製代碼
    • WeakSet應用場景

      對於WeakSet的應用場景,其與WeakMap類似,因為弱引用的優良回收機制,WeakSet依舊可以存放DOM節點,避免刪除這些節點後引發的記憶體泄漏的情況;也可以在構造函數和類中存放實例this,同樣避免刪除實例的時候產生的記憶體泄漏的情況。

      // 1 let wSet = new WeakSet() wSet.add(document.getElementById('box'))  const _boy = new WeakSet() // 2 class Boy {   constructor () {     _boy.add(this)   }   method () {     if (!_boy.has(this)) {       throw new TypeError('Boy.prototype.method 只能在Boy的實例上調用!')     }   } } 複製代碼

數組中map方法及遍歷相關方法

講完大Map,此時我們繼續瞭解完小map,map即為Array.map(),是數組中一個遍歷方法。並將map作為一個引子,我們對比多介紹幾個Array中遍歷相關的方法。

  • Array.map()、Array.flatMap()

    Array.map() —— 可以有三個參數,item、index、arr,此時當做forEach使用;常用方法是通過第一個參數遍歷修改後返回一個新數組。

    Array.flatMap() —— 前置知識:Array方法中有一個ES6中新加入的數組展開嵌套的方法Array.flat(),其中可以有一個參數表示展開層數,預設只展開一層。而Array.flatMap() 為 Array.map()與Array.flat()方法的疊加。

    例子如下:

    // flat const testArr = [1, 2, [3, [4]]] const flatArr = testArr.flat() console.log(flatArr) // [1, 2, 3, Array(1)] -> 0: 1                      //                        1: 2                      //                        2: 3                      //                        3: [4]  const arr = [1, 2, 3] // map const mapArr = arr.map(x => x * 2) console.log(mapArr) // [2, 4, 6]  arr.map((item, index, arr) => {   console.log(item, index, arr) // 1 0 [1, 2, 3]                                 // 2 1 [1, 2, 3]                                 // 3 2 [1, 2, 3] })  // flatMap // arr.flatMap(x => [x * 2]) === arr.map(x => x * 2) const flatMapArr = arr.flatMap(x => [x * 2]) console.log(flatMapArr) // [2, 4, 6] 複製代碼
  • Array.reduce()

    Array.reduce() —— reduce方法與map最大的不同是不返回新的數組,其返回的是一個計算值,參數為回調函數與回調函數參數pre初始值,回調函數中參數為pre與next,當在預設情況時,pre為數組中第一個值,next為數組中第二個值,回調函數返回值可以滾雪球般更改pre值;而當index設置數值後,pre初始值為參數值,next從數組中第一個值一直取到數組最後一位。

    例子如下:

    const arr = [1, 2, 3, 4, 5]  const result = arr.reduce((pre, next) => {   console.log(pre, next) // 1 2                          // 3 3                          // 6 4                          // 10 5   return pre + next }) console.log(result) // 15  arr.reduce((pre, next) => {   console.log(pre, next) // 9 1                          // 9bala 2                          // 9balabala 3                          // 9balabalabala 4                          // 9balabalabalabala 5   return pre += 'bala' }, 9) 複製代碼
  • Array.filter()、Array.find()、Array.findIndex()

    Array.filter() —— 返回值是一個數組,第一個參數為回調函數,第二個參數為回調函數中this指向。回調函數的參數有value,index及arr。滿足回調函數的中過濾條件的,會被push到返回值中新的數組中。

    Array.find() —— 返回值是數組內的一個值,該方法返回數組內滿足條件的第一個值,第一個參數為回調函數,第二個參數為回調函數中this指向。回調函數的參數有查找到的符合條件前的value,index及arr。當查找的是數組中不可重覆的值時,建議使用find方法,會比filter更優越。

    Array.findIndex() —— 返回值為Number,該方法返回數組內滿足條件的第一個值在數組中的index,第一個參數為回調函數,第二個參數為回調函數中this指向。回調函數中的參數與find方法類似。

    例子如下:

    const arr = [1, 2, 3, 4, 5] const obj = {num: 3}  // filter const filterArr = arr.filter(function (value, index, arr) {   console.log(index, arr) // 0 [1, 2, 3, 4, 5]                           // 1 [1, 2, 3, 4, 5]                           // 2 [1, 2, 3, 4, 5]                           // 3 [1, 2, 3, 4, 5]                           // 4 [1, 2, 3, 4, 5]   return value > this.num }, obj) console.log(filterArr) // [4, 5]  // find const findResult = arr.find(function (value, index, arr) {   console.log(index, arr) // 0 [1, 2, 3, 4, 5]                           // 1 [1, 2, 3, 4, 5]                           // 2 [1, 2, 3, 4, 5]                           // 3 [1, 2, 3, 4, 5]   return value > this.num }, obj) console.log(findResult) // 4  // findIndex const findIndexResult = arr.findIndex(function (value) { return value > this.num }, obj) console.log(findIndexResult) // 3 複製代碼
  • Array.includes()

    Array.includes() —— 返回值為Boolean值,其可以簡單快捷的判斷數組中是否含有某個值。其第一個參數為需要查找的值,第二個參數為開始遍歷的位置,遍歷位置起始點預設為0。相比於indexOf、filter

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • layui獲取全部覆選框checkbox選中的值,layui獲取表單開關switch的值 ...
  • 學習jQuer對錶單、表格操作的過程中,按照書上的例子發現一個問題: 以下代碼同樣使用prop()函數,使用attr()方法也不能實現預期 ...
  • 小程式製作扭蛋機 2019-09-24 13:26:53 公司要製作活動小程式,其中有一個扭蛋機的效果實現抽獎的功能。在網上找了好久竟沒有找到(不知道是不是我找代碼的方式有問題)。最後還是自己做一個吧- _ - ,效果如下: 1.wxml 當然我這裡沒有用wx:for遍歷 2.wxss 這一步比較麻 ...
  • 跨域:顧名思義,跨埠,功能變數名稱,協議都算跨域, 平常中請求後臺,發送http請求,就一般用的就是axios跟jquery,用這個兩個發送請求時,在同域也就是不跨域條件下了瀏覽器會自動帶cookie 那現在webpack他有了proxy設置,就是解決了跨域問題,也就是說如果我本地項目想要請求一個http ...
  • webpack在build包的時候,有時候會遇到打包時間很長的問題,這裡提供了一個解決方案,讓打包如絲般順滑~ 1. 介紹 在用 Webpack 打包的時候,對於一些不經常更新的第三方庫,比如 ,`lodash vue` 我們希望能和自己的代碼分離開,Webpack 社區有兩種方案 CommonsC ...
  • 最近朋友圈和微博都刷了一波傑倫的回憶殺–說好不哭,想想都9012了,在學習react如火如荼的路上,也不妨停下腳步來總結總結,朝花夕拾一下。 為了便於闡述,我們還是來段小明和禪師的故事吧。 小明在學習路上遇到了一些問題,於是有了以下對話: <1> npm 對 yarn 小明:經歷了從 npm -> ...
  • 瀏覽器與新技術 面試題來源於我的項目 "「前端面試與進階指南」" 本章關於瀏覽器原理部分的內容主要來源於 "瀏覽器工作原理" ,這是一篇很長的文章,可以算上一本小書了,有精力的非常建議閱讀。 常見的瀏覽器內核有哪些? | 瀏覽器/RunTime | 內核(渲染引擎) | JavaScript 引擎 ...
  • 點擊關註本 "公眾號" 獲取文檔最新更新,並可以領取配套於本指南的 《前端面試手冊》 以及 最標準的簡歷模板 . 前言 Babel 是現代 JavaScript 語法轉換器,幾乎在任何現代前端項目中都能看到他的身影,其背後的原理對於大部分開發者還屬於黑盒,不過 Babel 作為一個工具真的有瞭解背後 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...