【面試題】643- 推薦 48 道JavaScript基礎面試題

作者:劉寧Leo 

來源 | https://segmentfault.com/a/1190000015288700

1、 介紹JavaScript的基本數據類型
Number、String 、Boolean 、Null、Undefined
Object 是 JavaScript 中所有對象的父對象
數據封裝類對象:Object、Array、Boolean、Number 和 String
其他對象:Function、Arguments、Math、Date、RegExp、Error
新類型:Symbol

2、 說說寫JavaScript的基本規範?
1) 不要在同一行聲明多個變量
2) 使用 ===或!==來比較true/false或者數值
3) switch必須帶有default分支
4) 函數應該有返回值
5) for if else 必須使用大括號
6) 語句結束加分號
7) 命名要有意義,使用駝峯命名法

3、 jQuery使用建議
1) 儘量減少對dom元素的訪問和操作
2) 儘量避免給dom元素綁定多個相同類型的事件處理函數,可以將多個相同類型事件
處理函數合併到一個處理函數,通過數據狀態來處理分支
3) 儘量避免使用toggle事件

4、 Ajax使用
全稱 :Asynchronous Javascript And XML
所謂異步,就是向服務器發送請求的時候,我們不必等待結果,而是可以同時做其他的事情,等到有了結果它自己會根據設定進行後續操作,與此同時,頁面是不會發生整頁刷新的,提高了用戶體驗。
創建Ajax的過程:
1) 創建XMLHttpRequest對象(異步調用對象)

var xhr = new XMLHttpRequest();

2) 創建新的Http請求(方法、URL、是否異步)

xhr.open(‘get’,’example.php’,false);

3) 設置響應HTTP請求狀態變化的函數。
onreadystatechange事件中readyState屬性等於4。響應的HTTP狀態爲200(OK)或者304(Not Modified)。
4) 發送http請求

xhr.send(data);

5) 獲取異步調用返回的數據
注意:
1) 頁面初次加載時,儘量在web服務器一次性輸出所有相關的數據,只在頁面加載完成之後,用戶進行操作時採用ajax進行交互。
2) 同步ajax在IE上會產生頁面假死的問題。所以建議採用異步ajax。
3) 儘量減少ajax請求次數
4) ajax安全問題,對於敏感數據在服務器端處理,避免在客戶端處理過濾。對於關鍵業務邏輯代碼也必須放在服務器端處理。

5、 JavaScript有幾種類型的值?你能畫一下他們的內存圖嗎?
基本數據類型存儲在棧中,引用數據類型(對象)存儲在堆中,指針放在棧中。
兩種類型的區別是:存儲位置不同;原始數據類型直接存儲在棧中的簡單數據段,佔據空間小、大小固定,屬於被頻繁使用數據,所以放入棧中存儲;引用數據類型存儲在堆中的對象,佔據空間大、大小不固定,如果存儲在棧中,將會影響程序運行的性能
引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。

6、 棧和堆的區別?
棧(stack):由編譯器自動分配釋放,存放函數的參數值,局部變量等;
堆(heap):一般由程序員分配釋放,若程序員不釋放,程序結束時可能由操作系統釋放。

7、 Javascript實現繼承的幾種方式

JavaScript實現繼承的3種方法

1)、借用構造函數法(又叫經典繼承)

function SuperType(name) {
    this.name = name;
    this.sayName = function() {        window.alert(this.name);    };}        function SubType(name, age) {
    SuperType.call(this, name); //在這裏借用了父類的構造函數
    this.age = age;}

2)、對象冒充

function SuperType(name) {
    this.name = name;        this.sayName = function() {        window.alert(this.name);    };}        function SubType(name, age) {
    this.supertype = SuperType; //在這裏使用了對象冒充    this.supertype(name);
    this.age = age;}

3)、組合繼承(最常用)

function SuperType(name) {
    this.name = name;
}        SuperType.prototype = {            sayName : function() {        window.alert(this.name);    }};        function SubType(name, age) {        SuperType.call(this, name); //在這裏繼承屬性    this.age = age;}
SubType.prototype = new SuperType(); //這裏繼承方法

8 、Javascript創建對象的幾種方式?

JavaScript定義類的4種方法

1)、工廠方法

