Create by jsliang on 2019-07-31 11:22:27
Recently revised in 2019-08-15 16:00:00
數組 - 最簡單的內存數據結構前端
不折騰的前端,和鹹魚有什麼區別git
| 目錄 |
| --- |
| 一 目錄 |
| 二 前言 |
| 2.1 爲何用數組? |
| 2.2 如何建立和初始化數組? |
| 2.3 如何訪問數組? |
| 2.4 二維、三維乃至多維數組以及如何訪問? |
| 2.5 來份案例熱熱身? |
| 2.6 懸難疑惑? |
| 三 數組的增刪改查及其工做應用 |
| 3.1 數組的新增操做 |
| 3.2 數組的刪除和修改操做 |
| 3.2.1 刪除和修改之 splice() |
| 3.2.2 刪除和修改之 slice() |
| 3.2.3 刪除和修改之 filter() |
| 3.3 數組的查詢操做 |
| 四 數組的經常使用知識點 |
| 4.1 push() - 末尾添加元素 |
| 4.2 unshift() - 開頭添加元素 |
| 4.3 pop() - 末尾刪除元素 |
| 4.4 shift() - 開頭刪除元素 |
| 4.5 splice() - 增刪改元素 |
| 4.6 concat() - 拼合兩個數組 |
| 4.7 filter() - 過濾數組元素 |
| 4.8 forEach() - 遍歷操做原數組 |
| 4.9 join() - 數組轉字符串 |
| 4.10 indexOf() - 順序查找第一個 |
| 4.11 lastIndexOf() - 倒序查找第一個 |
| 4.12 map() - 遍歷返回新數組 |
| 4.13 reverse() - 反轉數組元素 |
| 4.14 slice() - 查找指定位置元素 |
| 4.15 sort() - 數組排序 |
| 4.16 toString() - 數組轉字符串 |
| 4.17 includes() - 數組包含某元素 |
| 4.18 fill() - 填充數組 |
| 4.19 reduce() - 數組累計 |
| 4.20 find() - 查找數組元素 |
| 4.21 findIndex() - 查找元素索引 |
| 五 總結 |github
返回目錄
若是小夥伴恰好看到這篇文章,想了解下 算法與數據結構 中,關於 數組 的知識。面試
那麼,但願看到這篇文章的小夥伴擁有:算法
同時,但願看完這篇文章的小夥伴掌握:編程
可是,jsliang 沒法肯定小夥伴是否真具有上面知識點(前置條件),因此前言會簡略介紹下數組,但願能先小科普一下,方便後面共同探討。數組
若是小夥伴已經清楚數組相關基礎知識點,能夠跳過【前言】部分;微信
若是小夥伴不清楚或者想回顧下熱熱身,能夠往下慢慢看。cookie
返回目錄
假設你寫代碼,列出星期一到星期日鍛鍊的時間,你可能會寫成:數據結構
let Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7;
這僅僅是一週的數據。
若是要你統計一年的數據,那樣也太麻煩了,畢竟命名 365 個字段,會讓人極度不適。
因而有了數組:
let exercise = [1, 2, 3, 4, 5, 6, 7];
這不就變得簡便了麼。
返回目錄
let a = []; let b = new Array();
這樣都是可行的。
若是你想初始化有長度的數組,或者數組一開始就有值:
let a = [1, 2, 3]; // [1, 2, 3] let b = new Array(3); // [undefined, undefined, undefined] let c = new Array([1, 2, 3]); // [1, 2, 3] let d = new Array(3).fill(1); // [1, 1, 1] let e = new Array(3).fill([]); // [[], [], []]
固然,後面兩個經過 fill()
建立的數組,推薦碰到 fill()
方法的時候再進一步瞭解。
變量 e 造成的數組會出問題的嗷~
返回目錄
let a = [1, 2, 3]; console.log(arr[0]); // 1 console.log(arr[1]); // 2 console.log(arr[2]); // 3
記住數組的下標是從 0 開始的。
若是某個程序猿跟你表白說你是它心中第 0 位的人……你仍是拒絕 「他」 吧,已經走火入魔沒救了。
返回目錄
咱們平時使用的 [1, 2, 3]
這種形式,稱爲一維數組。
而若是數組中嵌套數組,每嵌套多一層,就加一個維度,例如:
二維數組
let a = [[1, 2, 3], [4, 5]]; // 二維數組 // 訪問二維數組 for (let i = 0; i < a.length; i++) { for (let j = 0; j < a[i].length; j++) { console.log(a[i][j]); } } // 1 // 2 // 3 // 4 // 5
三維數組
let b = [[1, 2, [3, 4]], [5, 6]]; // 三維數組 // 訪問三維數組 for (let i = 0; i < b.length; i++) { for (let j = 0; j < b[i].length; j++) { for (let k = 0; k < b[i][j].length; k++) { console.log(b[i][j][k]); } } } // 3 // 4
至於多維數組,小夥伴能夠自行推算。
返回目錄
在這裏,貼兩份代碼帶你們熱熱身。
遍歷斐波那契數列
const fibonacciSequence = [1, 1, 2, 3, 5, 8, 13]; for (let i = 0; i < fibonacciSequence.length; i++) { console.log(fibonacciSequence[i]); } // 1 1 2 3 5 8 13
實現斐波那契數列
const fibonacciSequence = [1, 1]; for (let i = 2; i < 20; i++) { fibonacciSequence[i] = fibonacciSequence[i - 1] + fibonacciSequence[i - 2]; } console.log(fibonacciSequence); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
返回目錄
數組雖然是最簡單的內存數據結構,可是有些坑仍是須要注意的,以避免深陷沒法自拔,例如:傳值和傳址。
// 傳值 let str1 = '1'; str2 = str1; str2 = '2'; console.log(str1); // 1 console.log(str2); // 2 // 傳址 let arr1 = [1, 2, 3]; arr2 = arr1; arr2[0] = 4; console.log(arr1); // [4, 2, 3] console.log(arr2); // [4, 2, 3]
問:jsliang 你怎麼看傳值和傳址,能不能詳細介紹下?
答:看下我那篇面試文章 jsliang 的 2019 面試準備 或者百度下相關知識點吧,畢竟咱主要目的不是講這個知識點,在這裏引出是爲了讓小夥伴們熱熱身。
若是小夥伴以爲很是有必要將熱身寫成詳細清單,小夥伴記得私信我哈更多的數組問題或者相關隱藏點,仍是等我想起來或者小夥伴提到,我再進行補充吧。
返回目錄
提起 算法與數據結構,不少前端小夥伴可能第一時間反應:「臥槽這有卵用」,「爲啥面試都搞這個,進去後還不是寫 if...else...」……
enm...還真有用。
jsliang 結合數組的增刪改查及其工做中的使用場景,跟你聊聊數組的妙用。
返回目錄
話很少說,關門,放、放代碼:
let arr = [1, 2, 3]; // 經過 push() 方法 arr.push(4); // [1, 2, 3, 4] // 經過 length 屬性 arr[arr.length] = 5; // [1, 2, 3, 4, 5]
數組新增元素的兩種方式:
push()
方法,直接將元素添加到數組最後一位。length
屬性,該屬性能夠顯示數組的長度,而 arr[arr.length]
即給其最大長度後面再加一個長度。既然上面咱們經過兩種方式,往數組後面插入了新元素,那麼有沒有方法,往數組前面插入新元素呢?
有!
let arr = [3, 4, 5]; // 經過 unshift 方法 arr.unshift(2); // [2, 3, 4, 5] // 經過遍歷數組 for (let i = arr.length; i >= 0; i--) { arr[i] = arr[i - 1]; } arr[0] = 1; // [1, 2, 3, 4, 5]
很好,經過這四種方式,咱們就掌握了往數組頭部和數組尾部新增元素。
那麼,在工做中它們有何做用呢?
目前使用最多的是 push()
操做,一般用於給數組新增元素。
舉例常見的場景:給 Table 新增一行數據。
數據內容 | |
---|---|
新增 | 第一行數據 - jsliang |
新增 | 第二行數據 - 梁渣渣 |
jsliang 嘗試本身畫了個表格,過小了很差貼圖,就使用 Markdown 演示下好了。
如上,咱們點擊【新增】按鈕的時候,就能夠直接往 Table 下面新增一行數據。
而若是是 unshift()
,目前工做還未接觸到,若是有碰到的小夥伴,歡迎貼出應用場景~
返回目錄
首先,jsliang 以爲刪除和修改操做在必定程度上利用的數組 API 很是一致,因此貼在一塊兒寫了:
在這裏,咱們介紹三種方法進行操做。
返回目錄
對於前端來講,數組的 splice()
方法是個萬金油,它能適用於新增、修改、刪除這些場景。
那麼,如何利用 splice()
進行新增、修改和刪除呢?
咱們先了解下 splice()
特性:
var months = ['Jan', 'March', 'April', 'June']; // 新增操做 months.splice(1, 0, 'Feb'); // ['Jan', 'Feb', 'March', 'April', 'June'] // 修改操做 months.splice(4, 1, 'May'); // ['Jan', 'Feb', 'March', 'April', 'May'] // 刪除操做 months.splice(4, 1); // ['Jan', 'Feb', 'March', 'April']
如上,splice()
的語法是:array.splice(start, deleteCount, item1, item2, ...)
。
start
爲數組的開始座標deleteCount
爲須要從開始座標起,刪除的個數,能夠爲 0,表明不刪除item
爲須要新增進去的元素。那麼,講到這裏,小夥伴們應該對 splice()
有深入瞭解了。
下面咱們講講 splice()
對於修改操做,在工做中的使用場景:
修改 Table 某行的數據
let list = [ { id: '1', name: 'jsliang' }, { id: '2', name: '梁峻榮' }, { id: '3', name: 'JavaScriptLiang' }, ]; function addData(rowIndex) { list.splice(rowIndex, 0, { id: '4', name: '梁渣渣', }); } addData(1); console.log(list); // [ // { id: '1', name: 'jsliang' }, // { id: '4', name: '梁渣渣' }, // { id: '2', name: '梁峻榮' }, // { id: '3', name: 'JavaScriptLiang' }, // ]
如上,咱們但願將數據添加到指定位置(addData(n)
),這時候咱們就使用了 splice()
對其進行操做,從而修改了原數組。
返回目錄
而做爲和 splice()
一個字母之差的 slice()
,又是何等的優秀呢?
首先,我們科普下:
什麼意思呢?相信不少小夥伴在網上看數組相關攻略的時候,都會看到一堆的區分:這個方法會影響到原數組,這個方法不會影響到原數組……等等。
其實很容易理解:我想吃兩塊蛋糕,可是如今我只有一塊蛋糕。若是我還惦記着本身的減肥,那麼我能夠將這款蛋糕切成兩塊,這樣我就吃到兩塊蛋糕(影響到原數組);若是我以爲吃了兩塊蛋糕不會肥,那我就去照着這塊蛋糕的樣子,再買一塊(不影響原數組)。
固然,這裏都說是修改咯,slice()
仍是有丟丟影響到數組的:
const str = 'jsliang'; str.slice(0, 2); // 'js' str.slice(2); // 'liang'
對於 slice()
來講,它的參數爲:str.slice(beginSlice, endSlice)
。其中:
beginSlice
:從該索引(以 0 爲基數)處開始提取原字符串中的字符。endSlice
:結束位置(以 0 爲基數),若是不傳,默認到數組末尾。注意,splice()
的第二個參數是影響到數組的個數,而slice()
的第二個參數是結束的位置,因此slice()
通常寫法是:slice(index, index + length)
,即須要修改的位置(index),及其影響的長度(length)。
很好,說完這一堆廢話,我們講講工做中的使用場景:
let list = [ { id: '1', name: 'jsliang' }, { id: '2', name: '梁峻榮' }, ]; function insertData(rowId) { list = [ ...list.slice(0, list.findIndex(item => item.id === rowId) + 1), { id: '3', name: '', }, ...list.slice(list.findIndex(item => item.id === rowId) + 1), ] } insertData('1'); console.log(list); // [ // { id: '1', name: 'jsliang' }, // { id: '3', name: '' }, // { id: '2', name: '梁峻榮' }, // ]
還記得在上面咱們說過用 slice()
作修改操做,也會影響到原數組 嗎?是的,在這份代碼咱們能夠看出,咱們根據以前的數組,組合成一個新的數組,讓這個元素指向了新的數組地址。
固然,這個並不重要,咱們講講它的使用場景:在須要修改 Table 某行的時候,咱們將其惟一值(id)傳遞了過去,而後方法 insertData
根據傳遞的 id 找到那一行,對其進行了修改。
若是你感受並不是那麼容易理解,你能夠嘗試下將rowId
換成index
,這是個明智的選擇。
返回目錄
首先,jsliang 對於 filter()
這個方法也不是很常使用:
我有個朋友,網名就叫 filter()
,每次使用它就跟使喚我朋友同樣:「filter!我有事請你幫忙~」
可是,做爲團隊的一枚 螺絲釘,業務寫得多了,你仍是會接觸到同事的代碼的,因此仍是有必要對其進行了解:
function isBigEnough(element) { return element >= 10; } var filtered = [12, 5, 8, 130, 44].filter(isBigEnough); // [12, 130, 44] // 若是你喜歡用 ES6 的箭頭函數 const number = [12, 5, 8, 130, 44]; const filterNumber = number.filter(item => item >= 10); // [12, 130, 44]
很好,講到這裏,咱們就順帶科普下 filter()
這個方法了:
arr.filter(callback)
callback
:用來測試數組的每一個元素的函數。返回 true
表示該元素經過測試,保留該元素,false
則不保留。它會返回由經過元素組成的數組,或者是一個空數組 []。它接受如下三個參數:
element
:數組中當前正在處理的元素index
:正在處理的元素在數組中的索引。array
:調用了 filter
的數組自己。因此一個比較完整的 filter()
能夠這麼寫:
const number = [12, 5, 8, 130, 44]; const filterNumber = number.filter((item, index, array) => { console.log(array); return item >= 10 && index > 3; }); // 輸出 5 次 [12, 5, 8, 130, 44] // 最終 filterNumber 的值爲 [44]
OK,介紹完畢,我們看下應用場景:
let list = [ { id: '1', name: 'jsliang' }, { id: '2', name: '梁峻榮' }, { id: '3', name: 'JavaScriptLiang' }, ]; function changeData(id, newObj) { list = [...list.filter(item => item.id !== id), newObj]; } changeData('2', { id: '4', name: '梁渣渣', }); [ {id: "1", name: "jsliang"}, {id: "3", name: "JavaScriptLiang"}, {id: "4", name: "梁渣渣"}, ]
這樣,咱們就將 id
爲 2 的 梁峻榮
那行修改成 id
爲 4 的 梁渣渣
。
返回目錄
上面講完了新增、刪除和修改,最後仍是講講查詢操做。
相比於上面的話題,查詢的形式就多了。
例如說,我想知道數組中全部對象的 id:
let list = [ { id: '1', name: 'jsliang' }, { id: '2', name: '梁峻榮' }, { id: '3', name: 'JavaScriptLiang' }, ]; const ids = list.map(item => item.id); // ["1", "2", "3"]
無可厚非這也是一種查詢,並且在工做中特實用,畢竟像 Ant Design 等,下拉框多選等狀況,就須要將數據的 id 查找出來。
又或者說,我想知道 JavaScript
出如今哪一個索引值下,它的 id 是多少:
let list = [ { id: '1', name: 'jsliang' }, { id: '2', name: '梁峻榮' }, { id: '3', name: 'JavaScriptLiang' }, ]; const id1 = list[list.findIndex(item => item.name === 'JavaScriptLiang')].id; // '3' // 固然有更快速的 const id2 = list.find(item => item.name === 'JavaScriptLiang').id; // '3'
固然,JavaScript 還有不少操做,能夠查詢數組中的數據。
固然,無論怎麼說,jsliang 仍是強烈推薦將這些方法記下來,而後在工做中不停嘗試使用,這樣你纔會有所提高。
返回目錄
下面開始無聊的 五年高考三年模擬 試題訓練,但願小夥伴能在瞭解某個方法以後,經過其下面的 LeetCode 進行相應訓練,從而熟練掌握數組的經常使用知識點。
返回目錄
push()
方法將一個或多個元素添加到數組的末尾,並返回該數組的新長度。
let arr = [1, 2, 3]; // 經過 push() 方法 arr.push(4); // [1, 2, 3, 4]
實戰 LeetCode:
返回目錄
unshift()
方法將一個或多個元素添加到數組的開頭,並返回該數組的新長度。
let arrA = ['1']; arrA.unshift('0'); console.log(arrA); // ['0', '1'] let arrB = [4, 5, 6]; arrB.unshift(1, 2, 3); console.log(arrB); // [1, 2, 3, 4, 5, 6]
實戰 LeetCode:
返回目錄
pop()
方法從數組中刪除最後一個元素,並返回該元素的值。此方法更改數組的長度。
let arr = [1, 2, 3, 4]; for(let i = 0, time = 1; i < arr.length; time++) { console.log(`------\n第 ${time} 次遍歷:`); console.log(arr.pop()); console.log(arr); } /* Console: ------ 第 1 次遍歷: 4 [ 1, 2, 3 ] ------ 第 2 次遍歷: 3 [ 1, 2 ] ------ 第 3 次遍歷: 2 [ 1 ] ------ 第 4 次遍歷: 1 [] */
實戰 LeetCode:
返回目錄
shift()
方法從數組中刪除第一個元素,並返回該元素的值。此方法更改數組的長度。
let str = [1, 2, 3]; console.log(str.shift()); // 1 console.log(str.shift()); // 2 console.log(str.shift()); // 3 console.log(str.shift()); // undefined
實戰 LeetCode:
返回目錄
splice()
方法經過刪除或替換現有元素或者原地添加新的元素來修改數組,並以數組形式返回被修改的內容。此方法會改變原數組。
var months = ['Jan', 'March', 'April', 'June']; months.splice(1, 0, 'Feb'); console.log(months); // ['Jan', 'Feb', 'March', 'April', 'June'] months.splice(4, 1, 'May'); console.log(months); // ['Jan', 'Feb', 'March', 'April', 'May']
實戰 LeetCode:
返回目錄
concat()
方法用於合併兩個或多個數組。此方法不會更改現有數組,而是返回一個新數組。
const newArr = [1, 2, 3].concat(['a', 'b', 'c']); // [1, 2, 3, 'a', 'b', 'c']
實戰 LeetCode:
返回目錄
filter()
方法建立一個新數組, 其包含經過所提供函數實現的測試的全部元素。
function isBigEnough(element) { return element >= 10; } var filtered = [12, 5, 8, 130, 44].filter(isBigEnough); // [12, 130, 44]
實戰 LeetCode:
返回目錄
forEach()
方法對數組的每一個元素執行一次提供的函數。
const items = ['item1', 'item2', 'item3']; const copy = []; // 使用 for 遍歷 for (let i = 0; i < items.length; i++) { copy.push(items[i]); } // 使用 forEach 遍歷 items.forEach(function(item){ copy.push(item); });
實戰 LeetCode:
返回目錄
join()
方法將一個數組(或一個類數組對象)的全部元素鏈接成一個字符串並返回這個字符串。
var a = ['Wind', 'Rain', 'Fire']; var myVar1 = a.join(); // myVar1 的值變爲 "Wind,Rain,Fire" var myVar2 = a.join(', '); // myVar2的值變爲"Wind, Rain, Fire" var myVar3 = a.join(' + '); // myVar3的值變爲"Wind + Rain + Fire" var myVar4 = a.join(''); // myVar4的值變爲"WindRainFire"
實戰 LeetCode:
返回目錄
indexOf()
方法返回調用 String 對象中第一次出現的指定值的索引。
'I am jsliang'.indexOf('a', 4); // 9 [1, 3, 1, 4].indexOf(1, 1); // 2 '怪盜 jsliang'.indexOf('我'); // -1
實戰 LeetCode:
返回目錄
lastIndexOf()
方法返回指定元素(也即有效的 JavaScript 值或變量)在數組中的最後一個的索引,若是不存在則返回 -1。
var array = [2, 5, 9, 2]; var index = array.lastIndexOf(2); // 3
實戰 LeetCode:
暫無題目
返回目錄
map()
方法建立一個新數組,其結果是該數組中的每一個元素都調用一個提供的函數後返回的結果。
[1, 2, 3, 4].map(item => item * 2) // [2, 4, 6, 8] [{ name: 'jsliang', age: 24, }, { name: '梁峻榮', age: 124 }].map((item, index) => { return `${index} - ${item.name}`; }) // ['0 - jsliang', '1 - 梁峻榮']
實戰 LeetCode:
返回目錄
reverse()
方法將數組中元素的位置顛倒,並返回該數組。該方法會改變原數組。
let arr = [1, 2, 3]; arr.reverse(); console.log(arr); // [3, 2, 1]
實戰 LeetCode:
返回目錄
slice()
方法提取一個字符串的一部分,並返回一新的字符串。
const str = 'jsliang'; str.slice(0, 2); // 'js' str.slice(2); // 'liang'
實戰 LeetCode:
返回目錄
sort()
對數組的元素進行排序,並返回數組。
[4, 2, 5, 1, 3].sort(), // [1, 2, 3, 4, 5] [4, 2, 5, 1, 3].sort((a, b) => a < b), // [5, 4, 3, 2, 1] ['a', 'd', 'c', 'b'].sort(), // ['a', 'b', 'c', 'd'] ['jsliang', 'eat', 'apple'].sort(), // ['apple', 'eat', 'jsliang']
實戰 LeetCode:
返回目錄
toString()
返回一個字符串,表示指定的數組及其元素。
let arr = [1, 2, 3]; arr.toString(); // '1,2,3'
實戰 LeetCode:
返回目錄
includes()
方法用來判斷一個數組是否包含一個指定的值,根據狀況,若是包含則返回 true,不然返回 false。
[1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false [1, 2, 3].includes(3, 3); // false [1, 2, 3].includes(3, -1); // true [1, 2, NaN].includes(NaN); // true
實戰 LeetCode:
返回目錄
fill()
方法用一個固定值填充一個數組中從起始索引到終止索引內的所有元素。不包括終止索引。
let arr = [1, 2, 3, 4, 5]; arr = new Array(arr.length).fill(0); // arr - [0, 0, 0, 0, 0];
實戰 LeetCode:
返回目錄
reduce()
方法對數組中的每一個元素執行一個由您提供的reducer函數(升序執行),將其結果彙總爲單個返回值。
[1, 2, 3, 4].reduce((prev, next) => { return prev + next; }); // 10 ['前端', 'pang', 'liang'].reduce((prev, next, index) => { return (index = 0 ? '-js' : '') + prev + 'js' + next; }); // 前端-jspang-jsliang
實戰 LeetCode:
返回目錄
find()
方法返回數組中知足提供的測試函數的第一個元素的值。不然返回 undefined。
var inventory = [ {name: 'apples', quantity: 2}, {name: 'bananas', quantity: 0}, {name: 'cherries', quantity: 5} ]; function findCherries(fruit) { return fruit.name === 'cherries'; } inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }
實戰 LeetCode:
暫無實戰
返回目錄
findIndex()
方法返回數組中知足提供的測試函數的第一個元素的索引。不然返回-1。
var array1 = [5, 12, 8, 130, 44]; function isLargeNumber(element) { return element > 13; } array1.findIndex(isLargeNumber); // 3
實戰 LeetCode:
暫無實戰
返回目錄
通過前面一系列的折騰,咱們基本對數組的各類操做有所瞭解,雖然談不上 精通,可是支持平常工做是毫無問題的了。
寫到這裏,jsliang 查詢了下字數:36800。
是的,路漫漫其修遠兮,就連我一開始寫這篇文章的時候,我也沒想到它有這麼多的內容能夠寫,並且仍是沒有將一些 ES六、ES7 的數組新特性加上的狀況下篇幅。
數組 - 最簡單的內存數據結構,也是工做中最常使用的數據結構。
若是你以爲 jsliang 寫得很 OK,歡迎點贊、留言、加微信好友、關注微信公衆號等。
我們,將繼續探索,紮實編程基礎,瞭解更多的數據結構和算法!
全部聯繫方式盡在: jsliang 的文檔庫
不折騰的前端,和鹹魚有什麼區別!
jsliang 會天天更新一道 LeetCode 題解,從而幫助小夥伴們夯實原生 JS 基礎,瞭解與學習算法與數據結構。
掃描上方二維碼,關注 jsliang 的公衆號,讓咱們一塊兒折騰!
<img alt="知識共享許可協議" style="border-width:0" src=" https://i.creativecommons.org...; />
<span xmlns:dct=" http://purl.org/dc/terms/" property="dct:title">jsliang 的文檔庫</span> 由 梁峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於 https://github.com/LiangJunro...上的做品創做。
本許可協議受權以外的使用權限能夠從 https://creativecommons.org/l... 處得到。