摘要:線程的劃分尺度小于進程,使得多線程程序的并發性高。線程在執行過程中與進程還是有區別的。每個獨立的線程有一個程序運行的入口順序執行序列和程序的出口。從邏輯角度來看,多線程的意義在于一個應用程序中,有多個執行部分可以同時執行。
前言
本文講解 56 道 JavaScript 和 ES6+ 面試題的內容。
復習前端面試的知識,是為了鞏固前端的基礎知識,最重要的還是平時的積累!
注意:文章的題與題之間用下劃線分隔開,答案僅供參考。
前端硬核面試專題的完整版在此:前端硬核面試專題,包含:HTML + CSS + JS + ES6 + Webpack + Vue + React + Node + HTTPS + 數據結構與算法 + Git 。
JavaScript常見的瀏覽器內核有哪些 ?
Trident 內核:IE, 360,搜狗瀏覽器 MaxThon、TT、The World,等。[又稱 MSHTML]
Gecko 內核:火狐,FF,MozillaSuite / SeaMonkey 等
Presto 內核:Opera7 及以上。[Opera 內核原為:Presto,現為:Blink]
Webkit 內核:Safari,Chrome 等。 [ Chrome 的:Blink(WebKit 的分支)]
mouseenter 和 mouseover 的區別?
不論鼠標指針穿過被選元素或其子元素,都會觸發 mouseover 事件,對應 mouseout。
只有在鼠標指針穿過被選元素時,才會觸發 mouseenter 事件,對應 mouseleave。
用正則表達式匹配字符串,以字母開頭,后面是數字、字符串或者下劃線,長度為 9 - 20?
var re=new RegExp("^[a-zA-Z][a-zA-Z0-9_]{9,20}$");
手機號碼校驗
function checkPhone(){ var phone = document.getElementById("phone").value; if(!(/^1(3|4|5|7|8)d{9}$/.test(phone))){ alert("手機號碼有誤,請重填"); return false; } }
^1(3|4|5|7|8)d{9}$,表示以 1 開頭,第二位可能是 3/4/5/7/8 等的任意一個,在加上后面的 d 表示數字 [0-9] 的 9 位,總共加起來 11 位結束。
手機號碼格式驗證方法(正則表達式驗證)支持最新電信 199, 移動 198, 聯通 166
// 手機號碼校驗規則 let valid_rule = /^(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])[0-9]{8}$/; if ( ! valid_rule.test(phone_number)) { alert("手機號碼格式有誤"); return false; }
這樣 phone_number 就是取到的手機號碼,即可!
js 字符串兩邊截取空白的 trim 的原型方法的實現
js 中本身是沒有 trim 函數的。
// 刪除左右兩端的空格 function trim(str){ return str.replace(/(^s*)|(s*$)/g, ""); } // 刪除左邊的空格 /(^s*)/g // 刪除右邊的空格 /(s*$)/g
介紹一下你對瀏覽器內核的理解 ?
內核主要分成兩部分:渲染引擎(layout engineer 或 Rendering Engine) 和 JS 引擎。
渲染引擎
負責取得網頁的內容(HTML、XML、圖像等等)、整理訊息(例如加入 CSS 等),以及計算網頁的顯示方式,然后會輸出至顯示器或打印機。
瀏覽器的內核的不同對于網頁的語法解釋會有不同,所以渲染的效果也不相同。
所有網頁瀏覽器、電子郵件客戶端以及其它需要編輯、顯示網絡內容的應用程序都需要內核。
JS 引擎
解析和執行 javascript 來實現網頁的動態效果。
最開始渲染引擎和 JS 引擎并沒有區分的很明確,后來 JS 引擎越來越獨立,內核就傾向于只指渲染引擎。
哪些常見操作會造成內存泄漏 ?
內存泄漏指任何對象在您不再擁有或需要它之后仍然存在。
垃圾回收器定期掃描對象,并計算引用了每個對象的其他對象的數量。如果一個對象的引用數量為 0(沒有其他對象引用過該對象),或對該對象的惟一引用是循環的,那么該對象的內存即可回收。
setTimeout 的第一個參數使用字符串而非函數的話,會引發內存泄漏。
閉包、控制臺日志、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)。
線程與進程的區別 ?
一個程序至少有一個進程,一個進程至少有一個線程。
線程的劃分尺度小于進程,使得多線程程序的并發性高。
另外,進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。
線程在執行過程中與進程還是有區別的。
每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
從邏輯角度來看,多線程的意義在于一個應用程序中,有多個執行部分可以同時執行。
但操作系統并沒有將多個線程看做多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。
eval() 函數有什么用 ?
eval() 函數可計算某個字符串,并執行其中的的 JavaScript 代碼。
實現一個方法,使得:add(2, 5) 和 add(2)(5) 的結果都為 7
var add = function (x, r) { if (arguments.length == 1) { return function (y) { return x + y; }; } else { return x + r; } }; console.log(add(2)(5)); // 7 console.log(add(2,5)); // 7
alert(1 && 2) 和 alert(1 || 0) 的結果是 ?
alert(1 &&2 ) 的結果是 2
只要 “&&” 前面是 false,無論 “&&” 后面是 true 還是 false,結果都將返 “&&” 前面的值;
只要 “&&” 前面是 true,無論 “&&” 后面是 true 還是 false,結果都將返 “&&” 后面的值;
alert(0 || 1) 的結果是 1
只要 “||” 前面為 false,不管 “||” 后面是 true 還是 false,都返回 “||” 后面的值。
只要 “||” 前面為 true,不管 “||” 后面是 true 還是 false,都返回 “||” 前面的值。
只要記住 0 與 任何數都是 0,其他反推。
下面的輸出結果是 ?
var out = 25, inner = { out: 20, func: function () { var out = 30; return this.out; } }; console.log((inner.func, inner.func)()); console.log(inner.func()); console.log((inner.func)()); console.log((inner.func = inner.func)());
結果:25,20,20,25
代碼解析:這道題的考點分兩個
作用域
運算符(賦值預算,逗號運算)
先看第一個輸出:25,因為 ( inner.func, inner.func ) 是進行逗號運算符,逗號運算符就是運算前面的 ”,“ 返回最后一個,舉個栗子
var i = 0, j = 1, k = 2; console.log((i++, j++, k)) // 返回的是 k 的值 2 ,如果寫成 k++ 的話 這里返回的就是 3 console.log(i); // 1 console.log(j); // 2 console.log(k); // 2
回到原題 ( inner.func, inner.func ) 就是返回 inner.func ,而 inner.func 只是一個匿名函數
function () { var out = 30; return this.out; }
而且這個匿名函數是屬于 window 的,則變成了
(function () { var out = 30; return this.out; })()
此刻的 this => window
所以 out 是 25。
第二和第三個 console.log 的作用域都是 inner,也就是他們執行的其實是 inner.func();
inner 作用域中是有 out 變量的,所以結果是 20。
第四個 console.log 考查的是一個等號運算 inner.func = inner.func ,其實返回的是運算的結果,
舉個栗子
var a = 2, b = 3; console.log(a = b) // 輸出的是 3
所以 inner.func = inner.func 返回的也是一個匿名函數
function () { var out = 30; return this.out; }
此刻,道理就和第一個 console.log 一樣了,輸出的結果是 25。
下面程序輸出的結果是 ?
if (!("a" in window)) { var a = 1; } alert(a);
代碼解析:如果 window 不包含屬性 a,就聲明一個變量 a,然后賦值為 1。
你可能認為 alert 出來的結果是 1,然后實際結果是 “undefined”。
要了解為什么,需要知道 JavaScript 里的 3 個概念。
首先,所有的全局變量都是 window 的屬性,語句 var a = 1; 等價于 window.a = 1;
你可以用如下方式來檢測全局變量是否聲明:"變量名稱" in window。
第二,所有的變量聲明都在范圍作用域的頂部,看一下相似的例子:
alert("b" in window); var b;
此時,盡管聲明是在 alert 之后,alert 彈出的依然是 true,這是因為 JavaScript 引擎首先會掃描所有的變量聲明,然后將這些變量聲明移動到頂部,最終的代碼效果是這樣的:
var a; alert("a" in window);
這樣看起來就很容易解釋為什么 alert 結果是 true 了。
第三,你需要理解該題目的意思是,變量聲明被提前了,但變量賦值沒有,因為這行代碼包括了變量聲明和變量賦值。
你可以將語句拆分為如下代碼:
var a; //聲明 a = 1; //初始化賦值
當變量聲明和賦值在一起用的時候,JavaScript 引擎會自動將它分為兩部以便將變量聲明提前,
不將賦值的步驟提前,是因為他有可能影響代碼執行出不可預期的結果。
所以,知道了這些概念以后,重新回頭看一下題目的代碼,其實就等價于:
var a; if (!("a" in window)) { a = 1; } alert(a);
這樣,題目的意思就非常清楚了:首先聲明 a,然后判斷 a 是否在存在,如果不存在就賦值為1,很明顯 a 永遠在 window 里存在,這個賦值語句永遠不會執行,所以結果是 undefined。
提前這個詞語顯得有點迷惑了,你可以理解為:預編譯。
下面程序輸出的結果是 ?
var a = 1; var b = function a(x) { x && a(--x); }; alert(a);
這個題目看起來比實際復雜,alert 的結果是 1。
這里依然有 3 個重要的概念需要我們知道。
首先,第一個是 變量聲明在進入執行上下文就完成了;
第二個概念就是函數聲明也是提前的,所有的函數聲明都在執行代碼之前都已經完成了聲明,和變量聲明一樣。
澄清一下,函數聲明是如下這樣的代碼:
function functionName(arg1, arg2){ //函數體 }
如下不是函數,而是函數表達式,相當于變量賦值:
var functionName = function(arg1, arg2){ //函數體 };
澄清一下,函數表達式沒有提前,就相當于平時的變量賦值。
第三需要知道的是,函數聲明會覆蓋變量聲明,但不會覆蓋變量賦值。
為了解釋這個,我們來看一個例子:
function value(){ return 1; } var value; alert(typeof value); //"function"
盡管變量聲明在下面定義,但是變量 value 依然是 function,也就是說這種情況下,函數聲明的優先級高于變量聲明的優先級,但如果該變量 value 賦值了,那結果就完全不一樣了:
function value(){ return 1; } var value = 1; alert(typeof value); //"number"
該 value 賦值以后,變量賦值初始化就覆蓋了函數聲明。
重新回到題目,這個函數其實是一個有名函數表達式,函數表達式不像函數聲明一樣可以覆蓋變量聲明,但你可以注意到,變量 b 是包含了該函數表達式,而該函數表達式的名字是 a。不同的瀏覽器對 a 這個名詞處理有點不一樣,在 IE 里,會將 a 認為函數聲明,所以它被變量初始化覆蓋了,就是說如果調用 a(–x) 的話就會出錯,而其它瀏覽器在允許在函數內部調用 a(–x),因為這時候 a 在函數外面依然是數字。
基本上,IE 里調用 b(2) 的時候會出錯,但其它瀏覽器則返回 undefined。
理解上述內容之后,該題目換成一個更準確和更容易理解的代碼應該像這樣:
var a = 1, b = function(x) { x && b(--x); }; alert(a);
這樣的話,就很清晰地知道為什么 alert 的總是 1 了。
下面程序輸出的結果是 ?
function a(x) { return x * 2; } var a; alert(a);
alert 的值是下面的函數
function a(x) { return x * 2; }
這個題目比較簡單:即函數聲明和變量聲明的關系和影響,遇到同名的函數聲明,不會重新定義。
下面程序輸出的結果是 ?
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2, 3);
結果為 10。
活動對象是在進入函數上下文時刻被創建的,它通過函數的 arguments 屬性初始化。
三道判斷輸出的題都是經典的題
var a = 4; function b() { a = 3; console.log(a); function a(){}; } b();
明顯輸出是 3,因為里面修改了 a 這個全局變量,那個 function a(){} 是用來干擾的,雖然函數聲明會提升,就被 a 給覆蓋掉了,這是我的理解。
不記得具體的,就類似如下
var baz = 3; var bazz ={ baz: 2, getbaz: function() { return this.baz } } console.log(bazz.getbaz()) var g = bazz.getbaz; console.log(g()) ;
第一個輸出是 2,第二個輸出是 3。
這題考察的就是 this 的指向,函數作為對象本身屬性調用的時候,this 指向對象,作為普通函數調用的時候,就指向全局了。
還有下面的題:
var arr = [1,2,3,4,5]; for(var i = 0; i < arr.length; i++){ arr[i] = function(){ alert(i) } } arr[3]();
典型的閉包,彈出 5 。
JavaScript 里有哪些數據類型
一、數據類型 ?
undefiend 沒有定義數據類型? ? ? ?
number 數值數據類型,例如 10 或者 1 或者 5.5? ? ? ?
string 字符串數據類型用來描述文本,例如 "你的姓名"? ? ? ?
boolean 布爾類型 true | false ,不是正就是反? ? ? ?
object 對象類型,復雜的一組描述信息的集合
function 函數類型
解釋清楚 null 和 undefined
null 用來表示尚未存在的對象,常用來表示函數企圖返回一個不存在的對象。? null 表示"沒有對象",即該處不應該有值。
null 典型用法是:?
作為函數的參數,表示該函數的參數不是對象。?
作為對象原型鏈的終點。
當聲明的變量還未被初始化時,變量的默認值為 undefined。?undefined 表示"缺少值",就是此處應該有一個值,但是還沒有定義。?
變量被聲明了,但沒有賦值時,就等于 undefined。?
調用函數時,應該提供的參數沒有提供,該參數等于 undefined。?
對象沒有賦值的屬性,該屬性的值為 undefined。?
函數沒有返回值時,默認返回 undefined。
未定義的值和定義未賦值的為 undefined,null 是一種特殊的 object,NaN 是一種特殊的 number。
講一下 1 和 Number(1) 的區別*
1 是一個原始定義好的 number 類型;
Number(1) 是一個函數類型,是我們自己聲明的一個函數(方法)。
講一下 prototype 是什么東西,原型鏈的理解,什么時候用 prototype ?
prototype 是函數對象上面預設的對象屬性。
函數里的 this 什么含義,什么情況下,怎么用 ?
this 是 Javascript 語言的一個關鍵字。
它代表函數運行時,自動生成的一個內部對象,只能在函數內部使用。
隨著函數使用場合的不同,this 的值會發生變化。
但是有一個總的原則,那就是 this 指的是,調用函數的那個對象。
情況一:純粹的函數調用?
這是函數的最通常用法,屬于全局性調用,因此 this 就代表全局對象 window。? ?
function test(){? ? this.x = 1;? ? alert(this.x);? ? }? ? test(); // 1
為了證明 this 就是全局對象,我對代碼做一些改變:? ?
var x = 1;? ? function test(){? ? alert(this.x);? ? }? ? test(); // 1? ?
運行結果還是 1。
再變一下:? ?
var x = 1;? ? function test(){? ? this.x = 0;? ? }? ? test(); alert(x); // 0
情況二:作為對象方法的調用? ?
函數還可以作為某個對象的方法調用,這時 this 就指這個上級對象。? ?
function test(){? ? alert(this.x);? ? } var x = 2? ? var o = {};? ? o.x = 1;? ? o.m = test;? ? o.m(); // 1
情況三: 作為構造函數調用? ?
所謂構造函數,就是通過這個函數生成一個新對象(object)。這時的 this 就指這個新對象。
function Test(){? ? this.x = 1;? ? }? ? var o = new Test(); alert(o.x); // 1? ?
運行結果為 1。為了表明這時 this 不是全局對象,對代碼做一些改變:?
?
var x = 2;? ? function Test(){? ? this.x = 1;? ? }? ? var o = new Test();? ? alert(x); // 2
運行結果為 2,表明全局變量 x 的值沒變。
情況四: apply 調用? ?
apply() 是函數對象的一個方法,它的作用是改變函數的調用對象,它的第一個參數就表示改變后的調用這個函數的對象。因此,this 指的就是這第一個參數。? ?
var x = 0;? ? function test(){? ? alert(this.x);? ? }? ? var o = {};? ? o.x = 1;? ? o.m = test;? ? o.m.apply(); // 0? ?
apply() 的參數為空時,默認調用全局對象。因此,這時的運行結果為 0,證明 this 指的是全局對象。
? ?
如果把最后一行代碼修改為
o.m.apply(o); // 1
運行結果就變成了 1,證明了這時 this 代表的是對象 o。
apply 和 call ?什么含義,什么區別 ?什么時候用 ?
call,apply 都屬于 Function.prototype 的一個方法,它是 JavaScript 引擎內在實現的,因為屬于 Function.prototype,所以每個 Function 對象實例(就是每個方法)都有 call,apply 屬性。
既然作為方法的屬性,那它們的使用就當然是針對方法的了,這兩個方法是容易混淆的,因為它們的作用一樣,只是使用方式不同。
語法:
foo.call(this, arg1, arg2, arg3) == foo.apply(this, arguments) == this.foo(arg1, arg2, arg3);
相同點:兩個方法產生的作用是完全一樣的。
不同點:方法傳遞的參數不同。
每個函數對象會有一些方法可以去修改函數執行時里面的 this,比較常見得到就是 call 和 apply,通過 call 和 apply 可以重新定義函數的執行環境,即 this 的指向。
function add(c, d) { console.log(this.a + this.b + c + d); } var o = { a: 1, b: 3 }; add.call(o, 5, 7); //1+3+5+7=16 //傳參的時候是扁平的把每個參數傳進去 add.apply(o, [10, 20]); //1+3+10+20=34 //傳參的時候是把參數作為一個數組傳進去 //什么時候使用 call 或者 apply function bar() { console.log(Object.prototype.toString.call(this)); // 用來調用一些無法直接調用的方法 } bar.call(7); // "[object Number]"
異步過程的構成要素有哪些?和異步過程是怎樣的 ?
總結一下,一個異步過程通常是這樣的:
主線程發起一個異步請求,相應的工作線程接收請求并告知主線程已收到(異步函數返回);
主線程可以繼續執行后面的代碼,同時工作線程執行異步任務;
工作線程完成工作后,通知主線程;
主線程收到通知后,執行一定的動作(調用回調函數)。
異步函數通常具有以下的形式:A(args..., callbackFn)。
它可以叫做異步過程的發起函數,或者叫做異步任務注冊函數。
args 和 callbackFn 是這個函數的參數。
所以,從主線程的角度看,一個異步過程包括下面兩個要素:
發起函數(或叫注冊函數) A。
回調函數 callbackFn。
它們都是在主線程上調用的,其中注冊函數用來發起異步過程,回調函數用來處理結果。
舉個具體的例子:
setTimeout(fn, 1000);
其中的 setTimeout 就是異步過程的發起函數,fn 是回調函數。
注意:前面說的形式 A(args..., callbackFn) 只是一種抽象的表示,并不代表回調函數一定要作為發起函數的參數。
例如:
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = xxx; // 添加回調函數 xhr.open("GET", url); xhr.send(); // 發起函數
發起函數和回調函數就是分離的。
說說消息隊列和事件循環
主線程在執行完當前循環中的所有代碼后,就會到消息隊列取出這條消息(也就是 message 函數),并執行它。
完成了工作線程對主線程的通知,回調函數也就得到了執行。
如果一開始主線程就沒有提供回調函數,AJAX 線程在收到 HTTP 響應后,也就沒必要通知主線程,從而也沒必要往消息隊列放消息。
異步過程的回調函數,一定不在當前的這一輪事件循環中執行。
session 與 cookie 的區別
session 保存在服務器,客戶端不知道其中的信息;
cookie 保存在客戶端,服務器能夠知道其中的信息。?
session 中保存的是對象,cookie 中保存的是字符串。 ??
session 不能區分路徑,同一個用戶在訪問一個網站期間,所有的 session 在任何一個地方都可以訪問到。
而 cookie 中如果設置了路徑參數,那么同一個網站中不同路徑下的 cookie 互相是訪問不到的。 ?
cookies 是干嘛的,服務器和瀏覽器之間的 cookies 是怎么傳的,httponly 的 cookies 和可讀寫的 cookie 有什么區別,有無長度限制 ?
cookies 是一些存儲在用戶電腦上的小文件。
它是被設計用來保存一些站點的用戶數據,這樣能夠讓服務器為這樣的用戶定制內容,后者頁面代碼能夠獲取到 cookie 值然后發送給服務器。
比如 cookie 中存儲了所在地理位置,以后每次進入地圖就默認定位到改地點即可。
請描述一下 cookies,sessionStorage 和 localStorage 的區別
共同點
都是保存在瀏覽器端,且同源的。
區別
cookie 數據始終在同源的 http 請求中攜帶(即使不需要),即 cookie 在瀏覽器和服務器間來回傳遞。
而 sessionStorage 和 localStorage 不會自動把數據發給服務器,僅在本地保存。
cookie 數據還有路徑(path)的概念,可以限制 cookie 只屬于某個路徑下。
存儲大小限制也不同,cookie 數據不能超過 4k,同時因為每次 http 請求都會攜帶 cookie,所以 cookie 只適合保存很小的數據,如會話標識。
sessionStorage 和 localStorage 雖然也有存儲大小的限制,但比 cookie 大得多,可以達到 5M 或更大。
數據有效期不同,sessionStorage:僅在當前瀏覽器窗口關閉前有效,自然也就不可能持久保持;localStorage:始終有效,窗口或瀏覽器關閉也一直保存,因此用作持久數據;cookie 只在設置的 cookie 過期時間之前一直有效,即使窗口或瀏覽器關閉。
作用域不同,sessionStorage 在不同的瀏覽器窗口中不共享,即使是同一個頁面;cookie 和 localStorage 在所有同源窗口中都是共享的。
從敲入 URL 到渲染完成的整個過程,包括 DOM 構建的過程,說的約詳細越好
用戶輸入 url 地址,瀏覽器根據域名尋找 IP 地址
瀏覽器向服務器發送 http 請求,如果服務器段返回以 301 之類的重定向,瀏覽器根據相應頭中的 location 再次發送請求
服務器端接受請求,處理請求生成 html 代碼,返回給瀏覽器,這時的 html 頁面代碼可能是經過壓縮的
瀏覽器接收服務器響應結果,如果有壓縮則首先進行解壓處理,緊接著就是頁面解析渲染
解析渲染該過程主要分為以下步驟:解析 HTML、構建 DOM 樹、DOM 樹與 CSS 樣式進行附著構造呈現樹
布局
繪制
詳情:面試題之從敲入 URL 到瀏覽器渲染完成
是否了解公鑰加密和私鑰加密。如何確保表單提交里的密碼字段不被泄露。
公鑰用于對數據進行加密,私鑰用于對數據進行解密。
很直觀的理解:公鑰就是公開的密鑰,其公開了大家才能用它來加密數據。私鑰是私有的密鑰,誰有這個密鑰才能夠解密密文。
解決方案 1:
form 在提交的過程中,對密碼字段是不進行加密而是以明碼的形式進行數據傳輸的。
如果要對數據進行加密,你可以自己寫一個腳本對內容進行編碼后傳輸,只是這個安全性也并不高。
解決方案 2:
如果想對數據進行加密,你可以使用 HTTPS 安全傳輸協議,這個協議是由系統進行密碼加密處理的,在數據傳輸中是絕對不會被攔截獲取的,只是 HTTPS 的架設會相對麻煩點。一些大型網站的登錄、銀行的在線網關等都是走這條路。
驗證碼是干嘛的,是為了解決什么安全問題。
所謂驗證碼,就是將一串隨機產生的數字或符號,生成一幅圖片, 圖片里加上一些干擾象素(防止OCR),由用戶肉眼識別其中的驗證碼信息,輸入表單提交網站驗證,驗證成功后才能使用某項功能。
驗證碼一般是防止批量注冊的,人眼看起來都費勁,何況是機器。
像百度貼吧未登錄發貼要輸入驗證碼大概是防止大規模匿名回帖的發生。
目前,不少網站為了防止用戶利用機器人自動注冊、登錄、灌水,都采用了驗證碼技術。
截取字符串 abcdefg 的 efg。
從第四位開始截取
alert("abcdefg".substring(4)); alert ("abcdefg".slice(4))
判斷一個字符串中出現次數最多的字符,統計這個次數
步驟
將字符串轉化數組?
創建一個對象?
遍歷數組,判斷對象中是否存在數組中的值,如果存在值 +1,不存在賦值為 1
定義兩個變量存儲字符值,字符出現的字數
var str = "abaasdffggghhjjkkgfddsssss3444343"; // 1.將字符串轉換成數組 var newArr = str.split(""); // 2.創建一個對象 var json = {}; // 3. 所有字母出現的次數,判斷對象中是否存在數組中的值,如果存在值 +1,不存在賦值為 1 for(var i = 0; i < newArr.length; i++){ // 類似:json : { ‘a’: 3, ’b’: 1 } if(json[newArr[i]]){ json[newArr[i]] +=1; } else { json[newArr[i]] = 1; } } // 4 定義兩個變量存儲字符值,字符出現的字數 var num = 0 ; //次數 var element = ""; //最多的項 for(var k in json){ if(json[k] > num){ num = json[k]; element = k ; } } console.log("出現次數:"+num +"最多的字符:"+ element);
document.write 和 innerHTML 的區別
document.write 是直接寫入到頁面的內容流,如果在寫之前沒有調用 document.open, 瀏覽器會自動調用 open。每次寫完關閉之后重新調用該函數,會導致頁面被重寫。
innerHTML 則是 DOM 頁面元素的一個屬性,代表該元素的 html 內容。你可以精確到某一個具體的元素來進行更改。如果想修改 document 的內容,則需要修改 document.documentElement.innerElement。
innerHTML 將內容寫入某個 DOM 節點,不會導致頁面全部重繪。
innerHTML 很多情況下都優于 document.write,其原因在于其允許更精確的控制要刷新頁面的那一個部分。
document.write 是重寫整個 document, 寫入內容是字符串的 html;innerHTML 是 HTMLElement 的屬性,是一個元素的內部 html 內容?
JS 識別不同瀏覽器信息
function myBrowser() { var userAgent = navigator.userAgent; //取得瀏覽器的userAgent字符串 var isOpera = userAgent.indexOf("Opera") > -1; if (isOpera) { return "Opera" }; //判斷是否Opera瀏覽器 if (userAgent.indexOf("Firefox") > -1) { return "Firefox"; } //判斷是否Firefox瀏覽器 if (userAgent.indexOf("Chrome") > -1) { return "Chrome"; } //判斷是否Google瀏覽器 if (userAgent.indexOf("Safari") > -1) { return "Safari"; } //判斷是否Safari瀏覽器 if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) { return "IE"; }; //判斷是否IE瀏覽器 }
JavaScript 常見的內置對象
有 Object、Math、String、Array、Number、Function、Boolean、JSON 等,其中 Object 是所有對象的基類,采用了原型繼承方式。
編寫一個方法,求一個字符串的字節長度
假設:一個英文字符占用一個字節,一個中文字符占用兩個字節
function getBytes(str){ var len = str.length; var bytes = len; for(var i = 0; i < len; i++){ if (str.charCodeAt(i) > 255) bytes++; } return bytes; } alert(getBytes("你好,as"));
JS 組成
核心(ECMAScript) 描述了該語言的語法和基本對象
文檔對象模型(DOM) 描述了處理網頁內容的方法和接口
瀏覽器對象模型(BOM) 描述了與瀏覽器進行交互的方法和接口
new 操作符具體干了什么呢 ?
創建一個空對象,并且 this 變量引用該對象,同時還繼承了該函數的原型。
屬性和方法被加入到 this 引用的對象中。
新創建的對象由 this 所引用,并且最后隱式的返回 this 。
JSON 的了解?
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。
它是基于 JavaScript 的一個子集。
數據格式簡單,易于讀寫,占用帶寬小。
格式:采用鍵值對。例如:{ “age?: ?12?, ”name?: ?back? }
你有哪些性能優化的方法 ?
web 前端是應用服務器處理之前的部分,前端主要包括:HTML、CSS、javascript、image 等各種資源,針對不同的資源有不同的優化方式。
內容優化
減少 HTTP 請求數。這條策略是最重要最有效的,因為一個完整的請求要經過 DNS 尋址,與服務器建立連接,發送數據,等待服務器響應,接收數據這樣一個消耗時間成本和資源成本的復雜的過程。
常見方法:合并多個 CSS 文件和 js 文件,利用 CSS Sprites 整合圖像,Inline Images (使用 data:URL scheme 在實際的頁面嵌入圖像數據 ),合理設置 HTTP 緩存等。
減少 DNS 查找
避免重定向
使用 Ajax 緩存
延遲加載組件,預加載組件
減少 DOM 元素數量。頁面中存在大量 DOM 元素,會導致 javascript 遍歷 DOM 的效率變慢。
最小化 iframe 的數量。iframes 提供了一個簡單的方式把一個網站的內容嵌入到另一個網站中。但其創建速度比其他包括 JavaScript 和 CSS 的 DOM 元素的創建慢了 1-2 個數量級。
避免 404。HTTP 請求時間消耗是很大的,因此使用 HTTP 請求來獲得一個沒有用處的響應(例如 404 沒有找到頁面)是完全沒有必要的,它只會降低用戶體驗而不會有一點好處。
服務器優化
使用內容分發網絡(CDN)。把網站內容分散到多個、處于不同地域位置的服務器上可以加快下載速度。
GZIP 壓縮
設置 ETag:ETags(Entity tags,實體標簽)是 web 服務器和瀏覽器用于判斷瀏覽器緩存中的內容和服務器中的原始內容是否匹配的一種機制。
提前刷新緩沖區
對 Ajax 請求使用 GET 方法
避免空的圖像 src
Cookie 優化
減小 Cookie 大小
針對 Web 組件使用域名無關的 Cookie
CSS 優化
將 CSS 代碼放在 HTML 頁面的頂部
避免使用 CSS 表達式
使用 < link> 來代替 @import
避免使用 Filters
javascript 優化
將 JavaScript 腳本放在頁面的底部。
將 JavaScript 和 CSS 作為外部文件來引用。
在實際應用中使用外部文件可以提高頁面速度,因為 JavaScript 和 CSS 文件都能在瀏覽器中產生緩存。
縮小 JavaScript 和 CSS
刪除重復的腳本
最小化 DOM 的訪問。使用 JavaScript 訪問 DOM 元素比較慢。
開發智能的事件處理程序
javascript 代碼注意:謹慎使用 with,避免使用 eval Function 函數,減少作用域鏈查找。
圖像優化
優化圖片大小
通過 CSS Sprites 優化圖片
不要在 HTML 中使用縮放圖片
favicon.ico 要小而且可緩存
JS 格式化數字(每三位加逗號)
從后往前取。
function?toThousands(num)?{?? ????var?num?=?(num?||?0).toString(),?result?=?"";?? ????while?(num.length?>?3)?{?? ????????result?=?","?+?num.slice(-3)?+?result;?? ????????num?=?num.slice(0,?num.length?-?3);?? ????}?? ????if?(num)?{?result?=?num?+?result;?}?? ????return?result;?? }??
合并數組
如果你需要合并兩個數組的話,可以使用?Array.concat()
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; console.log(array1.concat(array2)); // [1,2,3,4,5,6];
然而,這個函數并不適用于合并大的數組,因為它需要創建一個新的數組,而這會消耗很多內存。
這時,你可以使用?Array.push.apply(arr1, arr2)?來代替創建新的數組,它可以把第二個數組合并到第一個中,從而較少內存消耗。
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; console.log(array1.push.apply(array1, array2)); // [1, 2, 3, 4, 5, 6]
把節點列表 (NodeList) 轉換為數組
如果你運行?document.querySelectorAll("p")?方法,它可能會返回一個 DOM 元素的數組 — 節點列表對象。
但這個對象并不具有數組的全部方法,如?sort(),reduce(),?map(),filter()。
為了使用數組的那些方法,你需要把它轉換為數組。
只需使用?[].slice.call(elements)?即可實現:
var elements = document.querySelectorAll("p"); // NodeList var arrayElements = [].slice.call(elements); // 現在 NodeList 是一個數組 var arrayElements = Array.from(elements); // 這是另一種轉換 NodeList 到 Array 的方法
打亂數組元素的順序
不適用?Lodash?等這些庫打亂數組元素順序,你可以使用這個技巧:
var list = [1, 2, 3]; console.log(list.sort(function() { Math.random() - 0.5 })); // [2, 1, 3]
js 的 ready 和 onload 事件的區別
onload 是等 HTML 的所有資源都加載完成后再執行 onload 里面的內容,所有資源包括 DOM 結構、圖片、視頻 等資源;
ready 是當 DOM 結構加載完成后就可以執行了,相當于 jQuery 中的 $(function(){ js 代碼 });
另外,onload 只能有一個,ready 可以有多個。
js 的兩種回收機制
標記清除(mark and sweep)
從語義上理解就比較好理解了,大概就是當變量進入到某個環境中的時候就把這個變量標記一下,比如標記為“進入環境”,當離開的時候就把這個變量的標記給清除掉,比如是“離開環境”。而在這后面還有標記的變量將被視為準備刪除的變量。
垃圾收集器在運行的時候會給存儲在內存中的所有變量都加上標記(可以使用任何標記方式)。
然后,它會去掉環境中的變量以及被環境中的變量引用的變量的標記。
而在此之后再被加上的標記的變量將被視為準備刪除的變量,原因是環境中的變量已經無法訪問到這些變量了。
最后,垃圾收集器完成內存清除工作。銷毀那些帶標記的值并回收它們所占用的內存空間。
這是 javascript 最常見的垃圾回收方式。至于上面有說道的標記,到底該如何標記 ?
好像是有很多方法,比如特殊位翻轉,維護一個列表什么的。
引用計數(reference counting)
引用計數的含義是跟蹤記錄每個值被引用的次數,當聲明一個變量并將一個引用類型的值賦給該變量時,這個時候的引用類型的值就會是引用次數 +1 了。如果同一個值又被賦給另外一個變量,則該值的引用次數又 +1。
相反如果包含這個值的引用的變量又取得另外一個值,即被重新賦了值,那么這個值的引用就 -1 。當這個值的引用次數編程 0 時,表示沒有用到這個值,這個值也無法訪問,因此環境就會收回這個值所占用的內存空間回收。
這樣,當垃圾收集器下次再運行時,它就會釋放引用次數為 0 的值所占用的內存。
三張圖搞懂 JavaScript 的原型對象與原型鏈
對于新人來說,JavaScript 的原型是一個很讓人頭疼的事情,一來 prototype 容易與 proto 混淆,
一、prototype 和 proto 的區別
var a = {}; console.log(a.prototype); //undefined console.log(a.__proto__); //Object {} var b = function(){} console.log(b.prototype); //b {} console.log(b.__proto__); //function() {}
結果:
/*1、字面量方式*/ var a = {}; console.log("a.__proto__ :", a.__proto__); // Object {} console.log("a.__proto__ === a.constructor.prototype:", a.__proto__ === a.constructor.prototype); // true /*2、構造器方式*/ var A = function(){}; var a2 = new A(); console.log("a2.__proto__:", a2.__proto__); // A {} console.log("a2.__proto__ === a2.constructor.prototype:", a2.__proto__ === a2.constructor.prototype); // true /*3、Object.create()方式*/ var a4 = { a: 1 } var a3 = Object.create(a4); console.log("a3.__proto__:", a3.__proto__); // Object {a: 1} console.log("a3.__proto__ === a3.constructor.prototype:", a3.__proto__ === a3.constructor.prototype); // false(此處即為圖1中的例外情況)
結果:
var A = function(){}; var a = new A(); console.log(a.__proto__); // A {}(即構造器 function A 的原型對象) console.log(a.__proto__.__proto__); // Object {}(即構造器 function Object 的原型對象) console.log(a.__proto__.__proto__.__proto__); // null
結果:
閉包的理解 ?
一、變量的作用域
要理解閉包,首先必須理解 Javascript 特殊的變量作用域。
變量的作用域無非就是兩種:全局變量和局部變量。
Javascript語言的特殊之處,就在于函數內部可以直接讀取全局變量。
var n = 999; function f1(){ alert(n); } f1(); // 999
另一方面,在函數外部自然無法讀取函數內的局部變量。
function f1(){ var n = 999; } alert(n); // error
這里有一個地方需要注意,函數內部聲明變量的時候,一定要使用 var 命令。
如果不用的話,你實際上聲明了一個全局變量!
function f1(){ n = 999; } f1(); alert(n); // 999
二、如何從外部讀取局部變量 ?
function f1() { var n = 999; function f2() { alert(n); } return f2; } var result = f1(); result(); // 999
既然 f2 可以讀取 f1 中的局部變量,那么只要把 f2 作為返回值,我們不就可以在 f1 外部讀取它的內部變量了嗎!
三、閉包的概念
上一節代碼中的 f2 函數,就是閉包。
我的理解是,閉包就是能夠讀取其他函數內部變量的函數。
由于在 Javascript 語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成 定義在一個函數內部的函數。
所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋梁。
四、閉包的用途
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。
怎么來理解呢 ?請看下面的代碼。
function f1() { var n = 999; nAdd = function () { n += 1 } function f2() { alert(n); } return f2; } var result = f1(); result(); // 999 nAdd(); result(); // 1000
在這段代碼中,result 實際上就是閉包 f2 函數。它一共運行了兩次,第一次的值是 999,第二次的值是 1000。這證明了,函數 f1 中的局部變量 n 一直保存在內存中,并沒有在 f1 調用后被自動清除。
為什么會這樣呢 ?
原因就在于 f1 是 f2 的父函數,而 f2 被賦給了一個全局變量,這導致 f2 始終在內存中,而 f2 的存在依賴于 f1,因此 f1 也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。
這段代碼中另一個值得注意的地方,就是
"nAdd=function(){ n+=1 }" 這一行,首先在 nAdd 前面沒有使用 var 關鍵字,因此 nAdd 是一個全局變量,而不是局部變量。
其次,nAdd 的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以 nAdd 相當于是一個 setter,可以在函數外部對函數內部的局部變量進行操作。
五、使用閉包的注意點
由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在 IE 中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
閉包面試經典問題
問題:想每次點擊對應目標時彈出對應的數字下標 0~4 ,但實際是無論點擊哪個目標都會彈出數字 5。
function onMyLoad() { var arr = document.getElementsByTagName("p"); for (var i = 0; i < arr.length; i++) { arr[i].onclick = function () { alert(i); } } }
問題所在:arr 中的每一項的 onclick 均為一個函數實例(Function 對象),這個函數實例也產生了一個閉包域,這個閉包域引用了外部閉包域的變量,其 function scope 的 closure 對象有個名為 i 的引用,外部閉包域的私有變量內容發生變化,內部閉包域得到的值自然會發生改變。
解決辦法一
解決思路:增加若干個對應的閉包域空間(這里采用的是匿名函數),專門用來存儲原先需要引用的內容(下標),不過只限于基本類型(基本類型值傳遞,對象類型引用傳遞)。
//聲明一個匿名函數,若傳進來的是基本類型則為值傳遞,故不會對實參產生影響, //該函數對象有一個本地私有變量 arg(形參) ,該函數的 function scope 的 closure 對象屬性有兩個引用,一個是 arr,一個是 i //盡管引用 i 的值隨外部改變 ,但本地私有變量(形參) arg 不會受影響,其值在一開始被調用的時候就決定了 for (var i = 0; i < arr.length; i++) { (function (arg) { arr[i].onclick = function () { // onclick 函數實例的 function scope 的 closure 對象屬性有一個引用 arg, alert(arg); //只要 外部空間的 arg 不變,這里的引用值當然不會改變 } })(i); //立刻執行該匿名函數,傳遞下標 i (實參) }
解決辦法二
解決思路:將事件綁定在新增的匿名函數返回的函數上,此時綁定的函數中的 function scope 中的 closure 對象的 引用 arg 是指向將其返回的匿名函數的私有變量 arg
for (var i = 0; i < arr.length; i++) { arr[i].onclick = (function (arg) { return function () { alert(arg); } })(i); }
解決辦法三
使用 ES6 新語法 let 關鍵字
for (var i = 0; i < arr.length; i++) { let j = i; // 創建一個塊級變量 arr[i].onclick = function () { alert(j); } }
JavaScript 判斷一個變量是對象還是數組 ?
typeof 都返回 object
在 JavaScript 中所有數據類型嚴格意義上都是對象,但實際使用中我們還是有類型之分,如果要判斷一個變量是數組還是對象使用 typeof 搞不定,因為它全都返回 object。
第一,使用 typeof 加 length 屬性
數組有 length 屬性,object 沒有,而 typeof 數組與對象都返回 object,所以我們可以這么判斷
var?getDataType =?function(o){ ????if(typeof?o ==?"object"){ ????????if(?typeof?o.length ==?"number"?){ ????????????return?"Array"; ????????} else { ????????????return?"Object";??? ????????} ????} else { ????????return?"param is no object type"; ????} };
第二,使用 instanceof
利用 instanceof 判斷數據類型是對象還是數組時應該優先判斷 array,最后判斷 object。
var?getDataType =?function(o){ ????if(o?instanceof?Array){ ????????return?"Array" ????} else?if ( o?instanceof?Object ){ ????????return?"Object"; ????} else { ????????return?"param is no object type"; ????} };
ES5 的繼承和 ES6 的繼承有什么區別 ?
ES5 的繼承時通過 prototype 或構造函數機制來實現。
ES5 的繼承實質上是先創建子類的實例對象,然后再將父類的方法添加到 this 上(Parent.apply(this))。
ES6 的繼承機制完全不同,實質上是先創建父類的實例對象 this(所以必須先調用父類的 super()方法),然后再用子類的構造函數修改 this。
具體的:ES6 通過 class 關鍵字定義類,里面有構造方法,類之間通過 extends 關鍵字實現繼承。子類必須在 constructor 方法中調用 super 方法,否則新建實例報錯。因為子類沒有自己的 this 對象,而是繼承了父類的 this 對象,然后對其進行加工。如果不調用 super 方法,子類得不到 this 對象。
ps:super 關鍵字指代父類的實例,即父類的 this 對象。在子類構造函數中,調用 super 后,才可使用 this 關鍵字,否則報錯。
翻轉一個字符串
先將字符串轉成一個數組,然后用數組的 reverse() + join() 方法。
let a = "hello word"; let b = [...str].reverse().join(""); // drow olleh
說說堆和棧的區別 ?
一、堆棧空間分配區別
棧(操作系統):由操作系統自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧;
堆(操作系統):一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由 OS 回收,分配方式倒是類似于鏈表。
二、堆棧緩存方式區別
棧使用的是一級緩存, 他們通常都是被調用時處于存儲空間中,調用完畢立即釋放;
堆是存放在二級緩存中,生命周期由虛擬機的垃圾回收算法來決定(并不是一旦成為孤兒對象就能被回收)。所以調用這些對象的速度要相對來得低一些。
三、堆棧數據結構區別
堆(數據結構):堆可以被看成是一棵樹,如:堆排序;
棧(數據結構):一種先進后出的數據結構。
js 經典面試知識文章JS 是單線程,你了解其運行機制嗎 ?
7 分鐘理解 JS 的節流、防抖及使用場景
JavaScript 常見的六種繼承方式
九種跨域方式實現原理(完整版)
常見六大 Web 安全攻防解析
一文讀懂 HTTP/2 及 HTTP/3 特性
深入理解 HTTPS 工作原理
JavaScript 中的垃圾回收和內存泄漏
你不知道的瀏覽器頁面渲染機制
JavaScript 設計模式
深入 javascript——構造函數和原型對象
ES6 +ES6 聲明變量的六種方法
ES5 只有兩種聲明變量的方法:var 和 function 。
ES6 除了添加 let 和 const 命令。
還有兩種聲明變量的方法:import 命令和 class 命令。
Promise 的隊列與 setTimeout 的隊列有何關聯 ?
setTimeout(function(){ console.log(4) }, 0); new Promise(function(resolve){ console.log(1) for( var i = 0 ; i < 10000 ; i++ ){ i == 9999 && resolve() } console.log(2) }).then(function(){ console.log(5) }); console.log(3);
為什么結果是:1, 2, 3, 5, 4;而不是:1, 2, 3, 4, 5 ?
js 里面有宏任務(macrotask)和微任務(microtask)。
因為 setTimeout 是屬于 macrotask 的,而整個 script 也是屬于一個 macrotask,promise.then 回調是 microtask,執行過程大概如下:
由于整個 script 也屬于一個 macrotask,由于會先執行 macrotask 中的第一個任務,再加上 promise 構造函數因為是同步的,所以會先打印出 1 和 2;
然后繼續同步執行末尾的 console.log(3) 打印出 3;
此時 setTimeout 被推進到 macrotask 隊列中, promise.then 回調被推進到 microtask 隊列中;
由于在第一步中已經執行完了第一個 macrotask ,所以接下來會順序執行所有的 microtask,也就是 promise.then 的回調函數,從而打印出 5;
microtask 隊列中的任務已經執行完畢,繼續執行剩下的 macrotask 隊列中的任務,也就是 setTimeout,所以打印出 4。
ES6+ 面試知識文章那些必會用到的 ES6 精粹
最后前端硬核面試專題的完整版在此:前端硬核面試專題,包含:HTML + CSS + JS + ES6 + Webpack + Vue + React + Node + HTTPS + 數據結構與算法 + Git 。
如果覺得本文還不錯,記得給個 star , 你的 star 是我持續更新的動力!。
聽說點收藏,不點贊的都是在耍流氓 -_-
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106793.html
摘要:更是中高級面試過程中經常會問的技術,無論你是否用過,你都必須熟悉。下面我為大家準備了一些常見的的面試題,一些是我經常問別人的,一些是我過去面試遇到的一些問題,總結給大家,希望對大家能有所幫助。 想往高處走,怎么能不懂 Dubbo? Dubbo是國內最出名的分布式服務框架,也是 Java 程序員必備的必會的框架之一。Dubbo 更是中高級面試過程中經常會問的技術,無論你是否用過,你都必須...
摘要:通過面試者的答案可以得知他都知道哪些開發語言。這個問題的答案能夠知道求職者有沒有可靠的資源,這些資源在未來開展項目時可能會派上用場。對這個問題的誠實回答可以幫助面試官為求職者提供合適的團隊。 翻譯:瘋狂的技術宅原文:https://www.indeed.com/hire/i... 不管你是面試官還是求職者,里面的思路都能讓你獲益匪淺。 你用 CSS 多久了? 解析: 這個問題可以讓面...
摘要:引言半月刊第四期來啦,這段時間新增了道高頻面試題,今天就把最近半月匯總的面試題和部分答案發給大家,幫助大家查漏補缺,歡迎加群互相學習。更多更全的面試題和答案匯總在下面的項目中,點擊查看。引言 半月刊第四期來啦,這段時間 Daily-Interview-Question 新增了 14 道高頻面試題,今天就把最近半月匯總的面試題和部分答案發給大家,幫助大家查漏補缺,歡迎 加群 互相學習。 更多更...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
閱讀 3939·2021-10-09 09:43
閱讀 2871·2021-10-08 10:05
閱讀 2734·2021-09-08 10:44
閱讀 882·2019-08-30 15:52
閱讀 2809·2019-08-26 17:01
閱讀 3016·2019-08-26 13:54
閱讀 1650·2019-08-26 10:48
閱讀 807·2019-08-23 14:41