function creatPerson(name, age) {                var obj = new Object();
    obj.name = name;    obj.age = age;
    obj.sayName = function() {        window.alert(this.name);    };                return obj;}

2)、構造函數方法

function Person(name, age) {
    this.name = name;    this.age = age;
    this.sayName = function() {        window.alert(this.name);    };}

3)、原型方法

function Person() {        }        Person.prototype = {    constructor : Person,    name : "Ning",    age : "23",    sayName : function() {        window.alert(this.name);    }};

大家可以看到這種方法有缺陷,類裏屬性的值都是在原型裏給定的。

4)、組合使用構造函數和原型方法(使用最廣)

function Person(name, age) {    this.name = name;    this.age = age;}        Person.prototype = {    constructor : Person,    sayName : function() {        window.alert(this.name);    }};

將構造函數方法和原型方法結合使用是目前最常用的定義類的方法。這種方法的好處是實現了屬性定義和方法定義的分離。比如我可以創建兩個對象person1person2,它們分別傳入各自的name值和age值,但sayName()方法可以同時使用原型裏定義的。

9、Javascript作用鏈域
作用域鏈的原理和原型鏈很類似,如果這個變量在自己的作用域中沒有,那麼它會尋找父級的,直到最頂層。
注意:JS沒有塊級作用域,若要形成塊級作用域,可通過(function(){})();立即執行的形式實現。

10、 談談this的理解
1) this總是指向函數的直接調用者(而非間接調用者)
2) 如果有new關鍵字,this指向new出來的那個對象
3) 在事件中,this指向目標元素,特殊的是IE的attachEvent中的this總是指向全局對象window。

11、 eval是做什麼的?
它的功能是把對應的字符串解析成JS代碼並運行;應該避免使用eval,不安全,非常耗性能(2次,一次解析成js語句,一次執行)。

12、 什麼是window對象? 什麼是document對象?
window對象代表瀏覽器中打開的一個窗口。document對象代表整個html文檔。實際上,document對象是window對象的一個屬性。

13、 null,undefined的區別?
null表示一個對象被定義了,但存放了空指針,轉換爲數值時爲0。
undefined表示聲明的變量未初始化,轉換爲數值時爲NAN。
typeof(null) -- object;
typeof(undefined) -- undefined

14、 ["1", "2", "3"].map(parseInt) 答案是多少?
[1,NaN,NaN]

解析:
Array.prototype.map()
array.map(callback[, thisArg])
callback函數的執行規則
參數:自動傳入三個參數
currentValue(當前被傳遞的元素);
index(當前被傳遞的元素的索引);
array(調用map方法的數組)

parseInt方法接收兩個參數
第三個參數["1", "2", "3"]將被忽略。parseInt方法將會通過以下方式被調用
parseInt("1", 0)
parseInt("2", 1)
parseInt("3", 2)

parseInt的第二個參數radix爲0時,ECMAScript5將string作爲十進制數字的字符串解析;
parseInt的第二個參數radix爲1時,解析結果爲NaN;
parseInt的第二個參數radix在2—36之間時,如果string參數的第一個字符(除空白以外),不屬於radix指定進制下的字符,解析結果爲NaN。
parseInt("3", 2)執行時,由於"3"不屬於二進制字符,解析結果爲NaN。

15、關於事件,IE與火狐的事件機制有什麼區別?如何阻止冒泡?
IE爲事件冒泡,Firefox同時支持事件捕獲和事件冒泡。但並非所有瀏覽器都支持事件捕獲。jQuery中使用event.stopPropagation()方法可阻止冒泡;(舊IE的方法 ev.cancelBubble = true;

16、 什麼是閉包(closure),爲什麼要用它?
閉包指的是一個函數可以訪問另一個函數作用域中變量。常見的構造方法,是在一個函數內部定義另外一個函數。內部函數可以引用外層的變量;外層變量不會被垃圾回收機制回收。
注意,閉包的原理是作用域鏈,所以閉包訪問的上級作用域中的變量是個對象,其值爲其運算結束後的最後一個值。
優點:避免全局變量污染。缺點:容易造成內存泄漏。
例子:

function makeFunc() {    var name = "Mozilla";    function displayName() {        console.log(name);    }    return displayName;}var myFunc = makeFunc();myFunc();   //輸出Mozilla

myFunc 變成一個 閉包。閉包是一種特殊的對象。它由兩部分構成:函數,以及創建該函數的環境。環境由閉包創建時在作用域中的任何局部變量組成。在我們的例子中,myFunc 是一個閉包,由 displayName 函數和閉包創建時存在的 "Mozilla" 字符串形成。

17、javascript 代碼中的"use strict";是什麼意思 ? 使用它區別是什麼?
除了正常模式運行外,ECMAscript添加了第二種運行模式:“嚴格模式”。
作用:
1) 消除js不合理,不嚴謹地方,減少怪異行爲
2) 消除代碼運行的不安全之處,
3) 提高編譯器的效率,增加運行速度
4) 爲未來的js新版本做鋪墊。

