主頁 > 前端設計 > 哈希表(JavaScript實作)

哈希表(JavaScript實作)

2021-12-20 07:44:35 前端設計

哈希表

  • 一、哈希表原理
  • 二、哈希表的概念
  • 三、哈希化沖突問題
    • 1、鏈地址法
    • 2、開放地址法
      • 1、線性探索
      • 2、二次探索
      • 3、再哈希法
  • 四、哈希函式的實作
  • 五、封裝哈希表
  • 六、哈希表操作
    • 1、插入&修改操作
    • 2、獲取操作
    • 3、洗掉操作
    • 4、判斷哈希表是否為空
    • 5、獲取哈希表的元素個數
  • 七、哈希表擴容
    • 1、哈希表擴容思想
    • 2、哈希表擴容實作
  • 八、完整代碼

一、哈希表原理

哈希表是一種非常重要的資料結構,幾乎所有的編程語言都有直接或者間接的應用這種資料結構,它通常是基于陣列實作的,當時相對于陣列,它有更多的優勢:

  • 它可以提供非常快速的插入-洗掉-查找操作,
  • 哈希表的速度比數還要快,基本可以瞬間查找到想要的元素
  • 哈希表相對于數來說編碼要容易的多,

但是哈希表相對于陣列也有一些不足:

  • 哈希表中的陣列是沒有順序的,所以不能以一種固定的方式(比如從小到大)來遍歷其中的元素,
  • 通常情況下,哈希表中的key是不允許重復的,不能放置相同的key,用于保存不同的元素,

那么,哈希表到底是什么呢?

它的結構是陣列,但是神奇的地方在于對下標值的一種變換,這種變換我們可以稱之為哈希函式,通過哈希函式可以獲得到HashCode
接下來,我們可以來看一個例子:

使用一種資料結構來存盤單詞資訊,比如有50000個單詞,找到單詞后每個單詞有自己的具題應用等等,我們應該怎樣操作呢?

或許我們可以嘗試將字母轉化成合適的下標,但是怎樣才能將一個字符轉化成陣列的下標值呢?有沒有一種方案,可以將單詞轉化成陣列的下標值呢?如果單詞轉化為陣列的下標,以后如果我們要查找某個單詞的資訊,直接按照下標值一步即可訪問到想要的元素,那么怎樣將字串轉化為下標值呢?
其實計算機有很多的編碼方案就是用數字代替單詞的字符,就是字符編碼,當然, 我們可以設計自己的編碼系統,比如a是1,b是2,c是3等等,但是有了編碼系統以后,一個單詞如何轉化成數字呢?
在這里,我們有兩種方案:

方案一:數字相加

一種轉換單詞的簡便方法就是把單詞每個字符的編碼求和
例如單詞cats轉成數字:3+1+20+19=43,那么43就作為cats單詞下標存在陣列中
但是按照這種方案有一個很明顯的問題就是很多單詞最終的下標可能都是43

我們知道陣列中一個下標值位置只能存盤一個資料,如果存入后來的資料,必然會造成資料的覆寫,故而,一個下標存盤這么多單詞顯然是不合理的,

方案二:冪的連乘

其實我們平時用的大于10的數字,就可以用冪的連乘來表示其唯一性,比如:6543 = 6 *103+5 *102+4 *10 + 4
我們的單詞也可以使用這種方案來表示,比如cats = 3 * 273+1 * 272 + 20 * 27+17 = 60337
這樣得到的數字可以基本保證其唯一性,不會和別的單詞重復,
但是存在一個問題,如果一個單詞是zzzzzzzzzz.那么得到的數字超過7000000000000.陣列不一定能夠表示這么大的下標值,就算能夠創建這么大的陣列,事實上有很多的無效單詞,并沒有意義,

兩種方案總結:
第一種方案(把數字相加求和)產生的陣列下標太少,第二種方案(與27的冪相乘求和)產生的陣列下標又太多,
所以現在需要一種壓縮方法,把冪的連乘方案系統中得到的巨大整數范圍壓縮到可接收的陣列范圍中,有一種簡單的方法就是使用取余運算子,他的作用是得到一個數被另一個數整除后的余數,

