上期整理了一些vue面試題,本期整理總結這些前端面試題,涵蓋面很廣,而且面的都是知名大廠,因此這些題仍是頗有表明性的,都掌握之後一面基礎面應該沒什麼問題,二面也能應付大半,奉上:css
1.margin: 0 auto;水平
2.text-align: center;水平
3.行高,垂直
4.表格,center,middle;水平垂直
5.display:table-cell;模擬表格,all
6.絕對定位,50%減自身寬高
7.絕對定位,上下左右全0,margin:auto
8.絕對定位加相對定位。不須要知道寬高
9.IE6,IE7:給父元素設一個font-size:高度/1.14,vertical-align:middlehtml
塊格式化上下文, 特性:前端
當父元素不給高度的時候,內部元素不浮動時會撐開, 而浮動的時候,父元素變成一條線, 形成塌陷.vue
好比antd的row和col, 將一行等分爲24份, col是幾就佔幾份, 底層按百分比實現; 結合媒體查詢, 能夠實現響應式java
// 經過設置border
.box
{
width:0px;
height:0px;
border-top:50px solid rgba(0,0,0,0);
border-right:50px solid rgba(0,0,0,0);
border-bottom:50px solid green;
border-left:50px solid rgba(0,0,0,0);
}
複製代碼
四個小圓點一直旋轉node
// 父標籤
animation: antRotate 1.2s infinite linear;
// 子標籤
animation: antSpin 1s infinite linear;
@keyframe antSpin {
to {
opacity: 1
}
}
@keyframe antRotate {
to {
transform: rotate(405)
}
}
// animation-delay: 逐個延遲0.4s
複製代碼
<div style="font-size: 20px">
123
<div style="font-size: 2em;width: 2em">456</div>
</div>
// 此時子元素的font-size爲40px, 寬度爲80px(還要乘以子元素font-size的係數)
複製代碼
vw:viewpoint width,視窗寬度,1vw等於視窗寬度的1%。
vh:viewpoint height,視窗高度,1vh等於視窗高度的1%。
vmin:vw和vh中較小的那個。
vmax:vw和vh中較大的那個。python
overflow: hidden
能清除塊內子元素的浮動影響. 由於該屬性進行超出隱藏時須要計算盒子內全部元素的高度, 因此會隱式清除浮動width
百分比, height: 0
, padding-top(bottom): 50%
box-sizing: border-box
; 標準模式: box-sizing: content-box
兩個對半矩形遮罩, 使用rotate
以及overflow: hidden
進行旋轉react
選擇器的特殊性值表述爲4個部分,用0,0,0,0表示。jquery
ES6 提供的一種異步編程解決方案, Generator 函數是一個狀態機,封裝了多個內部狀態。webpack
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
複製代碼
調用後返回指向內部狀態的指針, 調用next()纔會移向下一個狀態, 參數:
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
複製代碼
var myPromise = new Promise((resolve, reject) => {
// 須要執行的代碼
...
if (/* 異步執行成功 */) {
resolve(value)
} else if (/* 異步執行失敗 */) {
reject(error)
}
})
myPromise.then((value) => {
// 成功後調用, 使用value值
}, (error) => {
// 失敗後調用, 獲取錯誤信息error
})
複製代碼
function promise () {
this.msg = '' // 存放value和error
this.status = 'pending'
var that = this
var process = arguments[0]
process (function () {
that.status = 'fulfilled'
that.msg = arguments[0]
}, function () {
that.status = 'rejected'
that.msg = arguments[0]
})
return this
}
promise.prototype.then = function () {
if (this.status === 'fulfilled') {
arguments[0](this.msg)
} else if (this.status === 'rejected' && arguments[1]) {
arguments[1](this.msg)
}
}
複製代碼
又稱發佈-訂閱模式, 舉例子說明.
實現: 發佈者管理訂閱者隊列, 並有新消息推送功能. 訂閱者僅關注更新就行
Function.prototype.bind = function () {
// 保存原函數
var self = this
// 取出第一個參數做爲上下文, 至關於[].shift.call(arguments)
var context = Array.prototype.shift.call(arguments)
// 取剩餘的參數做爲arg; 由於arguments是僞數組, 因此要轉化爲數組才能使用數組方法
var arg = Array.prototype.slice.call(arguments)
// 返回一個新函數
return function () {
// 綁定上下文並傳參
self.apply(context, Array.prototype.concat.call(arg, Array.prototype.slice.call(arguments)))
}
}
複製代碼
function Father () {}
function Child () {}
// 1\. 原型繼承
Child.prototype = new Father()
// 2\. 構造繼承
function Child (name) {
Father.call(this, name)
}
// 3\. 組合繼承
function Child (name) {
Father.call(this, name)
}
Child.prototype = new Father()
// 4\. 寄生繼承
function cloneObj (o) {
var clone = object.create(o)
clone.sayName = ...
return clone
}
// 5\. 寄生組合繼承
// 6\. ES6 class extend繼承
複製代碼
四個小圓點一直旋轉
// 父標籤
animation: antRotate 1.2s infinite linear;
// 子標籤
animation: antSpin 1s infinite linear;
@keyframe antSpin {
to {
opacity: 1
}
}
@keyframe antRotate {
to {
transform: rotate(405)
}
}
// animation-delay: 逐個延遲0.4s
複製代碼
function jsonp ({url, param, callback}) {
return new Promise((resolve, reject) => {
var script = document.createElement('script')
window.callback = function (data) {
resolve(data)
document.body.removeChild('script')
}
var param = {...param, callback}
var arr = []
for (let key in param) {
arr.push(`${key}=${param[key]}`)
}
script.src = `${url}?${arr.join('&')}`
document.body.appendChild(script)
})
}
複製代碼
// for循環實現
Array.prototype.myMap = function () {
var arr = this
var [fn, thisValue] = Array.prototype.slice.call(arguments)
var result = []
for (var i = 0; i < arr.length; i++) {
result.push(fn.call(thisValue, arr[i], i, arr))
}
return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))
// forEach實現(reduce相似)
Array.prototype.myMap = function (fn, thisValue) {
var result = []
this.forEach((v, i, arr) => {
result.push(fn.call(thisValue, v, i, arr))
})
return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))
複製代碼
<body>
<button id="other">反選</button>
<input type="checkbox" id="all" />全選
<input type="checkbox" class="check" />1
<input type="checkbox" class="check" />2
<input type="checkbox" class="check" />3
<script>
var checkbox = document.getElementsByClassName('check')
var checkAll = document.getElementById('all')
var checkOther = document.getElementById('other')
checkAll.onclick = function() {
var flag = true
for (var i = 0; i < checkbox.length; i++) {
if (!checkbox[i].checked) flag = false
}
if (flag) {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = false
}
} else {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = true
}
}
}
checkOther.onclick = function() {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = !checkbox[i].checked
}
}
</script>
</body>
複製代碼
一般在通常的項目裏不須要,由於應用簡單,但你要用純js作一些複雜的工具或框架系統就要用到了,好比webgis、或者js框架如jquery、ext什麼的,否則一個幾千行代碼的框架不用繼承得寫幾萬行,甚至還沒法維護
單線程, 先執行同步主線程, 再執行異步任務隊列
塊級做用域, 暫時性死區
// 函數節流 滾動條滾動
var canRun = true;
document.getElementById("throttle").onscroll = function(){
if(!canRun){
// 判斷是否已空閒,若是在執行中,則直接return
return;
}
canRun = false;
setTimeout(function(){
console.log("函數節流");
canRun = true;
}, 300);
};
複製代碼
// 函數防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
clearTimeout(timer); // 清除未執行的代碼,重置回初始化狀態
timer = setTimeout(function(){
console.log("函數防抖");
}, 300);
};
複製代碼
// 這種實現方式是利用一個僞死循環阻塞主線程。由於JS是單線程的。因此經過這種方式能夠實現真正意義上的sleep()。
function sleep(delay) {
var start = (new Date()).getTime();
while ((new Date()).getTime() - start < delay) {
continue;
}
}
function test() {
console.log('111');
sleep(2000);
console.log('222');
}
test()
複製代碼
Facebook出品, 倡導數據的不可變性, 用的最多就是List和Map.
// 檢測l的原型鏈(__proto__)上是否有r.prototype,如有返回true,不然false
function myInstanceof (l, r) {
var R = r.prototype
while (l.__proto__) {
if (l.__proto__ === R) return true
}
return false
}
複製代碼
// 嚴格模式下, 隱式綁定丟失後this不會指向window, 而是指向undefined
'use strict'
var a = 2
var obj = {
a: 1,
b: function() {
// console.log(this.a)
console.log(this)
}
}
var c = obj.b
c() // undefined
複製代碼
// 模擬構造函數實現
var Book = function(name) {
this.name = name;
};
//正經常使用法
var java = new Book(‘Master Java’);
//使用代碼模擬,在非IE瀏覽器中測試,IE瀏覽器不支持
var python = {};
python.__proto__ = Book.prototype;
Book.call(python, 'Master Python');
複製代碼
for in
遍歷數組會遍歷到數組原型上的屬性和方法, 更適合遍歷對象forEach
不支持break, continue, return
等for of
能夠成功遍歷數組的值, 而不是索引, 不會遍歷原型使用消息隊列以及setInterval
或promise
進行入隊和出隊
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
複製代碼
虛擬DOM可提高性能, 無須總體從新渲染, 而是局部刷新.
JS對象, diff算法
跨域是指一個域下的文檔或腳本試圖去請求另外一個域下的資源
防止XSS、CSFR等攻擊, 協議+域名+端口不一樣
jsonp; 跨域資源共享(CORS)(Access control); 服務器正向代理等
預檢請求: 需預檢的請求要求必須首先使用 OPTIONS 方法發起一個預檢請求到服務器,以獲知服務器是否容許該實際請求。"預檢請求「的使用,能夠避免跨域請求對服務器的用戶數據產生未預期的影響
DOM自己是一個js對象, 操做這個對象自己不慢, 可是操做後觸發了瀏覽器的行爲, 如repaint和reflow等瀏覽器行爲, 使其變慢
判斷有無全局對象global和window
圖片等靜態資源在使用以前就提早請求
資源使用到的時候能從緩存中加載, 提高用戶體驗
頁面展現的依賴關係維護
chrome控制檯的application下可查看:
Cache-control, expire, last-modify
正向代理:
(1)訪問原來沒法訪問的資源,如google
(2) 能夠作緩存,加速訪問資源
(3)對客戶端訪問受權,上網進行認證
(4)代理能夠記錄用戶訪問記錄(上網行爲管理),對外隱藏用戶信息
反向代理:
(1)保證內網的安全,可使用反向代理提供WAF功能,阻止web攻擊大型網站,一般將反向代理做爲公網訪問地址,Web服務器是內網。
(2)負載均衡,經過反向代理服務器來優化網站的負載
在非簡單請求且跨域的狀況下,瀏覽器會自動發起options預檢請求。
經過v-model
VUE實現雙向數據綁定的原理就是利用了 Object.defineProperty() 這個方法從新定義了對象獲取屬性值(get)和設置屬性值(set)的操做來實現的。
// 依賴收集
// 簡化版
var obj = { }
var name
//第一個參數:定義屬性的對象。
//第二個參數:要定義或修改的屬性的名稱。
//第三個參數:將被定義或修改的屬性描述符。
Object.defineProperty(obj, "data", {
//獲取值
get: function () {
return name
},
//設置值
set: function (val) {
name = val
console.log(val)
}
})
//賦值調用set
obj.data = 'aaa'
//取值調用get
console.log(obj.data)
// 詳細版
myVue.prototype._obverse = function (obj) { // obj = {number: 0}
var value;
for (key in obj) { //遍歷obj對象
if (obj.hasOwnProperty(key)) {
value = obj[key];
if (typeof value === 'object') { //若是值是對象,則遞歸處理
this._obverse(value);
}
Object.defineProperty(this.$data, key, { //關鍵
enumerable: true,
configurable: true,
get: function () {
console.log(`獲取${value}`);
return value;
},
set: function (newVal) {
console.log(`更新${newVal}`);
if (value !== newVal) {
value = newVal;
}
}
})
}
}
}
複製代碼
// 1\. 新建eventBus.js
import Vue from 'vue'
export default new Vue
// 或直接在main.js中初始化EventBus(全局)
Vue.prototype.$EventBus = new Vue()
// 2\. 發射與接收
// 若是是定義在eventBus.js中
import eventBus from 'eventBus.js'
eventBus.$emit()
eventBus.$on()
// 若是是定義在main.js中
this.bus.$emit()
this.bus.$on()
// 3\. 移除監聽
eventBus.$off()
複製代碼
可使用獨立可複用的自定義組件來構成大型應用, 採用帕斯卡命名法或橫線鏈接, 經過以上方式進行組件間通訊. 每個組件都是Vue實例, 可使用生命週期鉤子.
seo關係到網站排名, vue搭建spa作先後端分離很差作seo, 可經過其餘方法解決:
10.預渲染和ssr
以上
對應一個對象,鍵是觀察表達式,值是對應回調。值也能夠是methods的方法名,或者是對象,包含選項。在實例化時爲每一個鍵調用 $watch()
routes = [
{
name: 'detail',
path: '/detail',
meta: {
requireAuth: true
}
},
{
name: 'login',
path: '/login'
}
]
複製代碼
router.beforeEach((from, to, next) => {
if (to.meta.requireAuth) { // 判斷跳轉的路由是否須要登陸
if (store.state.token) { // vuex.state判斷token是否存在
next() // 已登陸
} else {
next({
path: '/login',
query: {redirect: to.fullPath} // 將跳轉的路由path做爲參數,登陸成功後跳轉到該路由
})
}
} else {
next()
}
})
複製代碼
不寫key值會報warning, 和react的array渲染相似. 根據diff算法, 修改數組後, 寫key值會複用, 不寫會從新生成, 形成性能浪費或某些沒必要要的錯誤
正常要經過vm.[圖片上傳失敗...(image-6d2f4e-1570591304185)]
root傳參取值
let obj = JSON.parse(JSON.stringify(this.temp1));
爲了簡化,Vue 容許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue 只有在這個組件須要被渲染的時候纔會觸發該工廠函數,且會把結果緩存起來供將來重渲染
Vue.component(
'async-webpack-example',
// 這個 `import` 函數會返回一個 `Promise` 對象。
() => import('./my-async-component')
)
複製代碼
小到能夠只使用核心功能,好比單文件組件做爲一部分嵌入;大到使用整個工程,vue init webpack my-project來構建項目;VUE的核心庫及其生態系統也能夠知足你的各式需求(core+vuex+vue-route)
getDerivedStateFromProps
: 虛擬dom以後,實際dom掛載以前, 每次獲取新的props或state以後, 返回新的state, 配合didUpdate能夠替代willReceivePropsgetSnapshotBeforeUpdate
: update發生的時候,組件更新前觸發, 在render以後,在組件dom渲染以前;返回一個值,做爲componentDidUpdate的第三個參數;配合componentDidUpdate, 能夠覆蓋componentWillUpdate的全部用法componentDidCatch
: 錯誤處理高階組件就是一個函數,且該函數(wrapper)接受一個組件做爲參數,並返回一個新的組件。
高階組件並不關心數據使用的方式和緣由,而被包裹的組件也不關心數據來自何處.
export default DragSource(type, spec, collect)(MyComponent)
在無狀態組件(如函數式組件)中也能操做state以及其餘react特性, 經過useState
React是單向數據流,數據主要從父節點傳遞到子節點(經過props)。若是頂層(父級)的某個props改變了,React會重渲染全部的子節點。
react樹對比是按照層級去對比的, 他會給樹編號0,1,2,3,4.... 而後相同的編號進行比較。 因此複雜度是n,這個好理解。
關鍵是傳統diff的複雜度是怎麼算的? 傳統的diff須要出了上面的比較以外,還須要跨級比較。 他會將兩個樹的節點,兩兩比較,這就有n^2的複雜度了。 而後還須要編輯樹,編輯的樹可能發生在任何節點,須要對樹進行再一次遍歷操做,所以複雜度爲n。加起來就是n^3了。
聲明式, 組件化, 一次學習, 隨處編寫. 靈活, 豐富, 輕巧, 高效
判斷機型, 找出樣本機型去適配. 好比iphone以6爲樣本, 寬度375px, dpr是2
iPhone6的滿屏寬度是375px,而iPhone6採用的視網膜屏的物理像素是滿屏寬度的2倍,也就是dpr(設備像素比)爲2, 而且設計師所用的PS設計軟件分辨率和像素關係是1:1。因此爲了作出的清晰的頁面,設計師通常給出750px的設計圖,咱們再根據需求對元素的尺寸設計和壓縮。
safe area
: 默認放置在安全區域以免遮擋, 但會壓縮viewport-fit=cover
: 告訴瀏覽器要講整個頁面渲染到瀏覽器中,無論設備是圓角與否,這個時候會形成頁面的元素被圓角遮擋padding: constant(env)
: 解決遮擋問題一個 PWA 應用首先是一個網頁, 能夠經過 Web 技術編寫出一個網頁應用. 隨後添加上 App Manifest 和 Service Worker 來實現 PWA 的安裝和離線等功能
解決了哪些問題?
如今 web 頁面在移動端的地位愈來愈高,大部分主流 App 採用 native + webview 的 hybrid 模式,加載遠程頁面受限於網絡,本地 webview 引擎,常常會出現渲染慢致使的白屏現象,體驗不好,因而離線包方案應運而生。動態下載的離線包可使得咱們不須要走完整的 App 審覈發佈流程就完成了版本的更新
自適應佈局經過檢測視口分辨率,來判斷當前訪問的設備是:pc端、平板、手機,從而請求服務層,返回不一樣的頁面;響應式佈局經過檢測視口分辨率,針對不一樣客戶端在客戶端作代碼處理,來展示不一樣的佈局和內容。
自適應佈局須要開發多套界面,而響應式佈局只須要開發一套界面就能夠了。
自適應對頁面作的屏幕適配是在必定範圍:好比pc端通常要大於1024像素,手機端要小於768像素。而響應式佈局是一套頁面所有適應。
自適應佈局若是屏幕過小會發生內容過於擁擠。而響應式佈局正是爲了解決這個問題而衍生出的概念,它能夠自動識別屏幕寬度並作出相應調整的網頁設計。
Babel
: Babel 是一個普遍使用的 ES6 轉碼器,能夠將 ES6 代碼轉爲 ES5 代碼。注意:Babel 默認只轉換新的 JavaScript 句法(syntax),而不轉換新的 API
Polyfill
: Polyfill的準確意思爲,用於實現瀏覽器並不支持的原生API的代碼。
JsBridge給JavaScript提供了調用Native功能,Native也可以操控JavaScript。這樣前端部分就能夠方便使用地理位置、攝像頭以及登陸支付等Native能力啦。JSBridge構建 Native和非Native間消息通訊的通道,並且是 雙向通訊的通道。
2.實現一個簡單的 JSBridge,設計思路?
function quickSort (arr) {
if (arr.length < 2) return arr
var middle = Math.floor(arr.length / 2)
var flag = arr.splice(middle, 1)[0]
var left = [],
right = []
for (var i = 0; i < arr.length; i++) {
if (arr[i] < flag) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([flag], quickSort(right))
}
複製代碼
function findSubStr(str1, str2) {
if (str1.length > str2.length) {
[str1, str2] = [str2, str1]
}
var result = ''
var len = str1.length
for (var j = len; j > 0; j--) {
for (var i = 0; i < len - j; i++) {
result = str1.substr(i, j)
if (str2.includes(result)) return result
}
}
}
console.log(findSubStr('aabbcc11', 'ppooiiuubcc123'))
複製代碼
// dp[i][j] 計算去最大長度,記住口訣:相等左上角加一,不等取上或左最大值
function LCS(str1, str2){
var rows = str1.split("")
rows.unshift("")
var cols = str2.split("")
cols.unshift("")
var m = rows.length
var n = cols.length
var dp = []
for(var i = 0; i < m; i++){
dp[i] = []
for(var j = 0; j < n; j++){
if(i === 0 || j === 0){
dp[i][j] = 0
continue
}
if(rows[i] === cols[j]){
dp[i][j] = dp[i-1][j-1] + 1 //對角+1
}else{
dp[i][j] = Math.max( dp[i-1][j], dp[i][j-1]) //對左邊,上邊取最大
}
}
console.log(dp[i].join(""))//調試
}
return dp[i-1][j-1]
}
//!!!若是它來自左上角加一,則是子序列,不然向左或上回退。
//findValue過程,其實就是和 就是把T[i][j]的計算反過來。
// 求最長子序列
function findValue(input1,input2,n1,n2,T){
var i = n1-1,j=n2-1;
var result = [];//結果保存在數組中
console.log(i);
console.log(j);
while(i>0 && j>0){
if(input1[i] == input2[j]){
result.unshift(input1[i]);
i--;
j--;
}else{
//向左或向上回退
if(T[i-1][j]>T[i][j-1]){
//向上回退
i--;
}else{
//向左回退
j--;
}
}
}
console.log(result);
}
複製代碼
// 使用柯里化 + 遞歸
function curry ( fn ) {
var c = (...arg) => (fn.length === arg.length) ?
fn (...arg) : (...arg1) => c(...arg, ...arg1)
return c
}
複製代碼
var invertTree = function (root) {
if (root !== null) {
[root.left, root.right] = [root.right, root.left]
invertTree(root.left)
invertTree(root.right)
}
return root
}
複製代碼
var items = ['A','B','C','D']
var values = [50,220,60,60]
var weights = [5,20,10,12]
var capacity = 32 //揹包容積
greedy(values, weights, capacity) // 320
function greedy(values, weights, capacity) {
var result = 0
var rest = capacity
var sortArray = []
var num = 0
values.forEach((v, i) => {
sortArray.push({
value: v,
weight: weights[i],
ratio: v / weights[i]
})
})
sortArray.sort((a, b) => b.ratio - a.ratio)
sortArray.forEach((v, i) => {
num = parseInt(rest / v.weight)
rest -= num * v.weight
result += num * v.value
})
return result
}
複製代碼
function FindNumbersWithSum(array, sum)
{
var index = 0
for (var i = 0; i < array.length - 1 && array[i] < sum / 2; i++) {
for (var j = i + 1; j < array.length; j++) {
if (array[i] + array[j] === sum) return [array[i], array[j]]
}
//index = array.indexOf(sum - array[i], i + 1)
// if (index !== -1) {
// return [array[i], array[index]]
//}
}
return []
複製代碼
// 根據前序和中序重建二叉樹
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function reConstructBinaryTree(pre, vin)
{
var result = null
if (pre.length === 1) {
result = {
val: pre[0],
left: null,
right: null
}
} else if (pre.length > 1) {
var root = pre[0]
var vinRootIndex = vin.indexOf(root)
var vinLeft = vin.slice(0, vinRootIndex)
var vinRight = vin.slice(vinRootIndex + 1, vin.length)
pre.shift()
var preLeft = pre.slice(0, vinLeft.length)
var preRight = pre.slice(vinLeft.length, pre.length)
result = {
val: root,
left: reConstructBinaryTree(preLeft, vinLeft),
right: reConstructBinaryTree(preRight, vinRight)
}
}
return result
}
// 遞歸
// 前序遍歷
function prevTraverse (node) {
if (node === null) return;
console.log(node.data);
prevTraverse(node.left);
prevTraverse(node.right);
}
// 中序遍歷
function middleTraverse (node) {
if (node === null) return;
middleTraverse(node.left);
console.log(node.data);
middleTraverse(node.right);
}
// 後序遍歷
function lastTraverse (node) {
if (node === null) return;
lastTraverse(node.left);
lastTraverse(node.right);
console.log(node.data);
}
// 非遞歸
// 前序遍歷
function preTraverse(tree) {
var arr = [],
node = null
arr.unshift(tree)
while (arr.length) {
node = arr.shift()
console.log(node.root)
if (node.right) arr.unshift(node.right)
if (node.left) arr.unshift(node.left)
}
}
// 中序遍歷
function middleTraverseUnRecursion (root) {
let arr = [],
node = root;
while (arr.length !== 0 || node !== null) {
if (node === null) {
node = arr.shift();
console.log(node.data);
node = node.right;
} else {
arr.unshift(node);
node = node.left;
}
}
}
// 廣度優先-層序遍歷
// 遞歸
var result = []
var stack = [tree]
var count = 0
var bfs = function () {
var node = stack[count]
if (node) {
result.push(node.value)
if (node.left) stack.push(node.left)
if (node.right) stack.push(node.right)
count++
bfs()
}
}
bfs()
console.log(result)
// 非遞歸
function bfs (node) {
var result = []
var queue = []
queue.push(node)
while (queue.length) {
node = queue.shift()
result.push(node.value)
node.left && queue.push(node.left)
node.right && queue.push(node.right)
}
return result
}
複製代碼
// 插入排序
function insertSort(arr) {
var temp
for (var i = 1; i < arr.length; i++) {
temp = arr[i]
for (var j = i; j > 0 && temp < arr[j - 1]; j--) {
arr[j] = arr[j - 1]
}
arr[j] = temp
}
return arr
}
console.log(insertSort([3, 1, 8, 2, 5]))
// 歸併排序
function mergeSort(array) {
var result = array.slice(0)
function sort(array) {
var length = array.length
var mid = Math.floor(length * 0.5)
var left = array.slice(0, mid)
var right = array.slice(mid, length)
if (length === 1) return array
return merge(sort(left), sort(right))
}
function merge(left, right) {
var result = []
while (left.length || right.length) {
if (left.length && right.length) {
if (left[0] < right[0]) {
result.push(left.shift())
} else {
result.push(right.shift())
}
} else if (left.length) {
result.push(left.shift())
} else {
result.push(right.shift())
}
}
return result
}
return sort(result)
}
console.log(mergeSort([5, 2, 8, 3, 6]))
// 二分插入排序
function twoSort(array) {
var len = array.length,
i,
j,
tmp,
low,
high,
mid,
result
result = array.slice(0)
for (i = 1; i < len; i++) {
tmp = result[i]
low = 0
high = i - 1
while (low <= high) {
mid = parseInt((high + low) / 2, 10)
if (tmp < result[mid]) {
high = mid - 1
} else {
low = mid + 1
}
}
for (j = i - 1; j >= high + 1; j--) {
result[j + 1] = result[j]
}
result[j + 1] = tmp
}
return result
}
console.log(twoSort([4, 1, 7, 2, 5]))
複製代碼
遞歸很是耗費內存,由於須要同時保存成千上百個調用幀,很容易發生「棧溢出」錯誤(stack overflow)。但對於尾遞歸來講,因爲只存在一個調用幀,因此永遠不會發生「棧溢出」錯誤。
// 傳統遞歸斐波那契, 會形成超時或溢出
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Fibonacci(10) // 89
Fibonacci(100) // 超時
Fibonacci(500) // 超時
// 使用尾遞歸優化, 可規避風險
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity
複製代碼
function sort (A, B) {
var i = 0, j = 0, p = 0, m = A.length, n = B.length, C = []
while (i < m || j < n) {
if (i < m && j < n) {
C[p++] = A[i] < B[j] ? A[i++] : B[j++]
} else if (i < m) {
C[p++] = A[i++]
} else {
C[p++] = B[j++]
}
}
return C
}
複製代碼
歡迎關注前端之階公衆號,即時獲取更多前端技術,還可獲取前端大羣,裏面不少知名互聯網前端朋友,前端技術更新太快,不能被落伍淘汰,共同窗習,共同進步!