18、 如何判斷一個對象是否屬於某個類?
使用instanceof 即if(a instanceof Person){alert('yes');}

19、 new操作符具體幹了什麼呢?
1) 創建一個空對象,並且 this 變量引用該對象,同時還繼承了該函數的原型。
2) 屬性和方法被加入到 this 引用的對象中。
3) 新創建的對象由 this 所引用,並且最後隱式的返回 this 。

20、 Javascript中,執行時對象查找時,永遠不會去查找原型的函數?
Object.hasOwnProperty(proName):是用來判斷一個對象是否有你給出名稱的屬性。不過需要注意的是,此方法無法檢查該對象的原型鏈中是否具有該屬性,該屬性必須是對象本身的一個成員。

21、 對JSON的瞭解?
全稱:JavaScript Object Notation
JSON中對象通過“{}”來標識,一個“{}”代表一個對象,如{“AreaId”:”123”},對象的值是鍵值對的形式(key:value)。JSON是JS的一個嚴格的子集,一種輕量級的數據交換格式,類似於xml。數據格式簡單,易於讀寫,佔用帶寬小。
兩個函數:
JSON.parse(str)
解析JSON字符串 把JSON字符串變成JavaScript值或對象
JSON.stringify(obj)
將一個JavaScript值(對象或者數組)轉換爲一個 JSON字符串
eval('('+json+')')
用eval方法注意加括號 而且這種方式更容易被攻擊

22、 JS延遲加載的方式有哪些?
JS的延遲加載有助與提高頁面的加載速度。
defer和async、動態創建DOM方式(用得最多)、按需異步載入JS
defer:延遲腳本。立即下載,但延遲執行(延遲到整個頁面都解析完畢後再運行),按照腳本出現的先後順序執行。
async:異步腳本。下載完立即執行,但不保證按照腳本出現的先後順序執行。

23、 同步和異步的區別?
同步的概念在操作系統中:不同進程協同完成某項工作而先後次序調整(通過阻塞、喚醒等方式),同步強調的是順序性,誰先誰後。異步不存在順序性。
同步:瀏覽器訪問服務器,用戶看到頁面刷新,重新發請求,等請求完,頁面刷新,新內容出現,用戶看到新內容之後進行下一步操作。
異步:瀏覽器訪問服務器請求,用戶正常操作,瀏覽器在後端進行請求。等請求完,頁面不刷新,新內容也會出現,用戶看到新內容。

24、 什麼是跨域問題 ,如何解決跨域問題?

什麼是跨域?

要明白什麼是跨域之前,首先要明白什麼是同源策略

同源策略就是用來限制從一個源加載的文檔或腳本與來自另一個源的資源進行交互。那怎樣判斷是否是同源呢?

如果協議,端口(如果指定了)和主機對於兩個頁面是相同的,則兩個頁面具有相同的源,也就是同源。也就是說,要同時滿足以下3個條件,才能叫同源:

  1. 協議相同

  2. 端口相同

  3. 主機相同

舉個例子就一目瞭然了:

我們來看下面的頁面是否與 http://store.company.com/dir/index.html 是同源的?

  1. http://store.company.com/dir/index2.html 同源

  2. http://store.company.com/dir2/index3.html 同源 雖然在不同文件夾下

  3. https://store.company.com/secure.html 不同源 不同的協議(https)

  4. http://store.company.com:81/dir/index.html 不同源 不同的端口(81)

  5. http://news.company.com/dir/other.html 不同源 不同的主機(news)