取余操作的實作:(0-199之間的數字)

  • 假設把從0-199的數字,(large),壓縮為0-9的數字(small)
  • 下標的結果index = large % small
  • 當一個數被10整除時,余數一定在0-9之間
  • 例如16%10 = 6,23%10 = 3
  • 這中間還是會有重復,不過重復的數量明顯小了,比如說在0-199中間取5個數字,放在一個長度為10的陣列,也會重復,但是重復的概率非常小,

二、哈希表的概念

了解了哈希化的原理,我們就可以來看幾個概念:
哈希化:將大數字轉化為陣列范圍內下標的程序,就稱之為哈希化
哈希函式:例如將單詞轉化為大數字,大數字在進行哈希化的代碼實作放在一個函式中,這個函式就是哈希函式,
哈希表:最終將資料插入到這個陣列,對整個結構的封裝,就可以稱之為一個哈希表,

三、哈希化沖突問題

前面提到了,通過哈希化的下標值依然可能會重復,就會導致沖突,比如說我們將0-199的數字選取5個放在長度為10的單元格里,如果我們隨機選出來的是33,45,27,86,92,那么最終他們的位置會是3-5-7-6-2,沒有發生沖突,但是其中如果還有一個86,一個66呢?此時就會發生沖突,該如何解決這個問題呢?
常用的解決方案有兩種:

1、鏈地址法

鏈地址法是一種比較常見的解決沖突的方案(也稱拉鏈法)
如下圖所示:
在這里插入圖片描述

創建了一個記憶體為10的陣列,現在,需要將一些數字存到陣列內部,這些數字哈希化后可能會重復,將下標值相同的數通過鏈表或者陣列鏈接起來的方法叫做鏈地址法,當我們要查找某值的時候,就可以先根據其下標找到對應的鏈表或者陣列再在其內部尋找,
從圖片中,我們可以看出,鏈地址法解決沖突的辦法是每個陣列單元中存盤的不再是單個資料而是一個鏈條,這個鏈條常用的結構是陣列或者鏈條,那在具體應用中應該采用哪一種方式呢?其實這兩種方法都可以,效率上也差不多,當然在某些實作中,會將新插入的資料放在陣列或者鏈表的最前面,這種情況最好用鏈表,因為陣列再首位插入資料是需要所有其他項后移的的,而鏈表就沒有這樣的問題,

2、開放地址法

開放地址法的主要作業方式是尋找空白的單元格來添加重復的資料,如下圖所示:
在這里插入圖片描述
如果有一個數字32,現在要將其插入到陣列中,我們的解決方案為:

  • 新插入的32本來應該插入到52的位置,但是該位置已經包含資料,
  • 可以發現3、5、9的位置是沒有任何內容的
  • 這個時候就可以尋找對應的空白位置來放這個資料

但是探索這個位置的方式不同,有三種方式:

1、線性探索

即線性的查找空白單元
插入32

  • 經過哈希化得到的index=2,但是在插入的時候,發現該位置已經有52
  • 此時就從index+1的位置開始一點點查找合適的位置來放置32
  • 探測到的第一個空的位置就是該值插入的位置

查詢32

  • 首先經過哈希化得到index= 2,比較2的位置結果和查詢的數值是否相同,相同則直接回傳
  • 不相同則線性查找,從index位置+1查找和32一樣的,

需要注意的是:如果32的位置之前沒有插入,并不需要將整個哈希表查詢一遍來確定該值是否存在,而是如果查詢到空位置,就停止,因為32之前不可能跳過空位置去其他的位置,
線性探測也有一個問題就是:如果之前插入的資料是連續插入的,則新插入的資料就需要很長的探測距離,

2、二次探索

二次探索就在線性探索的基礎上進行了優化,
線性探測,我們可以看做是步長為1的探測,比如從下標值x開始,從x+1,x+2,x+3依次探測,
二次探測對步長做了優化,比如從下標值x開始,x+12,x+22,x+32依次探測,
但是二次探測依然存在問題:比如我們連續插入的是32-112-82-42-52,那么他們依次累加的時候步長是相同的,也就是這種情況下會造成步長不一的一種聚集,還是會影響效率,怎樣解決這個問題呢?來看看再哈希法,

3、再哈希法

