寫在開頭
跳槽是為了得到更高的薪酬,或者為了尋找更具發展空間的公司一種常見的行為。我也于今年完成了一次跳槽,自然也有很多的感悟,作成本文供大家參考。
本文主要分成兩個部分,一部分是我自己整理的面試大綱,另外一部分是面試官考察的問題。
面試大綱
關于面試大綱,我認為每個人都是千差萬別的。因為面試官都是對簡歷所寫的項目經驗進行深挖或者對你所掌握的基本技能進行考察。
我在簡歷上寫的技能點包含了HTML
/CSS
/JavaScript
/ES6
/HTTP協議
/Vue
/微信小程序
/Node
/Express
/MySQL
/面向對象
/設計模式
根據以上,整理出面試大綱
一、HTML
<li>標簽之間有空格怎么處理?
原因:行框的排列會受到中間空白(回車空格)等的影響,因為空格也屬于字符,這些空白也會被應用樣式,占據空間,所以會有間隔,把字符大小設為0或者將<li>標簽寫在一排,就沒有空格了。
二、CSS
CSS3新特性
- 支持rgba和透明度
- 支持媒體查詢
- 支持自定義字體
- 對長的不可分割單詞換行
word-wrap:break-word;
- 文字陰影
text-shadow: 5px 5px 5px #ff0000;
- 盒陰影
box-shadow: 10px 10px 5px #888888
- 支持圓角
border-radius: 50%;
- 邊框圖片
border-image: url(border.png) 30 30 round
CSS優先級如何計算?
- 元素和偽元素:1
- 類選擇器、屬性選擇器或偽類:10
- id選擇符:100
- 內聯樣式:1000
- !important聲明的樣式優先級最高,如果沖突再進行計算。
- 繼承得到的樣式的優先級最低。
- 如果優先級相同,則選擇最后出現的樣式。
清除浮動的幾種方式和各自的優缺點
- 添加空div,使用
clear: both;
缺點:進行清除浮動;會添加很多無意義的空標簽,有違結構與表現的分離,在后期維護中將是噩夢; - 父元素使用
overflow: hidden;
缺點:不能配合position
一起使用,超出的部分會被隱藏; - 父元素使用
overflow: auto;
缺點:超出部分會出現滾動條; - 父級定義高度
- 推薦使用的方法:父級div定義偽類:after和zoom
<style>
.clearfloat {
*zoom: 1;
}
.clearfloat:after {
content: "";
height: 0;
display: block;
clear: both;
visibility: hidden;
}
</style>
<!-- *是為了兼容低版本IE7瀏覽器 zoom是ie屬性,設置縮放比例 -->
<div class="box clearfloat">
<div class="left">
<div class="right">
</div>
如何水平居中一個浮動元素?
#test {
position: absolute;
width: 100px;
height: 100px;
background-color: green;
<!-- 三行代碼缺一不可 -->
margin: 0 auto;
left: 0;
right: 0;
}
<div id="test"></div>
如何水平居中一個浮動元素?
#test {
position: absolute;
width: 100px;
height: 100px;
background-color: green;
<!-- 三行代碼缺一不可 -->
margin: 0 auto;
left: 0;
right: 0;
}
<div id="test"></div>
利用CSS畫三角形
#triangle {
width: 0;
height: 0;
border-top: 40px solid transparent;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
border-bottom: 40px solid greenyellow;
}
<div id="triangle"><div>
display: none;
和 visibility: hidden;
的區別?
-
display:none;
不顯示對應的元素,在文檔布局中不再分配空間(回流+重繪) -
visibility:hidden;
隱藏對應元素,在文檔布局中仍保留原來的空間(重繪)
position
和 float
值相互疊加會發生什么?
position:absolute/fixed;
優先級最高,有他們在時,float
不起作用,display
值需要調整。
對BFC規范(塊級格式化上下文:block formatting context)的理解?
BFC規定了內部的Block Box如何布局。
定位方案:
- 內部的Box會在垂直方向上一個接一個放置。
- Box垂直方向的距離由margin決定,屬于同一個BFC的兩個相鄰Box的margin會發生重疊。
- 每個元素的margin box 的左邊,與包含塊border box的左邊相接觸。
- BFC的區域不會與float box重疊。
- BFC是頁面上的一個隔離的獨立容器,容器里面的子元素不會影響到外面的元素。
- 計算BFC的高度時,浮動元素也會參與計算。
滿足下列條件之一就可觸發BFC
- 根元素,即html
- float的值不為none(默認)
- overflow的值不為visible(默認)
- display的值為inline-block、table-cell、table-caption
- position的值為absolute或fixed
為什么會出現浮動和什么時候需要清除浮動?清除浮動的方式?
浮動元素碰到包含它的邊框或者浮動元素的邊框停留。由于浮動元素不在文檔流中,所以文檔流的塊框表現得就像浮動框不存在一樣。浮動元素會漂浮在文檔流的塊框上。
浮動帶來的問題:
- 父元素的高度無法被撐開,影響與父元素同級的元素
- 與浮動元素同級的非浮動元素(內聯元素)會跟隨其后
- 若非第一個元素浮動,則該元素之前的元素也需要浮動,否則會影響頁面顯示的結構。
清除浮動的方式:
- 父級div定義height
- 最后一個浮動元素后加空div標簽 并添加樣式clear:both。
- 包含浮動元素的父標簽添加樣式overflow為hidden或auto。
- 父級div定義zoom
:after
和::after
的區別是什么?
- 在CSS3的規范里
:
表示偽類,::
表示偽元素 -
:after
在CSS2.1的規范中,表示偽元素,隨著WEB的發展,在CSS3的規范中,偽元素的語法被修改成使用雙冒號,成為::after
實現一個高度自適應的div,里面有兩個div,一個高度100px,希望另一個填滿剩下的高度
html, body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
#box {
position: relative;
height: 100%;
width: 200px;
background-color: beige;
}
#box .block-1 {
width: 200px;
height: 100px;
background-color: rosybrown;
}
#box .block-2 {
position: absolute;
top: 100px;
bottom: 0;
width: 200px;
background-color: red;
}
<div id="box">
<div class="block-1"></div>
<div class="block-2"></div>
</div>
三、JavaScript
簡單說說繼承的方式和優缺點?
原型鏈繼承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
function Child(name) {
this.name = name;
}
Child.prototype = new Parent('father');
Child.prototype.constructor = Child;
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
var child = new Child('son');
child.sayName(); // child name: son
// 這種方法存在兩個缺陷:
// 1.子類型無法給超類型傳遞參數,在面向對象的繼承中,我們總希望通過 var child = new Child('son', 'father'); 讓子類去調用父類的構造器來完成繼承。而不是通過像這樣 new Parent('father') 去調用父類。
// 2.Child.prototype.sayName 必須寫在 Child.prototype = new Parent('father') 之后,不然就會被覆蓋掉。
類式繼承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
Parent.prototype.doSomthing = function() {
console.log('parent do something!');
}
function Child(name, parentName) {
Parent.call(this, parentName);
this.name = name;
}
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
var child = new Child('son');
child.sayName(); // child name: son
child.doSomthing(); // TypeError: child.doSomthing is not a function
// 解決了原型鏈繼承帶來的問題
// 但存在缺陷:每次創建一個 Child 實例對象時候都需要執行一遍 Parent 函數,無法復用一些公用函數。
組合式繼承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
Parent.prototype.doSomething = function() {
console.log('parent do something!');
}
function Child(name, parentName) {
Parent.call(this, parentName); // 第二次調用
this.name = name;
}
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
Child.prototype = new Parent(); // 第一次調用
Child.prototype.construtor = Child;
var child = new Child('son');
child.sayName();
child.doSomething();
// 第一次調用構造函數顯然是沒有必要的,因為第一次調用構造函數時候不需要函數內部的那些實例屬性,這么寫只是想獲得其原型上的方法罷了
// 下面的寄生組合式繼承解決了這個問題
寄生組合式繼承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
function Child(name, parentName) {
Parent.call(this, parentName);
this.name = name;
}
Child.prototype = Object.create(Parent.prototype); //修改
Child.prototype.construtor = Child;
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
var parent = new Parent('father');
parent.sayName(); // parent name: father
var child = new Child('son', 'father');
child.sayName(); // child name: son
// tips:
/*
Object.create(proto, [propertiesObject])
Object.create()方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__
proto
新創建對象的原型對象。
propertiesObject 可選。
如果沒有指定為undefined,則是要添加到新創建對象的可枚舉屬性(即其自身定義的屬性,而不是其原型鏈上的枚舉屬性)對象的屬性描述符以及相應的屬性名稱。這些屬性對應Object.defineProperties()的第二個參數。
*/
談談JS中的this
-
this
是在執行上下文創建時確定的一個在執行過程中不可更改的變量 -
this
只在函數調用階段確定,也就是執行上下文創建的階段進行賦值,保存在變量對象中。
執行上下文,就是JavaScript引擎在執行一段代碼之前將代碼內部會用到的一些
變量
、函數
、this
提前聲明然后保存在變量對象中的過程。
運算符優先級
- ()括號 .成員訪問
- ()函數調用 new
- ++ -- !
- * %
- + -
- >= <= > <
- == !==
- & ^ | 位運算符
- && || 邏輯運算符
- a ? x : y 條件運算符
- = op = 運算賦值
// 本題的涉及考點主要還是運算符的優先級
function Foo() {
getName = function() {
console.log(1)
}
return this
}
Foo.getName = function() {
console.log(2)
}
Foo.prototype.getName = function() {
console.log(3)
}
var getName = function() {
console.log(4)
}
function getName() {
console.log(5)
}
// 寫出以下函數的執行結果
Foo.getName() // 2
getName() // 4
Foo().getName() // 1
getName() // 1
new Foo.getName() // 2
// new (Foo.getName)()
new Foo().getName() // 3
// (new Foo()).getName()
new new Foo().getName() // 3
// new((new Foo()).getName)()
簡單聊一聊包裝對象?
引用類型
和包裝對象
的區別在于生存期
引用類型所創建的對象會一直存在于堆內存中,而基本包裝對象只存在于一瞬間
var str = 'hello';
str.number = 10; //假設我們想給字符串添加一個屬性number ,后臺會有如下步驟
/*
var str = new String('hello'); // 1 找到對應的包裝對象類型,然后通過包裝對象創建出一個和基本類型值相同的對象
str.number = 10; // 2 通過這個對象調用包裝對象下的方法 但結果并沒有被任何東西保存
str =null; // 3 這個對象又被銷毀
*/
alert(str.number); //undefined 當執行到這一句的時候,因為基本類型本來沒有屬性,后臺又會重新重復上面的步驟
/*
var str = new String('hello'); // 1 找到基本包裝對象,然后又新開辟一個內存,創建一個值為hello對象
str.number = undefined // 2 因為包裝對象下面沒有number這個屬性,所以又會重新添加,因為沒有值,所以值是未定 ;然后彈出結果
str =null; // 3 這個對象又被銷毀
*/
instanceof 和 typeof
instanceof
運算符用于測試構造函數的prototype屬性是否出現在對象的原型鏈中的任何位置(Instanceof 的使用規則是: A instanceof B
A沿著proto這條線來找,同時B沿著prototype這條線來找,如果兩條線能找到同一個引用,即同一個對象,那么就返回true。如果找到終點還未重合,則返回false。)
函數/變量提升
// 1、函數提升優先級高于變量提升
// 2、表達式可以修改提升后變量的值
test() // 2
var test = function() {
console.log(1)
}
function test() {
console.log(2)
}
test() // 1
alert(a);
var a=1;
alert(a);
function a(){alert(2)};
alert(a);
var a=3;
alert(a);
function a(){alert(4)};
alert(a);
a();
// function a() {alert (4)};
// 1
// 1
// 3
// 3
// 報錯
new 操作符都干了些什么?
// new共經歷了四個階段
// 1、創建一個空對象
var obj = new Object()
// 2、設置原型鏈
obj.__proto__ = Func.prototype
// 3、讓Func中的this指向obj,并執行Func的函數體
var result = Func.call(obj)
// 4、判斷Func的返回值類型
// 如果是值類型,返回obj。如果是引用類型,就返回這個引用類型的對象
// (tip: 構造函數默認 return this,不用寫)
if(typeof result === 'object') {
return result
} eles {
return obj
}
四、Vue
對MVVM的理解
MVVM分為Model、View、ViewModel三者
Model
代表數據模型,數據和業務邏輯都在Model層中定義;
View
代表UI視圖,負責數據的展示;
ViewModel
負責監聽 Model 中數據的改變并且控制視圖的更新,處理用戶交互操作;
Model 和 View 并無直接關聯,而是通過 ViewModel 來進行聯系的,Model 和 ViewModel 之間有著雙向數據綁定的聯系。因此當 Model 中的數據改變時會觸發 View 層的刷新,View 中由于用戶交互操作而改變的數據也會在 Model 中同步。
這種模式實現了 Model 和 View 的數據自動同步,因此開發者只需要專注對數據的維護操作即可,而不需要自己操作 dom
Vue的雙向數據綁定是如何實現的?
簡單來說就是使用數據劫持
和發布訂閱的設計模式
實現的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>MVVM雙向數據綁定</title>
</head>
<body>
<script>
class Vue {
constructor(options) {
this.options = options
this.$data = options.data
this.$el = document.querySelector(options.el)
this._directives = {};
// 數據劫持
this.Observer(this.$data)
// 解析指令
this.Compile(this.$el)
}
Observer(data) {
for(let key in data) {
this._directives[key] = []
let val = data[key]
let _obj = this._directives[key]
Object.defineProperty(this.$data, key, {
get: function() {
return val
},
set: function(newVal) {
if(val !== newVal) {
val = newVal
_obj.forEach((item) => {
item.update()
})
}
}
})
}
}
Compile(el) {
let nodes = el.children
for(let i=0; i<nodes.length; i++) {
let node = nodes[i]
if(node.children.length) {
this.Compile(node)
}
if(node.hasAttribute('v-text')) {
let attrValue = node.getAttribute("v-text");
this._directives[attrValue].push(new Watch(node, this, attrValue, "innerHTML"))
}
if(node.hasAttribute('v-model')) {
let _this = this
let attrValue = node.getAttribute("v-model");
this._directives[attrValue].push(new Watch(node, this, attrValue, "value"))
node.addEventListener('input', (function(){
return function() {
_this.$data[attrValue] = node.value
}
})())
}
}
}
}
class Watch {
constructor(el, vm, exp, attr) {
this.el = el
this.vm = vm
this.exp = exp
this.attr = attr
this.update()
}
update() {
this.el[this.attr] = this.vm.$data[this.exp]
}
}
</script>
<div id="app">
<h1>數據雙向綁定</h1>
<div>
<div v-text="myText"></div>
<input type="text" v-model="myText">
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
myText: '今晚吃雞吶,大吉大利!'
}
});
</script>
</body>
</html>
Vue如何監聽某個屬性值的變化
watch: {
'obj.a': {
handler (newName, oldName) {
console.log('obj.a changed')
}
}
}
computed: {
a1 () {
return this.obj.a
}
}
Vue中給data中的對象屬性添加一個新的屬性時會發生什么,如何解決?
數據已經添加,但是視圖并沒有刷新;
原因在于在Vue實例創建時,obj.b
并未聲明,因此就沒有被Vue轉換為響應式的屬性,自然就不會觸發視圖的更新,這時就需要使用Vue的全局api Vue.$set()
addObjB () {
// this.obj.b = 'obj.b'
this.$set(this.obj, 'b', 'obj.b')
console.log(this.obj)
}
Vue組件通訊的方式
- 父子組件通訊:父->子:使用props,子->父:$emit方法傳遞參數
- 使用eventBus,就是創建一個事件中心,相當于中轉站,可以用它來傳遞事件和接收事件
- Vuex
可以去掘金找這類文章看一下,Vue組件的通訊方式達6種之多
Vue常用的修飾符
.prevent
: 提交事件不再重載頁面;
.stop
: 阻止單擊事件冒泡;
.self
: 當事件發生在該元素本身而不是子元素的時候會觸發;
.capture
: 事件偵聽,事件發生的時候會調用;
$route 和 $router 的區別
$route 是“路由信息對象”,包括path,params,hash,query,fullPath,matched,name等路由信息參數
$router 是“路由實例”對象包括了路由的跳轉方法,鉤子函數等
Vue的路由實現:hash模式 和 history模式
hash模式: 在瀏覽器中符號“#”,#以及#后面的字符稱之為hash,用window.location.hash讀取;
特點:hash雖然在URL中,但不被包括在HTTP請求中;用來指導瀏覽器動作,對服務端安全無用,hash不會重加載頁面。
hash 模式下,僅 hash 符號之前的內容會被包含在請求中,如 http://www.xxx.com,因此對于后端來說,即使沒有做到對路由的全覆蓋,也不會返回 404 錯誤。
history模式: history采用HTML5的新特性;且提供了兩個新方法:pushState(),replaceState()可以對瀏覽器歷史記錄棧進行修改,以及popState事件的監聽到狀態變更。
history 模式下,前端的 URL 必須和實際向后端發起請求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少對 /items/id 的路由處理,將返回 404 錯誤。Vue-Router 官網里如此描述:“不過這種模式要玩好,還需要后臺配置支持……所以呢,你要在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。”
對keep-alive的了解?
keep-alive是Vue內置的一個組件,可以使被包含的組件保留狀態,或避免重新渲染
在vue 2.1.0 版本之后,keep-alive新加入了兩個屬性: include(包含的組件緩存) 與 exclude(排除的組件不緩存,優先級大于include) 。
delete和Vue.delete刪除數組的區別?
delete只是被刪除的元素變成了 empty/undefined 其他的元素的鍵值還是不變;
Vue.delete直接刪除了數組 改變了數組的鍵值。
如何優化SPA應用的首屏加載速度慢的問題?
- 將公用的JS庫通過script標簽外部引入,減小app.bundel的大小,讓瀏覽器并行下載資源文件,提高下載速度;
- 在配置 路由時,頁面和組件使用懶加載的方式引入,進一步縮小 app.bundel 的體積,在調用某個組件時再加載對應的js文件;
- 加一個首屏 loading 圖,提升用戶體驗;
Vuex是什么?如何使用?哪種場景使用它?
將需要讀取的狀態集中放在store
中
改變狀態的方式是提交mutations
,這是一個同步的事務
異步邏輯應該封裝在actions
中
state Vuex 使用單一狀態樹,即每個應用將僅僅包含一個store 實例,但單一狀態樹和模塊化并不沖突。存放的數據狀態,不可以直接修改里面的數據。
mutations 定義的方法動態修改Vuex
的store
中的狀態或數據。
getters 類似vue
的計算屬性,主要用來過濾一些數據。
action 可以理解為通過將mutations
里面處里數據的方法變成可異步的處理數據的方法,簡單的說就是異步操作數據。view層通過store.dispath
來分發action
modules 項目特別復雜的時候,可以讓每一個模塊擁有自己的state、mutation、action、getters,使得結構非常清晰,方便管理
Vue等單頁應用的優缺點
優點:Vue的目標是通過盡可能簡單的API實現響應的數據綁定和組合的視圖組件,核心是響應式系統。
缺點:不支持低版本瀏覽器,最低僅支持ie9;不利于SEO的優化,首頁加載耗時相對偏長一些。
四、ES6
let
和var
的區別
- 不存在變量提升
- 暫時性的死區
if (true) {
// --- 死區開始 ---
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp;
// --- 死區結束 ---
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
-
let
為JavaScript新增了塊級作用域
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
// 變量i是var命令聲明的,在全局范圍內都有效,所以全局只有一個變量i,每一次循環,變量i的值都會發生改變
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
// 變量i是let聲明的,當前的i只在本輪循環有效,所以每一次循環的i其實都是一個新的變量,所以最后輸出的是6。
解構賦值
定義:從數組或對象中提取值,對變量進行賦值
- 數組的解構賦值
let [a, b, c] = [1, 2, 3];
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
let [x, y = 10] = [7]
- 對象的解構賦值
(tip: 數組的解構賦值對順序有要求,而對象則沒有要求,變量必須與屬性同名,才能取到正確的值)
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
var {x, y = 5} = {x: 1};
// 如果變量名與屬性名不一致,必須寫成下面這樣。
// foo 是模式, baz 才是變量,特別要注意區分模式和變量
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
- 嵌套解構賦值
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
// p 是模式,不會被復制,若想被賦值,可以寫成如下形式
let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
箭頭函數和非箭頭函數的區別
- 箭頭函數的
this
指向在定義的時候繼承自外層的第一個普通函數的this
,若外層沒有普通函數,指向的是window
- 不能直接修改箭頭函數的
this
指向 - 箭頭函數的this指向全局,使用
arguments
會報未聲明的錯誤。 - 箭頭函數的this指向普通函數時,它的
argumens
繼承于該普通函數 - 使用
new
調用箭頭函數會報錯,因為箭頭函數沒有constructor
簡單講講Promise
Promise的特點
狀態一旦改變就再也不會發生改變了
Promise的缺點
- 無法取消Promise,一旦新建它就會立即執行,無法中途取消;
- 如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部;
- 當處于pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
如何創建Promise實例?
const promise = new Promise(function(resolve, reject) {
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
簡單講講Promise API
- Promise.resolve();
// 1. 如果傳入的 value 本身就是 Promise 對象,則該對象作為 Promise.resolve 方法的返回值返回。
function fn(resolve){
setTimeout(function(){
resolve(123);
},3000);
}
let p0 = new Promise(fn);
let p1 = Promise.resolve(p0);
// 返回為true,返回的 Promise 即是 入參的 Promise 對象。
console.log(p0 === p1);
//2. 如果這個值是個 thenable(thenable對象指的是具有then方法的對象),返回的 Promise 對象會“跟隨”這個 thenable 的對象,采用它的最終狀態(指 resolved/rejected/pending/settled)
let promise = Promise.resolve($.ajax('/test/test.json'));// => promise對象
promise.then(function(value){
console.log(value);
});
- Promise.reject();
- Promise.race
// 類方法,多個 Promise 任務同時執行,返回最先執行結束的 Promise
// 任務的結果,不管這個 Promise 結果是成功還是失敗。 。
- Promise.all
// 類方法,多個 Promise 任務同時執行。
// 如果全部成功執行,則以數組的方式返回所有 Promise 任務的執行結果。
// 如果有一個 Promise 任務 rejected,則只返回 rejected 任務的結果。
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return getJSON('/post/' + id + ".json");
});
Promise.all(promises).then(function (posts) {
// ...
}).catch(function(reason){
// ...
});
- Promise.prototype.then()
- Promise.prototype.catch() 該方法可以捕獲到then鏈上發生的任何一個錯誤
- Promise.prototype.finally() 該方法用于指定不管
Promise
對象最后狀態如何,都會執行的操作
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {
// finally方法的回調函數不接受任何參數,這意味著沒有辦法知道,
//前面的 Promise 狀態到底是fulfilled還是rejected。這表明,
// finally方法里面的操作,應該是與狀態無關的,不依賴于 Promise 的執行結果。
});
關于Promise
,不同面試官考察的深度因人而異,反正做好手寫實現Promise
的準備即可
五、微信小程序
簡述微信小程序原理
- 小程序本質就是一個單頁應用,所有的頁面渲染和事件處理,都在一個頁面內進行,但又可以通過微信客戶端調用原生的各種接口;
- 功能可分為渲染層
webview
和邏輯層appService
兩個部分;
webview
用來展現UI,appService
有來處理業務邏輯、數據及接口調用;
兩個部分在兩個進程中運行,通過系統層JSBridge
實現通信,實現UI的渲染、事件的處理等。
為什么微信小程序的DOM API不完整?
因為微信小程序的渲染線程和腳本線程是分開的,腳本線程獨立在 JSCore
中,沒有一個完整的瀏覽器對象。
簡單談談WXS?
WXS(weixin script),結合HTML,構建頁面結構。
<wxs module="m1">var msg = "hello world"; module.exports.message = msg;</wxs>
<view>{{m1.message}}</view>
為什么setData操作會很昂貴?
頻繁用戶交互的效果在小程序上表現是比較卡頓的, 例如頁面有 2 個元素 A 和 B,用戶在 A 上做 touchmove 手勢,要求 B 也跟隨移動。一次 touchmove
事件的響應過程為:
a、touchmove 事件從視圖層(Webview)拋到邏輯層(App Service)
b、邏輯層(App Service)處理 touchmove 事件,再通過 setData 來改變 B 的位置
一次 touchmove 的響應需要經過 2 次的邏輯層和渲染層的通信以及一次渲染,通信(JSBridge)的耗時比較大。此外 setData 渲染也會阻塞其它腳本執行,導致了整個用戶交互的動畫過程會有延遲。
談談小程序的生命周期函數
onLoad() 頁面加載時觸發,只會調用一次,可獲取當前頁面路徑中的參數;
onShow() 頁面顯示/切入前臺時觸發,一般用來發送數據請求(或者 set to foreground);
onReady() 頁面初次渲染完成時觸發 只會調用一次,代表頁面已可和視圖層進行交互(當邏輯層通知渲染層 Send Initial Data,且渲染層 First Render 之后調用);
onHide() 頁面隱藏/切入后臺時觸發(set to background), 如底部tab切換到其他頁面或小程序切入后臺等;
onUnload() 頁面卸載時觸發(destroy),如redirectTo或navigateBack到其他頁面時。
小程序頁面有哪些傳遞數據的方法
- 使用全局變量實現數據傳遞
- 頁面跳轉或重定向時,使用url帶參數傳遞數據
- 使用組件模板 template傳遞參數
- 使用緩存傳遞參數
- 使用數據庫傳遞數據
請談談微信小程序主要目錄和文件的作用?
- project.config.json 項目配置文件,用得最多的就是配置是否開啟https校驗;
- App.js 設置一些全局的基礎數據等;
- App.json 底部tab, 標題欄, 小程序的window背景色和路由等設置;
- App.wxss 公共樣式,引入iconfont等;
- pages 里面包含一個個具體的頁面;
- index.json (配置當前頁面標題和引入組件等);
- index.wxml (頁面結構);
- index.wxss (頁面樣式表);
- index.js (頁面的邏輯,請求和數據處理等);
談談微信小程序.wxss樣式
- 新增加了尺寸單位rpx(tip: rpx換算px規則是屏幕寬度/750, iphone6下,1rpx = 0.5px)
- 提供了全局樣式(app.wxss)和局部樣式(page.wxss)
- WXSS 僅支持部分 CSS 選擇器( > :first-of-type :nth-child 均不支持)
六、HTTP協議
HTTP請求中的內容
- 請求行
GET /images/logo.gif HTTP/1.1
- 首部
- 通用首部
- 請求首部
- 響應首部
- 實體首部
- 實體
HTTP狀態碼
- 1**: 信息,服務器收到請求,需要請求者繼續執行操作
- 2**: 成功,操作被成功接收并處理
- 3**: 重定向,需要進一步的操作以完成請求
- 304 Not Modified 所請求的資源未修改,服務器返回此狀態碼時,不會返回任何資源。
- 4**: 客戶端錯誤,請求包含語法錯誤或無法完成請求
- 400 客戶端請求的語法錯誤,服務端無法理解
- 403 Forbidden 服務器理解請求客戶端的請求,但是拒絕執行此請求
- 404 Not Found 服務器無法根據客戶端的請求找到資源(網頁)
- 5**: 服務器錯誤,服務器在處理請求的過程中發生了錯誤
- 500 服務器內部錯誤
- 502 Bad Gateway 作為網關或者代理工作的服務器嘗試執行請求時,從遠程服務器接收到了一個無效的響應
RTT
往返時延,在計算機網絡中它也是一個重要的性能指標,它表示從發送端發送數據開始,到發送端收到來自接收端的確認(接收端收到數據后便立即發送確認),總共經歷的時延;
UDP協議
只是數據報文的搬運工,不會對報文進行拆分和拼接操作,不保證有序且不丟失的傳遞到對端
- 面向無連接
- 不可靠 -> 高效
在直播行業和即時對戰游戲等實時性要求高的行業,UDP協議使用廣泛
TCP協議
TCP 基本是和 UDP 反著來,建立連接斷開連接都需要先需要進行握手。在傳輸數據的過程中,通過各種算法保證數據的可靠性,當然帶來的問題就是相比 UDP 來說不那么的高效。是全雙工協議。
標識符
- URG=1:該字段為一表示本數據報的數據部分包含緊急信息,是一個高優先級數據報文,此時緊急指針有效。緊急數據一定位于當前數據包數據部分的最前面,緊急指針標明了緊急數據的尾部。
- ACK=1:代表確認接受,確認號字段有效。此外,TCP 還規定在連接建立后傳送的所有報文段都必須把 ACK 置為一。
- PSH=1:該字段為一表示接收端應該立即將數據 push 給應用層,而不是等到緩沖區滿后再提交。
- RST=1:該字段為一表示當前 TCP 連接出現嚴重問題,可能需要重新建立 TCP 連接,也可以用于拒絕非法的報文段和拒絕連接請求。
- SYN=1:代表請求創立連接,當SYN=1,ACK=0時,表示當前報文段是一個連接請求報文。當SYN=1,ACK=1時,表示當前報文段是一個同意建立連接的應答報文。
- FIN=1:代表請求釋放連接。
- seq:序列號,什么意思呢?當發送一個數據時,數據是被拆成多個數據包來發送,序列號就是對每個數據包進行編號,這樣接受方才能對數據包進行再次拼接。
- ack:這個代表下一個數據包的編號,這也就是為什么第二請求時,ack是seq + 1(tip: ack和ACK代表的意思不同)
三次握手
客戶端 ------ SYN = 1, ACK = 0, seq = x ------- > 服務端
客戶端 < ----- SYN = 1, ACK = 1, seq = y, ack = x + 1 -------- 服務端
客戶端 ------ ACK = 1, seq = x + 1, ack = y + 1------- > 服務端
四次揮手
客戶端 ------ FIN ------- > 服務端
客戶端 < ----- ACK -------- 服務端
客戶端 < ----- FIN -------- 服務端
客戶端 ------ ACK ------- > 服務端
ARQ協議
ARQ協議其實就是超時重傳機制。通過確認和超時重傳機制保證數據的正確送達。
- 停止等待ARQ協議
- 連續ARQ協議
連續ARQ協議 - > 滑動窗口
發送端窗口包含已發送但未收到應答的數據
和待發送的數據
發送端窗口是由接收窗口剩余大小決定的。接收方會把當前接收窗口的剩余大小寫入應答報文,發送端收到應答后根據該值和當前網絡擁塞情況設置發送窗口的大小,所以發送窗口的大小是不斷變化的。
滑動窗口解決了數據的丟包、順序不對和流量控制問題
擁塞處理
- 慢開始算法
- 擁塞避免算法
- 快速重傳
- 快速恢復
七、面向對象
面向對象的基本要素
繼承
封裝
和多態
JS適合用來面向對象嗎?
你在工作中是如何使用面向對象思想的?
明天需要上班,后續跟進補充!