跨域的幾種解決方案

1)、document.domain方法

我們來看一個具體場景:有一個頁面 http://www.example.com/a.html ,它裏面有一個iframe,這個iframe的源是 http://example.com/b.html ,很顯然它們是不同源的,所以我們無法在父頁面中操控子頁面的內容。

解決方案如下:

<!-- b.html --><script>document.domain = 'example.com';</script>
<!-- a.html --><script>document.domain = 'example.com';var iframe = document.getElementById('iframe').contentWindow.document;
//後面就可以操作iframe裏的內容了...</script>

所以我們只要將兩個頁面的document.domain設置成一致就可以了,要注意的是,document.domain的設置是有限制的,我們只能把document.domain設置成自身或更高一級的父域。

但是,這種方法只能解決主域相同的跨域問題。

2)、window.name方法

window對象有個name屬性,該屬性有個特徵:即在一個窗口(window)的生命週期內,窗口載入的所有的頁面都是共享一個window.name的,每個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的所有頁面中的,並不會因新頁面的載入而進行重置。

我們來看一個具體場景,在一個頁面 example.com/a.html 中,我們想獲取 data.com/data.html 中的數據,以下是解決方案:

<!-- data.html --><script>window.name = 'data'; //這是就是我們需要通信的數據</script>
<!-- a.html --><html><head><script>    function getData () {        var iframe = document.getElementById('iframe');        iframe.src = 'example.com/b.html'; // 這裏讓iframe與父頁面同源                iframe.onload = function () {            var data = iframe.contentWindow.name; //在這裏我們得到了跨域頁面中傳來的數據        };    }</script></head><body></body></html>

3)、JSONP方法

JONSP(JSON with Padding)是JSON的一種使用模式。基本原理如下:

<!-- a.html --><script>    function dealData (data) {        console.log(data);    }</script>
<script src='http://example.com/data.php?callback=dealData'></script>
<?php    $callback = $_GET['callback'];    $data = 'data';    echo $callback.'('.json_encode($data).')';?>

這時候在a.html中我們得到了一條js的執行語句dealData('data'),從而達到了跨域的目的。

所以JSONP的原理其實就是利用引入script不限制源的特點,把處理函數名作爲參數傳入,然後返回執行語句,仔細閱讀以上代碼就可以明白裏面的意思了。

如果在jQuery中用JSONP的話就更加簡單了:

<script>$.getJSON(''http://example.com/data.php?callback=?', function (data) {    console.log(data);});</script>

注意jQuery會自動生成一個全局函數來替換callback=?中的問號,之後獲取到數據後又會自動銷燬,實際上就是起一個臨時代理函數的作用。$.getJSON方法會自動判斷是否跨域,不跨域的話,就調用普通的ajax方法;跨域的話,則會以異步加載js文件的形式來調用JSONP的回調函數。

25、 頁面編碼和被請求的資源編碼如果不一致如何處理?
若請求的資源編碼,如外引js文件編碼與頁面編碼不同。可根據外引資源編碼方式定義爲 charset="utf-8"或"gbk"。
比如:http://www.yyy.com/a.html 中嵌入了一個http://www.xxx.com/test.js
a.html 的編碼是gbk或gb2312的。而引入的js編碼爲utf-8的 ,那就需要在引入的時候
<script src="http://www.xxx.com/test.js&quot; charset="utf-8"></script>

26、 模塊化開發怎麼做?
模塊化開發指的是在解決某一個複雜問題或者一系列問題時,依照一種分類的思維把問題進行系統性的分解。模塊化是一種將複雜系統分解爲代碼結構更合理,可維護性更高的可管理的模塊方式。對於軟件行業:系統被分解爲一組高內聚,低耦合的模塊。
(1)定義封裝的模塊
(2)定義新模塊對其他模塊的依賴
(3)可對其他模塊的引入支持。在JavaScript中出現了一些非傳統模塊開發方式的規範。CommonJS的模塊規範,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等。AMD是異步模塊定義,所有的模塊將被異步加載,模塊加載不影響後邊語句運行。

27、 AMD(Modules/Asynchronous-Definition)、CMD(Common Module Definition)規範區別?
AMD 是 RequireJS 在推廣過程中對模塊定義的規範化產出。CMD 是 SeaJS 在推廣過程中對模塊定義的規範化產出。
區別:
1) 對於依賴的模塊,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。
2) CMD 推崇依賴就近,AMD 推崇依賴前置。
3) AMD 的 API 默認是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。