再哈希法的做法就是:把關鍵字用另一個哈希函式在做一次哈希化,用這次哈希化的結果作為步長,對于指定的關鍵字,步長在整個探測中是不變的,不同的關鍵字使用不同的步長,
第二次哈希化需要具備以下特點:

  • 和第一個哈希函式不同
  • 不能輸出為0(否則,將沒有步長,每次嘆詞都是原地踏步,演算法進入死回圈)

而計算機專家已經設計好了一種作業很好的哈希函式,

stepSize = constant - (key % constant)

其中constant是質數,且小于陣列的容量,key是第一次哈希化得到的值,
例如:stepSize = 5-(key%5),滿足需求,并且結果不可能為0,

四、哈希函式的實作

哈希表的主要優點在于它的速度,提高速度的一個方法就是讓哈希函式中有盡量少的乘法和除法,設計好的哈希函式需要具備以下優點:
(1)快速的計算
(2)均勻的分布
來具體實作一下:
首先我們所實作的哈希函式最主要的操作是:將字符創轉化為比較大的數字和將大的數字壓縮到陣列范圍之內,如下所示:

 function hashFunc(str,size){
            //定義hashCode變數
            var hashCode = 0;
            //根據霍納演算法,計算hashCode的值
            //先將字串轉化為數字編碼
            for(var i =0;i<str.length;i++){
                hashCode = 37*hashCode + str.charCodeAt(i)
            }
            //取余操作
            var index = hashCode % size;
            return index;
        }

代碼測驗:

 console.log( hashFunc('abc',7));
       console.log( hashFunc('cba',7));
       console.log( hashFunc('nba',7));
       console.log( hashFunc('rgt',7));

測驗結果為:
在這里插入圖片描述
可以發現我們得到的字串對應的下標值分布還是很均勻的,

五、封裝哈希表

這里我將采用鏈地址法來實作哈希表:
其中定義了三個屬性:

  • storage:作為陣列,存放相關元素
  • count:記錄當前已存放的資料量
    -limit:標記陣列中一共可以存放多少資料

