TCP/IP五層協議
TCP/IP
五層協議和OSI
的七層協議對應關係如下:
- 應用層 (application layer):直接為應用程式提供服務。應用層協議定義的是應用程式間通訊和互動的規則,不同的應用有著不同的應用層協議,如 HTTP協議(全球資訊網服務)、FTP協議(檔案傳輸)、SMTP協議(電子郵件)、DNS(域名查詢)等。
傳輸層 (transport layer):有時也譯為運輸層,它負責為兩臺主機中的程式提供通訊服務。該層主要有以下兩種協議:
- 傳輸控制協議 (Transmission Control Protocol,TCP):提供面向連線的、可靠的資料傳輸服務,資料傳輸的基本單位是報文段(segment);
- 使用者資料包協議 (User Datagram Protocol,UDP):提供無連線的、盡最大努力的資料傳輸服務,但不保證資料傳輸的可靠性,資料傳輸的基本單位是使用者資料包。
- 網路層 (internet layer):有時也譯為網際層,它負責為兩臺主機提供通訊服務,並透過選擇合適的路由將資料傳遞到目標主機。
- 資料鏈路層 (data link layer):負責將網路層交下來的 IP 資料包封裝成幀,並在鏈路的兩個相鄰節點間傳送幀,每一幀都包含資料和必要的控制資訊(如同步資訊、地址資訊、差錯控制等)。
- 物理層 (physical Layer):確保資料可以在各種物理媒介上進行傳輸,為資料的傳輸提供可靠的環境。
從上圖中可以看出,TCP/IP
模型比OSI
模型更加簡潔,它把應用層/表示層/會話層
全部整合為了應用層
。
在每一層都工作著不同的裝置,比如我們常用的交換機就工作在資料鏈路層的,一般的路由器是工作在網路層的。 在每一層實現的協議也各不同,即每一層的服務也不同,下圖列出了每層主要的傳輸協議:
同樣,TCP/IP
五層協議的通訊方式也是對等通訊:
說一下JSON.stringify有什麼缺點?
1.如果obj裡面有時間物件,則JSON.stringify後再JSON.parse的結果,時間將只是字串的形式,而不是物件的形式
2.如果obj裡有RegExp(正規表示式的縮寫)、Error物件,則序列化的結果將只得到空物件;
3、如果obj裡有函式,undefined,則序列化的結果會把函式或 undefined丟失;
4、如果obj裡有NaN、Infinity和-Infinity,則序列化的結果會變成null
5、JSON.stringify()只能序列化物件的可列舉的自有屬性,例如 如果obj中的物件是有建構函式生成的, 則使用JSON.parse(JSON.stringify(obj))深複製後,會丟棄物件的constructor;
6、如果物件中存在迴圈引用的情況也無法正確實現深複製;
為什麼 0.1 + 0.2 != 0.3,請詳述理由
因為 JS 採用 IEEE 754 雙精度版本(64位),並且只要採用 IEEE 754 的語言都有該問題。
我們都知道計算機表示十進位制是採用二進位制表示的,所以 0.1
在二進位制表示為
// (0011) 表示迴圈
0.1 = 2^-4 * 1.10011(0011)
那麼如何得到這個二進位制的呢,我們可以來演算下
小數算二進位制和整數不同。乘法計算時,只計算小數位,整數位用作每一位的二進位制,並且得到的第一位為最高位。所以我們得出 0.1 = 2^-4 * 1.10011(0011)
,那麼 0.2
的演算也基本如上所示,只需要去掉第一步乘法,所以得出 0.2 = 2^-3 * 1.10011(0011)
。
回來繼續說 IEEE 754 雙精度。六十四位中符號位佔一位,整數位佔十一位,其餘五十二位都為小數位。因為 0.1
和 0.2
都是無限迴圈的二進位制了,所以在小數位末尾處需要判斷是否進位(就和十進位制的四捨五入一樣)。
所以 2^-4 * 1.10011...001
進位後就變成了 2^-4 * 1.10011(0011 * 12次)010
。那麼把這兩個二進位制加起來會得出 2^-2 * 1.0011(0011 * 11次)0100
, 這個值算成十進位制就是 0.30000000000000004
下面說一下原生解決辦法,如下程式碼所示
parseFloat((0.1 + 0.2).toFixed(10))
原型
JavaScript中的物件都有一個特殊的 prototype 內建屬性,其實就是對其他物件的引用
幾乎所有的物件在建立時 prototype 屬性都會被賦予一個非空的值,我們可以把這個屬性當作一個備用的倉庫
當試圖引用物件的屬性時會出發get操作,第一步時檢查物件本身是否有這個屬性,如果有就使用它,沒有就去原型中查詢。一層層向上直到Object.prototype頂層
基於原型擴充套件描述一下原型鏈,什麼是原型鏈,原型的繼承,ES5和ES6繼承與不同點。
Promise.all
描述:所有 promise
的狀態都變成 fulfilled
,就會返回一個狀態為 fulfilled
的陣列(所有promise
的 value
)。只要有一個失敗,就返回第一個狀態為 rejected
的 promise
例項的 reason
。
實現:
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
if(Array.isArray(promises)) {
if(promises.length === 0) return resolve(promises);
let result = [];
let count = 0;
promises.forEach((item, index) => {
Promise.resolve(item).then(
value => {
count++;
result[index] = value;
if(count === promises.length) resolve(result);
},
reason => reject(reason)
);
})
}
else return reject(new TypeError("Argument is not iterable"));
});
}
說一下data為什麼是一個函式而不是一個物件?
JavaScript中的物件是引用型別的資料,當多個例項引用同一個物件時,只要一個例項對這個物件進行操作,其他例項中的資料也會發生變化。而在Vue中,我們更多的是想要複用元件,那就需要每個元件都有自己的資料,這樣元件之間才不會相互干擾。所以元件的資料不能寫成物件的形式,而是要寫成函式的形式。資料以函式返回值的形式定義,這樣當我們每次複用元件的時候,就會返回一個新的data,也就是說每個元件都有自己的私有資料空間,它們各自維護自己的資料,不會干擾其他元件的正常執行。
參考 前端進階面試題詳細解答
實現模板字串解析
描述:實現函式使得將 template
字串中的{{}}
內的變數替換。
核心:使用字串替換方法 str.replace(regexp|substr, newSubStr|function)
,使用正則匹配代換字串。
實現:
function render(template, data) {
// 模板字串正則 /\{\{(\w+)\}\}/, 加 g 為全域性匹配模式, 每次匹配都會呼叫後面的函式
let computed = template.replace(/\{\{(\w+)\}\}/g, function(match, key) {
// match: 匹配的子串; key:括號匹配的字串
return data[key];
});
return computed;
}
// 測試
let template = "我是{{name}},年齡{{age}},性別{{sex}}";
let data = {
name: "張三",
age: 18
}
console.log(render(template, data)); // 我是張三,年齡18,性別undefined
Set,Map解構
ES6 提供了新的資料結構 Set。
它類似於陣列,但是成員的值都是唯一的,沒有重複的值。 Set 本身是一個建構函式,用來生成 Set 資料結構。
ES6 提供了 Map 資料結構。它類似於物件,也是鍵值對的集合,但是“鍵”的範圍不限於字串,各種型別的值(包括物件)都可以當作鍵。
localStorage sessionStorage cookies 有什麼區別?
localStorage:以鍵值對的方式儲存 儲存時間沒有限制 永久生效 除非自己刪除記錄
sessionStorage:當頁面關閉後被清理與其他相比不能同源視窗共享 是會話級別的儲存方式
cookies 資料不能超過4k 同時因為每次http請求都會攜帶cookie 所有cookie只適合儲存很小的資料 如會話標識
<script src=’xxx’ ’xxx’/>外部js檔案先載入還是onload先執行,為什麼?
onload 是所以載入完成之後執行的
Promise.reject
Promise.reject = function(reason) {
return new Promise((resolve, reject) => reject(reason));
}
程式碼輸出問題
function fun(n, o) {
console.log(o)
return {
fun: function(m){
return fun(m, n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1); c.fun(2); c.fun(3);
輸出結果:
undefined 0 0 0
undefined 0 1 2
undefined 0 1 1
這是一道關於閉包的題目,對於fun方法,呼叫之後返回的是一個物件。我們知道,當呼叫函式的時候傳入的實參比函式宣告時指定的形參個數要少,剩下的形參都將設定為undefined值。所以 console.log(o);
會輸出undefined。而a就是是fun(0)返回的那個物件。也就是說,函式fun中引數 n 的值是0,而返回的那個物件中,需要一個引數n,而這個物件的作用域中沒有n,它就繼續沿著作用域向上一級的作用域中尋找n,最後在函式fun中找到了n,n的值是0。瞭解了這一點,其他運算就很簡單了,以此類推。
說一下 web worker
在 HTML 頁面中,如果在執行指令碼時,頁面的狀態是不可相應的,直到指令碼執行完成後,頁面才變成可相應。web worker 是執行在後臺的 js,獨立於其他指令碼,不會影響頁面的效能。 並且透過 postMessage 將結果回傳到主執行緒。這樣在進行復雜操作的時候,就不會阻塞主執行緒了。
如何建立 web worker:
- 檢測瀏覽器對於 web worker 的支援性
- 建立 web worker 檔案(js,回傳函式等)
- 建立 web worker 物件
說一下常見的git操作
git branch 檢視本地所有分支
git status 檢視當前狀態
git commit 提交
git branch -a 檢視所有的分支
git branch -r 檢視遠端所有分支
git commit -am "nit" 提交併且加註釋
git remote add origin git@192.168.1.119:ndshow
git push origin master 將檔案給推到伺服器上
git remote show origin 顯示遠端庫origin裡的資源
git push origin master:develop
git push origin master:hb-dev 將本地庫與伺服器上的庫進行關聯
git checkout --track origin/dev 切換到遠端dev分支
git branch -D master develop 刪除本地庫develop
git checkout -b dev 建立一個新的本地分支dev
git merge origin/dev 將分支dev與當前分支進行合併
git checkout dev 切換到本地dev分支
git remote show 檢視遠端庫
git add .
git rm 檔名(包括路徑) 從git中刪除指定檔案
git clone git://github.com/schacon/grit.git 從伺服器上將程式碼給拉下來
git config --list 看所有使用者
git ls-files 看已經被提交的
git rm [file name] 刪除一個檔案
git commit -a 提交當前repos的所有的改變
git add [file name] 新增一個檔案到git index
git commit -v 當你用-v引數的時候可以看commit的差異
git commit -m "This is the message describing the commit" 新增commit資訊
git commit -a -a是代表add,把所有的change加到git index裡然後再commit
git commit -a -v 一般提交命令
git log 看你commit的日誌
git diff 檢視尚未暫存的更新
git rm a.a 移除檔案(從暫存區和工作區中刪除)
git rm --cached a.a 移除檔案(只從暫存區中刪除)
git commit -m "remove" 移除檔案(從Git中刪除)
git rm -f a.a 強行移除修改後檔案(從暫存區和工作區中刪除)
git diff --cached 或 $ git diff --staged 檢視尚未提交的更新
git stash push 將檔案給push到一個臨時空間中
git stash pop 將檔案從臨時空間pop下來
事件傳播機制(事件流)
冒泡和捕獲
Promise.resolve
Promise.resolve = function(value) {
// 1.如果 value 引數是一個 Promise 物件,則原封不動返回該物件
if(value instanceof Promise) return value;
// 2.如果 value 引數是一個具有 then 方法的物件,則將這個物件轉為 Promise 物件,並立即執行它的then方法
if(typeof value === "object" && 'then' in value) {
return new Promise((resolve, reject) => {
value.then(resolve, reject);
});
}
// 3.否則返回一個新的 Promise 物件,狀態為 fulfilled
return new Promise(resolve => resolve(value));
}
程式碼輸出結果
var A = {n: 4399};
var B = function(){this.n = 9999};
var C = function(){var n = 8888};
B.prototype = A;
C.prototype = A;
var b = new B();
var c = new C();
A.n++
console.log(b.n);
console.log(c.n);
輸出結果:9999 4400
解析:
- console.log(b.n),在查詢b.n是首先查詢 b 物件自身有沒有 n 屬性,如果沒有會去原型(prototype)上查詢,當執行var b = new B()時,函式內部this.n=9999(此時this指向 b) 返回b物件,b物件有自身的n屬性,所以返回 9999。
- console.log(c.n),同理,當執行var c = new C()時,c物件沒有自身的n屬性,向上查詢,找到原型 (prototype)上的 n 屬性,因為 A.n++(此時物件A中的n為4400), 所以返回4400。
程式碼輸出問題
window.number = 2;
var obj = {
number: 3,
db1: (function(){
console.log(this);
this.number *= 4;
return function(){
console.log(this);
this.number *= 5;
}
})()
}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number); // 15
console.log(window.number); // 40
這道題目看清起來有點亂,但是實際上是考察this指向的:
- 執行db1()時,this指向全域性作用域,所以window.number 4 = 8,然後執行匿名函式, 所以window.number 5 = 40;
- 執行obj.db1();時,this指向obj物件,執行匿名函式,所以obj.numer * 5 = 15。
實現 JSONP 跨域
JSONP 核心原理:script
標籤不受同源策略約束,所以可以用來進行跨域請求,優點是相容性好,但是隻能用於 GET 請求;
實現:
const jsonp = (url, params, callbackName) => {
const generateUrl = () => {
let dataSrc = "";
for(let key in params) {
if(params.hasOwnProperty(key)) {
dataSrc += `${key}=${params[key]}&`
}
}
dataSrc += `callback=${callbackName}`;
return `${url}?${dataSrc}`;
}
return new Promise((resolve, reject) => {
const scriptEle = document.createElement('script');
scriptEle.src = generateUrl();
document.body.appendChild(scriptEle);
window[callbackName] = data => {
resolve(data);
document.removeChild(scriptEle);
}
});
}
src和href的區別
src和href都是用來引用外部的資源,它們的區別如下:
- src: 表示對資源的引用,它指向的內容會嵌入到當前標籤所在的位置。src會將其指向的資源下載並應⽤到⽂檔內,如請求js指令碼。當瀏覽器解析到該元素時,會暫停其他資源的下載和處理,直到將該資源載入、編譯、執⾏完畢,所以⼀般js指令碼會放在頁面底部。
- href: 表示超文字引用,它指向一些網路資源,建立和當前元素或本文件的連結關係。當瀏覽器識別到它他指向的⽂件時,就會並⾏下載資源,不會停⽌對當前⽂檔的處理。 常用在a、link等標籤上。