// CMD
define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    // 此處略去 100 行
    var b = require('./b') // 依賴可以就近書寫
    b.doSomething()
})
// AMD 默認推薦
define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好
    a.doSomething();
    // 此處略去 100 行
    b.doSomething();
})

28、requireJS的核心原理是什麼?(如何動態加載的?如何避免多次加載的?如何緩存的?)
核心是js的加載模塊,通過正則匹配模塊以及模塊的依賴關係,保證文件加載的先後順序,根據文件的路徑對加載過的文件做了緩存。

29、 call和apply
call()方法和apply()方法的作用相同,動態改變某個類的某個方法的運行環境。他們的區別在於接收參數的方式不同。在使用call()方法時,傳遞給函數的參數必須逐個列舉出來。使用apply()時,傳遞給函數的是參數數組。

30、 documen.write和 innerHTML的區別
document.write()只能重繪整個頁面

setTimeout(function(){
       document.write('<p>5 secs later</p>');
}, 5000);

window.onload = function() { document.write("HI");

innerHTML可以重繪頁面的一部分

31、 迴流與重繪
當渲染樹中的一部分(或全部)因爲元素的規模尺寸,佈局,隱藏等改變而需要重新構建。這就稱爲迴流(reflow)。每個頁面至少需要一次迴流,就是在頁面第一次加載的時候。在迴流的時候,瀏覽器會使渲染樹中受到影響的部分失效,並重新構造這部分渲染樹。完成迴流後,瀏覽器會重新繪製受影響的部分到屏幕中,該過程成爲重繪

32、 DOM操作
(1)創建新節點
createDocumentFragment() //創建一個DOM片段
createElement() //創建一個具體的元素
createTextNode() //創建一個文本節點
(2)添加、移除、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子節點前插入一個新的子節點
(3)查找
getElementsByTagName() //通過標籤名稱
getElementsByName() //通過元素的Name屬性的值(IE容錯能力較強,會得到一個數組,其中包括id等於name值的)
getElementById() //通過元素Id,唯一性

33、 數組對象有哪些原生方法,列舉一下
pop、push、shift、unshift、splice、reverse、sort、concat、join、slice、toString、indexOf、lastIndexOf、reduce、reduceRight
forEach、map、filter、every、some

34、 那些操作會造成內存泄漏
全局變量、閉包、DOM清空或刪除時,事件未清除、子元素存在引用

35、 什麼是Cookie 隔離?(或者:請求資源的時候不要帶cookie怎麼做)
通過使用多個非主要域名來請求靜態文件,如果靜態文件都放在主域名下,那靜態文件請求的時候帶有的cookie的數據提交給server是非常浪費的,還不如隔離開。

因爲cookie有域的限制,因此不能跨域提交請求,故使用非主要域名的時候,請求頭中就不會帶有cookie數據,這樣可以降低請求頭的大小,降低請求時間,從而達到降低整體請求延時的目的。同時這種方式不會將cookie傳入server,也減少了server對cookie的處理分析環節,提高了server的http請求的解析速度。

36、 響應事件
onclick鼠標點擊某個對象;onfocus獲取焦點;onblur失去焦點;onmousedown鼠標被按下

37、 flash和js通過什麼類如何交互?
Flash提供了ExternalInterface接口與JavaScript通信,ExternalInterface有兩個方法,call和addCallback,call的作用是讓Flash調用js裏的方法,addCallback是用來註冊flash函數讓js調用。

38、 Flash與Ajax各自的優缺點?
Flash:適合處理多媒體、矢量圖形、訪問機器。但對css、處理文本不足,不容易被搜索。
Ajax:對css、文本支持很好,但對多媒體、矢量圖形、訪問機器不足。

39、 有效的javascript變量定義規則
第一個字符必須是一個字母、下劃線(_)或一個美元符號($);其他字符可以是字母、下劃線、美元符號或數字。

40、 XML與JSON的區別?
1) 數據體積方面。JSON相對於XML來講,數據的體積小,傳遞的速度更快些。
2) 數據交互方面。JSON與JavaScript的交互更加方便,更容易解析處理,更好的數據交互。
3) 數據描述方面。JSON對數據的描述性比XML較差。
4) 傳輸速度方面。JSON的速度要遠遠快於XML。