實作的哈希表(基于storage的陣列)每個index對應的是一個陣列(bucket),bucket里面存放的是(keyvalue),最終哈希表的資料格式是:[[[k,v],[k,v],[k,v],[[k,v],[k,v]],[k,v]]
如下圖所示:
在這里插入圖片描述
代碼如下:

function HashTable(){
     // 定義屬性
     this.storage = [];
     this.count = 0;
     this.limit = 8;
 }

在將我們前面封裝好的哈希函式通過原型添加進去:

function HashTable(){
   // 定義屬性
    this.storage = [];
    this.count = 0;
    this.limit = 8;

 HashTable.prototype.hashFunc = function(str,size){
      //定義hashCode變數
       var hashCode = 0;
       //先將字串轉化為數字編碼
       for(var i =0;i<str.length;i++){
           hashCode = 37*hashCode + str.charCodeAt(i)
       }
       //取余操作
       var index = hashCode % size;
       return index;
   }
}

六、哈希表操作

1、插入&修改操作

哈希表的插入和修改操作是同一個函式,因為當使用者傳入一個<key,value>時,如果原來不存在該key,那么就是插入操作,如果已經存在該key,對應的就是修改操作,
具體實作思路為:先根據傳入的key獲取對應的hashCode,即陣列的index,接著從哈希表的Index位置中取出另一個陣列(bucket),查看上一步的bucket是否為空,如果為空的話,表示之前在該位置沒有放置過任何的內容,則新建一個陣列[];再查看是否之前已經放置過key對應的value,如果放置過,那么就是依次替換操作,而不是插入新的資料,如果不是修改操作的話,那么插入新的資料,并且讓資料項加1,
實作代碼為:

 //插入和修改操作
        HashTable.prototype.put = function(key,value){
            //根據key獲取對應的index
            var index = this.hashFunc(str,this.limit);
            //根據index取出對應的bucket
            var bucket = this.storage[index];
            //如果值為空,給bucket賦值一個陣列
            if(bucket === null){
                bucket = [];
                this.storage[index] = bucket;
            }
            //判斷是否是修改資料
            for(let i =0;i<bucket.length;i++){
                var tuple = bucket[i];
                if(tuple[0] === key){
                    tuple[1] = value;
                    return;
                }
            }
            //進行添加操作
            bucket.push([key,value]);
            this.count += 1;
        }

測驗代碼:

 ht.put('a',12)
 ht.put('b',67)
 ht.put('c',88)
 ht.put('d',66)
 console.log('ht',ht);

列印結果為:
在這里插入圖片描述
測驗成功

2、獲取操作

首先根據key獲取對應的index,在根據對應的index獲取對應的bucket;判斷bucket是否為空,如果為空,回傳null,否則,線性查找bucket中的key和傳入的key是否相等,如果相等,直接回傳對應的value值,否則,直接回傳null
實作代碼為:

HashTable.prototype.get = function(key){
    //根據key獲取對應的index
    var index = this.hashFunc(key,this.limit);
    //根據index獲取對應的bucket
    var bucket = this.storage[index];
    //判斷是否為空
    if(bucket == null){
        return null;
    }
    //線性查找
    for(let i = 0;i<bucket.length;i++){
        var tuple = bucket[i];
        if(tuple[0] === key){
            return tuple[1];
        }
    }
    return null;
}

測驗代碼:比如回去key為d的元素的值

 console.log("ht.get('d'):"+ht.get('d'));

在這里插入圖片描述

3、洗掉操作

方法和獲取操作的方法相似,首先根據key獲取對應的index,在根據對應的index獲取對應的bucket;判斷bucket是否為空,如果為空,回傳null,否則,線性查找bucket中的key和傳入的key是否相等,如果相等,則進行洗掉,否則,直接回傳null

 HashTable.prototype.remove = function(key){
             //根據key獲取對應的index
            var index = this.hashFunc(key,this.limit);
             //根據index獲取對應的bucket
            var bucket = this.storage[index];
            //判斷是否為空
            if(bucket == null){
                return null;
            }
            //線性查找并通過splice()洗掉
            for(let i =0;i<bucket.length;i++){
                var tuple = bucket[i];
                if(tuple[0] === key){
                    bucket.splice(i,1);
                    this.count -= 1;
                    return tuole[1];
                }
            }
            return null;
        }

測驗代碼:洗掉key為b的元素

 console.log("ht.remove('b'):"+ht.remove('b'));

在這里插入圖片描述

4、判斷哈希表是否為空

HashTable.prototype.isEmpty = function(){
            return this.count === 0;
        }

代碼測驗:

 console.log("是否為空:"+ht.isEmpty());

結果:
在這里插入圖片描述

5、獲取哈希表的元素個數

HashTable.prototype.size = function(){
            return this.count;
        }

代碼測驗:

 console.log("哈希表元素個數:"+ht.size());

結果:
在這里插入圖片描述

七、哈希表擴容

1、哈希表擴容思想

上述代碼的封裝中,我們是將所有的資料項放在長度為5的陣列中,因為我們使用的是鏈地址法,所以哈希表當前的資料量和總長度的比值可以大于1,這個哈希表可以無限制的插入新資料,但是,隨著資料量的增多,每一個index對應的bucket會越來越長,也會造成效率的降低,所以,在合適的情況下對陣列進行擴容是很有必要的,那應該如何進行擴容呢?擴容可以簡單的將容量增大兩倍,但是在這種情況下,所有的資料項一定要同時進行修改(重新呼叫哈希函式,獲取到不同的位置)什么情況下擴容呢?比較常見的是:當哈希表當前的資料量和總長度的比值可以大于0.75的時候就可以進行擴容,

2、哈希表擴容實作

實作思路:首先,保存舊的陣列內容,然后重置所有的屬性,遍歷保存的舊陣列的內容,判斷bucket是否為空,為空的話,進行跳過,否則,取出資料,重新插入,實作代碼為:

HashTable.prototype.resize = function(newLimit){
            //保存舊陣列的內容
            var oldStorge = this.storage;
            //重置所有屬性
            this.storage = [];
            this.count = 0;
            this.limit = newLimit;
            //遍歷舊陣列的內容
            for(var i =0;i<oldStorge.length;i++){
                //取出對應的bucket
                var bucket = oldStorge[i];
                //判斷backet是否為空
                if(bucket == null){
                    continue;
                }
                //取出資料重新插入
                for(var j =0;j<bucket.length;j++){
                    var tuple = bucket[j];
                    this.put(tuple[0],tuple[1]);
                }
            }
        }

封裝完成后,每添加一個資料項的時候,就進行是否擴容判斷,需要的話,在進行擴容,代碼為:

if(this.count > this.limit*0.75){
   this.resize(this.limit*2);
     }

那么,有對應的擴大容量,就有對應的縮小容量,當我們洗掉資料項的時候,如果剩余的資料項很小,我們就可以進行縮小容量,代碼如下:

if(this.limit > 5 && this.count < this.limit/2){
    this.resize(Math.floor(this.limit/2))
     }

八、完整代碼

  function HashTable(){
   // 定義屬性
       this.storage = [];
       this.count = 0;
       this.limit = 5;

       HashTable.prototype.hashFunc = function(str,size){
       //定義hashCode變數
       var hashCode = 0;
       //根據霍納演算法,計算hashCode的值
       //先將字串轉化為數字編碼
       for(var i =0;i<str.length;i++){
           hashCode = 37*hashCode + str.charCodeAt(i)
       }
       //取余操作
       var index = hashCode % size;
       return index;
   }
   //插入和修改操作
   HashTable.prototype.put = function(key,value){
       //根據key獲取對應的index
       var index = this.hashFunc(key,this.limit);
       //根據index取出對應的bucket
       var bucket = this.storage[index];
       //如果值為空,給bucket賦值一個陣列
       if(bucket == null){
           bucket = [];
           this.storage[index] = bucket;
       }
       //判斷是否是修改資料
       for(let i =0;i<bucket.length;i++){
           var tuple = bucket[i];
           if(tuple[0] === key){
               tuple[1] = value;
               return;
           }
       }
       //進行添加操作
       bucket.push([key,value]);
       this.count += 1;
       //進行擴容判斷
       if(this.count > this.limit*0.75){
           this.resize(this.limit*2);
       }
   }

   //獲取操作
   HashTable.prototype.get = function(key){
       //根據key獲取對應的index
       var index = this.hashFunc(key,this.limit);
       //根據index獲取對應的bucket
       var bucket = this.storage[index];
       //判斷是否為空
       if(bucket == null){
           return null;
       }
       //線性查找
       for(let i = 0;i<bucket.length;i++){
           var tuple = bucket[i];
           if(tuple[0] === key){
               return tuple[1];
           }
       }
       return null;
   }
   //洗掉操作
   HashTable.prototype.remove = function(key){
        //根據key獲取對應的index
       var index = this.hashFunc(key,this.limit);
        //根據index獲取對應的bucket
       var bucket = this.storage[index];
       //判斷是否為空
       if(bucket == null){
           return null;
       }
       //線性查找并通過splice()洗掉
       for(let i =0;i<bucket.length;i++){
           var tuple = bucket[i];
           if(tuple[0] === key){
               bucket.splice(i,1);
               this.count -= 1;
               return tuple[1];

               //縮小容量
               if(this.limit > 5 && this.count < this.limit/2){
                   this.resize(Math.floor(this.limit/2))
               }
           }
       }
       return null;
   }
   //擴容
   HashTable.prototype.resize = function(newLimit){
       //保存舊陣列的內容
       var oldStorge = this.storage;
       //重置所有屬性
       this.storage = [];
       this.count = 0;
       this.limit = newLimit;
       //遍歷舊陣列的內容
       for(var i =0;i<oldStorge.length;i++){
           //取出對應的bucket
           var bucket = oldStorge[i];
           //判斷backet是否為空
           if(bucket == null){
               continue;
           }
           //取出資料重新插入
           for(var j =0;j<bucket.length;j++){
               var tuple = bucket[j];
               this.put(tuple[0],tuple[1]);
           }
       }
   }
   HashTable.prototype.isEmpty = function(){
       return this.count === 0;
   }
   HashTable.prototype.size = function(){
       return this.count;
   }
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/386619.html

標籤:其他

上一篇:多樂影視電影網站開發——HTML+CSS+JavaScript+PHP

下一篇:【記憶體泄漏和記憶體溢位】JavaScript之深入淺出理解記憶體泄漏和記憶體溢位

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more