41、 HTML與XML的區別?
(1)XML用來傳輸和存儲數據,HTML用來顯示數據;
(2)XML使用的標籤不用預先定義
(3)XML標籤必須成對出現
(4)XML對大小寫敏感
(5)XML中空格不會被刪減
(6)XML中所有特殊符號必須用編碼表示
(7)XML中的圖片必須有文字說明

42、 漸進增強與優雅降級
漸進增強:針對低版本瀏覽器進行構建頁面,保證最基本的功能,然後再針對高級瀏覽器進行效果、交互等改進,達到更好的用戶體驗。
優雅降級:一開始就構建完整的功能,然後再針對低版本瀏覽器進行兼容。

43、 Web Worker和Web Socket?
web socket:在一個單獨的持久連接上提供全雙工、雙向的通信。使用自定義的協議(ws://、wss://),同源策略對web socket不適用。
web worker:運行在後臺的JavaScript,不影響頁面的性能。
創建worker:var worker = new Worker(url);
向worker發送數據:worker.postMessage(data);
接收worker返回的數據:worker.onmessage
終止一個worker的執行:worker.terminate();

44、JS垃圾回收機制?
1) 標記清除:
這個算法把“對象是否不再需要”簡化定義爲“對象是否可以獲得”。
這個算法假定設置一個叫做根(root)的對象(在Javascript裏,根是全局對象)。定期的,垃圾回收器將從根開始,找所有從根開始引用的對象,然後找這些對象引用的對象。從根開始,垃圾回收器將找到所有可以獲得的對象和所有不能獲得的對象。

2) 引用計數:
這是最簡單的垃圾收集算法。此算法把“對象是否不再需要”簡化定義爲“對象有沒有其他對象引用到它”。如果沒有引用指向該對象(零引用),對象將被垃圾回收機制回收。
該算法有個限制:無法處理循環引用。兩個對象被創建,並互相引用,形成了一個循環。它們被調用之後不會離開函數作用域,所以它們已經沒有用了,可以被回收了。然而,引用計數算法考慮到它們互相都有至少一次引用,所以它們不會被回收。

45、 web應用從服務器主動推送data到客戶端的方式?
JavaScript數據推送:commet(基於http長連接的服務器推送技術)。
基於web socket的推送:SSE(server-send Event)

46、 如何刪除一個cookie?
1) 將cookie的失效時間設置爲過去的時間(expires)

document.cookie = ‘user=’+ encodeURIComponent(‘name’) + ';
expires=’+ new Date(0);

2) 將系統時間設置爲當前時間往前一點時間

var data = new Date();
date.setDate(date.getDate()-1);

47、 attribute與property的區別?
attribute是dom元素在文檔中作爲html標籤擁有的屬性
property是dom元素在js中作爲對象擁有的屬性。
所以,對於html的標準屬性來說,attribute和property是同步的,是會自動更新的。但對於自定義屬性,他們不同步。

48、 Ajax請求的頁面歷史記錄狀態問題?
(1)通過location.hash記錄狀態,讓瀏覽器記錄Ajax請求時頁面狀態的變化。
(2)通過HTML5的history.pushstate,來實現瀏覽器地址欄的無刷新改變。

1. JavaScript 重溫系列(22篇全)

2. ECMAScript 重溫系列(10篇全)

3. JavaScript設計模式 重溫系列(9篇全)

4. 正則 / 框架 / 算法等 重溫系列(16篇全)

5. Webpack4 入門(上)|| Webpack4 入門(下)

6. MobX 入門(上) ||  MobX 入門(下)

7. 59篇原創系列彙總

回覆“加羣”與大佬們一起交流學習~

點擊“閱讀原文”查看70+篇原創文章

點這,與大家一起分享本文吧~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章