事件傳播指的是發生事件時傳播的過程。一共按順序分爲如下三個階段。 javascript
捕獲階段:從window對象傳導到目標節點(從上到下)的過程,直到目標的元素,爲截獲事件提供機會css
目標階段:在當前目標觸發的過程 ,目標接受事件html
冒泡階段:從目標節點傳導回到windowd對象(從下到上)的過程,在這個階段對事件作出響應。前端
ES5組合繼承vue
//定義一個父類:人
function Person(cai) {
this.cai = cai;
this.emotion = ['喜', '怒', '哀', '樂']; //人都有喜怒哀樂
}
//定義原型類方法 將 Person 類中需共享的方法放到 prototype 中,實現複用
Person.prototype.eat = function () {
console.log('吃' + this.cai);
}
//定義子類:學生,繼承了「人」這個類
function Student(cai, studentID) {
//先調用父類構造器 子類繼承父類的屬性 須要將this指向父類中的cai
Person.call(this, cai);
this.studentID = studentID; // studentID是子類本身的屬性
}
Student.prototype = new Person(); //子類繼承父類的方法此時 Student.prototype 中的 constructor 被重寫了,會致使 stu1.constructor === Person
Student.prototype.constructor = Student; //將 Student 原型對象的 constructor 指針從新指向 Student 自己
//建立子類的實例
var stu1 = new Student('西蘭花', 1001);
console.log(stu1.emotion); //['喜', '怒', '哀', '樂']
console.log(stu1.cai); // 西蘭花
stu1.eat(); //吃西蘭花
console.log(stu1.constructor); //Student複製代碼
結合原型鏈繼承和借用構造函數繼承即組合繼承是javascript最經常使用的繼承模式,不過,它也有本身的不足:組合繼承不管在什麼狀況下,都會調用兩次父類構造函數。 一次是在建立子類原型的時候,另外一次是在子類構造函數內部.子類最終會包含父類對象的所有實例屬性,但咱們不得不在調用子類構造函數時重寫這些屬性。
java
繼承實質的區別:node
**ES5:**先創造子類的實例對象this,而後再將父類的方法添加到this上面(Parent.apply(this))。 nginx
**ES6:**先創造父類的實例對象this,(因此必須先調用super方法)而後再用子類的構造函數修改this。
web
ES6繼承面試
class Person {
constructor(cai) {
this.cai = cai;
this.emotion = ['喜', '怒', '哀', '樂']; //人都有喜怒哀樂
}
eat() {
console.log('吃' + this.cai);
}
}
class Student extends Person {
constructor(cai, studentID) {
// 指向父類的構造函數
super(cai);
this.studentID = studentID;
}
showStudentID() {
console.log(this.studentID)
}
}
var stu1 = new Student('西蘭花', 1001);
console.log(stu1.emotion);
stu1.eat(); //吃西蘭花
stu1.showStudentID() //1001複製代碼
注:
ES6 裏的 Class 是經過關鍵字 extends 實現繼承
子類必須在 constructor 方法中調用 super 方法 ,super的關鍵字在這裏表示父類的構造函數,用來創造父類的this對象, 而子類是沒有本身的 this 對象的,須要調用 super 方法,來繼承父類的 this 對象,而後對其加工 ,故可知只有調用了 super 以後纔可使用 this 關鍵字,不然會報錯
參考連接 https://juejin.im/post/59ac1c4ef265da248e75892b#heading-2
引子:
Js裏有兩種數據類型,基本數據類型和引用數據類型。深拷貝、淺拷貝通常都是針對引用數據類型的。
基本數據類型主要是:undefined,boolean,number,string,null
var a = 1;
var b = a;
a = 2;
console.log(a); // 2
console.log(b); // 1複製代碼
var arr1 = [1,2,3,4];
var arr2 = arr1;
arr1.push(5);
console.log(arr1); // [1,2,3,4,5]
console.log(arr2); // [1,2,3,4,5]
arr2.push(6);
console.log(arr1); // [1,2,3,4,5,6]
console.log(arr2); // [1,2,3,4,5,6]複製代碼
然而,對於引用數據類型的賦值操做,arr2 僅僅是複製了 arr1的引用(也能夠稱之爲指向 arr1 內存地址的指針)。簡單來講,就是 arr1 與 arr2 指向了同一個內存空間
淺拷貝:
若是屬性是基本類型,拷貝的就是基本類型的值;若是屬性是內存地址(引用類型),拷貝的就是內存地址 ,所以若是其中一個對象改變了這個地址,就會影響到另外一個對象。
function shallowCopy(copyTarget) {
var obj = {};
for (var key in copyTarget) {
obj[key] = copyTarget[key];
}
return obj;
}
var json1 = {
'name': '張三',
'family': {
'children': '張三三',
'wife': '李四'
}
}
var json2 = shallowCopy(json1);
// before
console.log(json2);
// after
json1.family['father'] = '張一'
console.log(json1);
console.log(json2);複製代碼
由此能夠看出,淺拷貝僅僅拷貝了基本類型的數據,對於引用類型數據,則指向被複制的內存地址,若原地址中的對象發生改變,那麼淺複製出來的對象也會相應改變。
深拷貝:
深拷貝可歸納爲:爲引用類型數據成員另闢了一個獨立的內存空間,實現真正內容上的拷貝。
function deepCopy(copyTarget) {
var obj = {};
for(var key in copyTarget) {
// 先判斷obj[key]是否爲對象
if(typeof copyTarget[key] === "object"){
// 遞歸
obj[key] = deepCopy(copyTarget[key]);
} else {
// 若是不是對象,直接賦值便可
obj[key] = copyTarget[key];
}
}
return obj;
}
var json1 = {
'name': '張三',
'family': {
'children': '張三三','wife': '李四'
}
}
var json2 = deepCopy(json1);
// before
console.log(json2);
// after
json1.family['father'] = '張一'
console.log(json1);
console.log(json2);複製代碼
拓展
深複製能夠用JSON的方式:JSON.parse(JSON.stringify(obj))
可是JSON複製會忽略掉值爲undefined以及函數表達式。
var obj = {
a: 1,
b: 2,
c: undefined,
sum: function() { return a + b; }
};
var obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); //Object {a: 1, b: 2}複製代碼
瀏覽器中 Event Loop:
瀏覽器中, js引擎線程會循環從 任務隊列 中讀取事件而且執行, 這種運行機制稱做 Event Loop (事件循環).
每一個瀏覽器環境,至多有一個event loop。 一個event loop能夠有1個或多個task queue(任務隊列) 先執行同步的代碼,而後js會跑去消息隊列中執行異步的代碼,異步完成後,再輪到回調函數,而後是去下個事件循環中執行setTimeout 它從script(總體代碼)開始第一次循環。以後全局上下文進入函數調用棧。直到調用棧清空(只剩全局),而後執行全部的micro-task(微任務)
。當全部可執行的micro-task(微任務)
執行完畢以後。循環再次從macro-task 宏任務
開始,找到其中一個任務隊列執行完畢,而後再執行全部的micro-task,這樣一直循環下去。
從規範上來說,setTimeout有一個4ms的最短期,也就是說無論你設定多少,反正最少都要間隔4ms才運行裏面的回調。而Promise的異步沒有這個問題。Promise所在的那個異步隊列優先級要高一些 Promise是異步的,是指他的then()和catch()方法,Promise自己仍是同步的 Promise的任務會在當前事件循環末尾中執行,而setTimeout中的任務是在下一次事件循環執行
總結:
setTimeout、onClick
等等的一些操做,咱們會將他的執行結果放入隊列,此期間主線程不阻塞event loop
在隊列裏面從頭開始取,在執行棧中執行event loop
永遠不會斷Event Loop
(事件循環機制)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
複製代碼
經過Vue傳遞參數能夠分爲兩種方式: params參數 query參數
params參數傳遞方式分兩種:(1)經過router-link進行跳轉 (2)經過編程導航進行路由跳轉
params傳值
<div
v-for="item in list"
@click="getDescribe(item)"
>
{{item}}
</div>複製代碼
data() {
return {
list: ['101','102','103']
}
}複製代碼
對應路的由配置以下:
{
path: '/two/:id',
name: 'two',
component: () => import('@/components/two.vue'),
meta: {
title: 'two'
}
}複製代碼
push 後面能夠是對象,也能夠是字符串:
對象:
getDescribe(item) {
this.$router.push({
path: `/two/${item}`
})
}複製代碼
字符串:
getDescribe(item) {
this.$router.push(`/two/${item}`)
}複製代碼
命名的路由:
getDescribe(item) {
this.$router.push({ name: 'two', params: { id: `${item}` }})
}複製代碼
注意: 須要注意的是使用params必須和name屬性一塊兒使用,不然要跳轉的目標路由頁面沒法經過params獲取到傳遞過來的參數。params: 至關於 post 請求,請求參數不會體如今地址欄中。 這種方法會出現以下問題: 若是子頁面點擊【刷新】按鈕時,剛纔傳過來的參數並不會同步。由於參數沒有同步到網頁地址欄中。可是若是要讓他體如今地址欄中,能夠在配置路由時,寫在path屬性中。 以下能夠解決刷新的問題:
{
path: '/two/:id',
name: 'two',
component: () => import('@/components/two.vue'),
meta: {
title: 'two'
}
}複製代碼
讀取參數:
console.log(this.$route.params)複製代碼
query傳值。 相似get傳值
query: 至關於 get 請求,請求參數會體如今地址欄中。
this.$router.push({
path: `/two`,
query: {
id:item
}
})複製代碼
http://localhost:8888/#/two?id=101
console.log(this.$route.query)複製代碼
this.$router 和this.$route有何區別?
上面提到的編程式導航中用this.$router.push()來改變路由,用this.$route.params來獲得參數值
1.$router爲VueRouter實例,想要導航到不一樣URL,則使用$router.push方法。
2.$route爲當前router跳轉對象,裏面能夠獲取name、path、query、params等
computed特性
1.是計算值,
2.應用:就是簡化tempalte裏面{{}}計算和處理props或$emit的傳值
3.具備緩存性,頁面從新渲染值不變化,計算屬性會當即返回以前的計算結果,而沒必要再次執行函數
注意:computed的值在getter執行後是會緩存的,只有在它依賴的屬性值改變以後,下一次獲取computed的值時纔會從新調用對應的getter來計算
watch特性
1.是觀察的動做,
2.應用:監聽props,$emit或本組件的值執行異步操做
3.無緩存性,頁面從新渲染時值不變化也會執行
注意:watch在每次監聽的值變化時,都會執行回調。其實從這一點來看,都是在依賴的值變化以後,去執行回調
總結:若是一個值依賴多個屬性(多對一),用computed確定是更加方便的。若是一個值變化後會引發一系列操做,或者一個值變化會引發一系列值的變化(一對多),用watch更加方便一些。
watch的回調裏面會傳入監聽屬性的新舊值,經過這兩個值能夠作一些特定的操做。computed一般就是簡單的計算。 watch和computed並無哪一個更底層,watch內部調用的是vm.$watch,它們的共同之處就是每一個定義的屬性都單獨創建了一個Watcher對象
Vue.js 提供了一個方法 watch,它用於觀察Vue實例上的數據變更。
<template>
<div>
<input
type="text"
v-model="age"
>
</div>
</template>
<script>
export default {
data() {
return {
age:18
}
},
watch: {
age: (newAge,oldAge) => {
console.log('新值爲:'+newAge+',舊值爲:'+oldAge);
}
},
methods: {
},
components: {
},
computed: {
},
mounted() {
}
}
複製代碼
1、vdom是什麼?
vdom是虛擬DOM(Virtual DOM)的簡稱,指的是用JS模擬的DOM結構,將DOM變化的對比放在JS層來作。換而言之,vdom就是JS對象。
以下真實DOM
<ul id="list">
<li class="item">Item1</li>
<li class="item">Item2</li>
</ul>複製代碼
映射成虛擬DOM就是這樣:
{
tag: "ul",
attrs: {
id: "list"
},
children: [
{
tag: "li",
attrs: { className: "item" },
children: ["Item1"]
}, {
tag: "li",
attrs: { className: "item" },
children: ["Item2"]
}
]
} 複製代碼
2、爲何要用vdom?
採用JS對象模擬的方法,將DOM的比對操做放在JS層,減小瀏覽器沒必要要的重繪,提升效率。
固然有人說虛擬DOM並不比真實的DOM快,其實也是有道理的。當一個table中的每一條數據都改變時,顯然真實的DOM操做更快,由於虛擬DOM還存在js中diff算法的比對過程。因此,性能優點僅僅適用於大量數據的渲染而且改變的數據只是一小部分的狀況。
虛擬DOM更加優秀的地方在於:
一、它打開了函數式的UI編程的大門,即UI = f(data)這種構建UI的方式。
二、能夠將JS對象渲染到瀏覽器DOM之外的環境中,也就是支持了跨平臺開發,好比ReactNative。
3、diff算法
一、父組件向子組件通訊
使用props,父組件可使用props向子組件傳遞數據。
father.vue
<template>
<div>
<Child :name="msg"></Child>
</div>
</template>
<script>
import Child from '@/components/child.vue'
export default {
components: {
Child
},
data() {
return {
msg: 'feixuan'
}
},
computed: {
},
mounted() {
},
methods: {
}
}
</script>複製代碼
child.vue
<template>
<div>
{{name}}
</div>
</template>
<script>
export default {
props:{
name:{
type:String,
default:''
}
},
data() {
return {
}
},
components: {
},
computed: {
},
mounted() {
},
methods: {
}
}
</script>複製代碼
二、子組件向父組件通訊
方法一: 使用vue事件 父組件向子組件傳遞事件方法,子組件經過$emit觸發事件,回調給父組件。
father.vue
<template>
<div>
<Child @msgFunc="msgEvent"></Child>
{{message}}
</div>
</template>
<script>
import Child from '@/components/child.vue'
export default {
components: {
Child
},
data() {
return {
message: ''
}
},
methods: {
msgEvent(msg) {
console.log(msg)
this.message = msg
}
},
computed: {
},
mounted() {
}
}
</script>複製代碼
child.vue
<template>
<div>
<button @click="handleClick">點我</button>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
handleClick() {
this.$emit('msgFunc','我是來自子組件的消息');
}
},
components: {
},
computed: {
},
mounted() {
}
}
</script>複製代碼
三、非父子組件通訊
對於兩個組件不是父子關係,那麼又該如何實現通訊呢?在項目規模不大的狀況下,徹底可使用中央事件總線 EventBus
的方式。若是你的項目規模是大中型的,那麼可使用vuex狀態管理
EventBus
經過新建一個 Vue
事件 bus
對象,而後經過 bus.$emit
觸發事件,bus.$on
監聽觸發的事件。
在使用vue.js的時候,有時候由於一些特定的業務場景,不得不去操做DOM,好比這樣:
<template>
<div>
<div ref="test">{{test}}</div>
<button @click="handleClick">點擊</button>
</div>
</template>
<script>
export default {
data() {
return {
test: 'begin'
}
},
methods: {
handleClick () {
this.test = 'end';
console.log(this.$refs.test.innerText);//打印「begin」
}
},
components: {
},
computed: {
},
mounted() {
}
}
</script>
複製代碼
打印的結果是begin,爲何咱們明明已經將test設置成了「end」,獲取真實DOM節點的innerText卻沒有獲得咱們預期中的「end」,而是獲得以前的值「begin」呢?
緣由
Vue.js源碼的Watch實現。當某個響應式數據發生變化的時候,它的setter函數會通知閉包中的Dep,Dep則會調用它管理的全部Watch對象。觸發Watch對象的update實現。咱們來看一下update的實現。
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
/*同步則執行run直接渲染視圖*/
this.run()
} else {
/*異步推送到觀察者隊列中,下一個tick時調用。*/
queueWatcher(this)
}
}複製代碼
Vue是一個MVVM框架,數據綁定簡單來講,就是當數據發生變化時,相應的視圖會進行更新,當視圖更新時,數據也會跟着變化。
實現數據綁定的方式大體有如下幾種:
- 一、發佈者-訂閱者模式(backbone.js)
- 二、髒值檢查(angular.js)
- 三、數據劫持(vue.js)複製代碼
Vue.js則是經過數據劫持以及結合發佈者-訂閱者來實現的,數據劫持是利用ES5的Object.defineProperty(obj, key, val)來劫持各個屬性的的setter以及getter,在數據變更時發佈消息給訂閱者,從而觸發相應的回調來更新視圖。
二進制 → 十進制
方法:二進制數從低位到高位(即從右往左)計算,第0位的權值是2的0次方,第1位的權值是2的1次方,第2位的權值是2的2次方,依次遞增下去,把最後的結果相加的值就是十進制的值了。
例:將二進制的(101011)B轉換爲十進制的步驟以下:
1. 第0位 1 x 2^0 = 1;
2. 第1位 1 x 2^1 = 2;
3. 第2位 0 x 2^2 = 0;
4. 第3位 1 x 2^3 = 8;
5. 第4位 0 x 2^4 = 0;
6. 第5位 1 x 2^5 = 32;
7. 讀數,把結果值相加,1+2+0+8+0+32=43,即(101011)B=(43)D。
複製代碼
八進制 → 十進制
方法:八進制數從低位到高位(即從右往左)計算,第0位的權值是8的0次方,第1位的權值是8的1次方,第2位的權值是8的2次方,依次遞增下去,把最後的結果相加的值就是十進制的值了。 八進制就是逢8進1,八進制數採用 0~7這八數來表達一個數。
例:將八進制的(53)O轉換爲十進制的步驟以下:
1. 第0位 3 x 8^0 = 3;
2. 第1位 5 x 8^1 = 40;
3. 讀數,把結果值相加,3+40=43,即(53)O=(43)D。複製代碼
十進制 → 二進制
方法:除2取餘法,即每次將整數部分除以2,餘數爲該位權上的數,而商繼續除以2,餘數又爲上一個位權上的數,這個步驟一直持續下去,直到商爲0爲止,最後讀數時候,從最後一個餘數讀起,一直到最前面的一個餘數。
例:將十進制的(43)D轉換爲二進制的步驟以下:
1. 將商43除以2,商21餘數爲1;
2. 將商21除以2,商10餘數爲1;
3. 將商10除以2,商5餘數爲0;
4. 將商5除以2,商2餘數爲1;
5. 將商2除以2,商1餘數爲0;
6. 將商1除以2,商0餘數爲1;
7. 讀數,由於最後一位是通過屢次除以2才獲得的,所以它是最高位,讀數字從最後的餘數向前讀,101011,即(43)D=(101011)B。複製代碼
函數防抖(debounce)
防抖函數 debounce 指的是某個函數在某段時間內,不管觸發了多少次回調,都只執行最後一次。假如咱們設置了一個等待時間 3 秒的函數,在這 3 秒內若是遇到函數調用請求就從新計時 3 秒,直至新的 3 秒內沒有函數調用請求,此時執行函數,否則就以此類推從新計時。
原理及實現 :
實現原理就是利用定時器,函數第一次執行時設定一個定時器,以後調用時發現已經設定過定時器就清空以前的定時器,並從新設定一個新的定時器,若是存在沒有被清空的定時器,當定時器計時結束後觸發函數執行。
函數節流(throttle)
規定在一個單位時間內,只能觸發一次函數。若是這個單位時間內觸發屢次函數,只有一次生效,
即每隔一段時間後執行一次,也就是下降頻率,將高頻操做優化成低頻操做,
一般使用場景: 滾動條事件 或者 resize 事件,一般每隔 100~500 ms執行一次便可。
複製代碼
總結 :
函數防抖和函數節流都是防止某一時間頻繁觸發,可是這兩兄弟之間的原理卻不同。 函數防抖是某一段時間內只執行一次,而函數節流是間隔時間執行。
結合應用場景
debounce :
search搜索聯想,用戶在不斷輸入值時,用防抖來節約請求資源。 window觸發resize的時候,不斷的調整瀏覽器窗口大小會不斷的觸發這個事件,用防抖來讓其只觸發一次
throttle :
鼠標不斷點擊觸發,mousedown(單位時間內只觸發一次) 監聽滾動事件,好比是否滑到底部自動加載更多,用throttle來判斷
斐波那契數列也叫黃金分割數列,也叫兔子數列
原理:假定一對大兔子每個月能生一對小兔子,且每對新生的小兔子通過一個月能夠長成一對大兔子,若是不發生死亡,且每次均生下一雌一雄,問一年後共有多少對兔子?
月份 | 兔子狀況 | 總數 |
---|---|---|
第0個月 | a(小兔子) | 1 |
第1個月 | a(具有繁殖能力) | 1 |
第2個月 | b(生啦生啦)+ a(他父母) | 2 |
第3個月 | b(2月份出生的具有繁殖能力,正躍躍欲試) + b2(他父母又生二胎了) +a(他父母) | 3 |
第4個月 | c(2月份的兔子b喜當爹)+b(二月份出生的兔子) + b2(二胎具有繁殖能力,準備生娃) +a(他父母)+d(a生三胎) | 5 |
… | … | … |
一、1 、二、三、五、八、1三、2一、3四、5五、89……
因此規律就是 fn(n)=fn(n-1)+fn(n-2)
迭代 方式
/*
*i 月份
*/
function fn(i){
var a=[];
/*0個月什麼都不存在*/
a[0]=0;
a[1]=1;
for(var j = 2; j<= i;j++){
a[j]=a[j-1] + a[j-2];
}
return a[i]
}複製代碼
遞歸方式
/*
* i 月份
*/
function fn(i){
if(i < 2){return i === 0 ? 0 : 1;}
return fn(i-1)+fn(i-2)
}複製代碼
總結: 針對這個例子來講,這裏的遞歸會進行太屢次的調用(比迭代多),因此簡潔的背後犧牲的是性能
這個是阿里的一道面試題
核心原理: 更新視圖但不從新請求頁面。
vue-router實現單頁面路由跳轉,提供了三種方式:hash模式、history模式、abstract模式,根據mode參數來決定採用哪種方式。
路由模式
vue-router 提供了三種運行模式:
hash: 使用 URL hash 值來做路由。默認模式。
history: 依賴 HTML5 History API 和服務器配置。查看 HTML5 History 模式。
abstract: 支持全部 JavaScript 運行環境,如 Node.js 服務器端
Hash模式:
hash即瀏覽器url中#後面的內容,包含#。hash是URL中的錨點,表明的是網頁中的一個位置,單單改變#後的部分,瀏覽器只會加載相應位置的內容,不會從新加載頁面。 也就是說 即#是用來指導瀏覽器動做的,對服務器端徹底無用,HTTP請求中,不包含#。 每一次改變#後的部分,都會在瀏覽器的訪問歷史中增長一個記錄,使用」後退」按鈕,就能夠回到上一個位置。 因此說Hash模式經過錨點值的改變,根據不一樣的值,渲染指定DOM位置的不一樣數據。
History模式:
HTML5 History API提供了一種功能,能讓開發人員在不刷新整個頁面的狀況下修改站點的URL,就是利用 history.pushState API 來完成 URL 跳轉而無須從新加載頁面; 因爲hash模式會在url中自帶#,若是不想要很醜的 hash,咱們能夠用路由的 history 模式,只須要在配置路由規則時,加入"mode: 'history'",這種模式充分利用 history.pushState API 來完成 URL 跳轉而無須從新加載頁面。 有時,history模式下也會出問題: eg: hash模式下:xxx.com/#/id=5 請求地址爲 xxx.com,沒有問題。 history模式下:xxx.com/id=5 請求地址爲 xxx.com/id=5,若是後端沒有對應的路由處理,就會返回404錯誤; 爲了應對這種狀況,須要後臺配置支持: 在服務端增長一個覆蓋全部狀況的候選資源:若是 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。
abstract模式:
abstract模式是使用一個不依賴於瀏覽器的瀏覽歷史虛擬管理後端。 根據平臺差別能夠看出,在 Weex 環境中只支持使用 abstract 模式。 不過,vue-router 自身會對環境作校驗,若是發現沒有瀏覽器的 API,vue-router 會自動強制進入 abstract 模式,因此 在使用 vue-router 時只要不寫 mode 配置便可,默認會在瀏覽器環境中使用 hash 模式,在移動端原生環境中使用 abstract 模式。 (固然,你也能夠明確指定在全部狀況下都使用 abstract 模式)。
async 及 await
async 是讓方法變成異步。 await 是等待異步方法執行完成。
注意:await 必須在 async 方法中才可使用由於await 訪問自己就會形成程序中止堵塞,因此必須在異步方法中才可使用
使用await語法的時候,後面的函數須要返回Promise對象
async test1() {
let result = await this.$http.get('/api/job',{})
console.log(result)
}複製代碼
冒泡排序:
兩兩元素進行比較,較大者放置後頭,相似水中的氣泡較大者浮在水的上端
function bubble(arr) {
for(var i = 0; i < arr.length; i++) {
for(var j = 0; j < arr.length - 1- i; j++) {
if(arr[j] > arr[j + 1]) {
var max = arr[j];
arr[j] = arr[j+1];//交換數據
arr[j+1] = max;//交換數據
}
}
}
console.log(arr)
}
bubble([1,4,6,2,7,2])// [1, 2, 2, 4, 6, 7]複製代碼
快速排序:
取一個元素爲基準,把序列分紅兩部分,小於基準的放到它的左面,大於等於的放到它的右面,而後在把左面和右面的子序列再進行上述的拆分,直到子序列不可再分割(小於2個元素),最終達到整個序列有序
function quickSort(arr) {
if(arr.length < 2) {
return arr;
} else {
const pivot = arr[0]; // 基準值
const pivotArr = []; // 同樣大的放中間
const lowArr= []; // 小的放左邊
const hightArr = []; // 大的放右邊
arr.forEach(current => {
if(current === pivot) pivotArr.push(current);
else if(current > pivot) hightArr.push(current);
else lowArr.push(current);
})
return quickSort(lowArr).concat(pivotArr).concat(quickSort(hightArr));
}
}
console.log(quickSort([4,6,2,3,1,5,7,8])) // [1, 2, 3, 4, 5, 6, 7, 8]複製代碼
簡單選擇排序:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。 再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。 重複第二步,直到全部元素均排序完畢。
const selectionSort = array => {
const len = array.length;
let minIndex, temp;
for (let i = 0; i < len - 1; i++) {
minIndex = i;
for (let j = i + 1; j < len; j++) {
if (array[j] < array[minIndex]) {
// 尋找最小的數
minIndex = j; // 將最小數的索引保存
}
}
temp = array[i];
array[i] = array[minIndex];
array[minIndex] = temp;
console.log('array: ', array);
}
return array;
};
selectionSort([6,4,3,8])//[3, 4, 6, 8]複製代碼
異步加載js有三種 : defer 、 async 、 動態建立script標籤 、 按需異步載入js
async : 並行加載腳本文件,下載完畢當即解釋執行代碼,不會按照頁面上的script順序執行。 defer : 並行下載js,會按照頁面上的script標籤的順序執行,而後在文檔解析完成以後執行腳本
defer 與 async 的相同點是採用並行下載,在下載過程當中不會產生阻塞。區別在於執行時機,async 是加載完成後自動執行,而 defer 須要等待頁面完成後執行。
解析:
<script src="script.js"></script>
沒有 defer 或 async,瀏覽器會當即加載並執行指定的腳本,「當即」指的是在渲染該 script 標籤之下的文檔元素以前,也就是說不等待後續載入的文檔元素,讀到就加載並執行。
<script async src="script.js"></script>
有 async,加載和渲染後續文檔元素的過程將和 script.js 的加載與執行並行進行(異步)。
<script defer src="myscript.js"></script>
有 defer,加載後續文檔元素的過程將和 script.js 的加載並行進行(異步),可是 script.js 的執行要在全部元素解析完成以後,DOMContentLoaded 事件觸發以前完成。
Load 事件觸發表明頁面中的 DOM,CSS,JS,圖片已經所有加載完畢。DOMContentLoaded 事件觸發表明初始的 HTML 被徹底加載和解析,不須要等待 CSS,JS,圖片加載。
call 和 apply 是爲了動態改變 this 而出現的,當一個 object 沒有某個方法,可是其餘的有,咱們能夠藉助 call 或 apply 用其它對象的方法來操做。
知乎簡單易懂答案:
貓吃魚狗吃肉,奧特曼打怪獸
有一天,狗想吃魚了
貓.吃魚.call(狗,魚)
狗就吃到魚了
貓成精了,想打怪獸
奧特曼.打怪獸.call(貓,小怪獸)
obj.call(thisObj, arg1, arg2, ...)
obj.apply(thisObj, [arg1, arg2, ...])
二者做用一致,都是把obj(即this)綁定到thisObj,這時候thisObj具有了obj的屬性和方法。
或者說thisObj繼承了obj的屬性和方法。惟一區別是apply接受的是數組參數,call接受的是連續參數。複製代碼
例子:
var obj1 = {
value: 1
}
function say() {
console.log(this.value)
}
say() // 輸出undefined
say.call(obj1) // 輸出1複製代碼
注意兩點 : call 改變了 this 的指向,此時的 this 指到了 obj1 say 函數執行了
bind方法的特殊性:
之因此把bind方法單獨放出來是由於bind方法和前面二者仍是有不小的區別的,雖然都是動態改變this的值,舉個例子
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81複製代碼
有沒有注意到使用bind方法時候後面還要多加上一對括號,由於使用bind只是返回了對應函數並無當即執行,而call和apply方法是當即執行的
beforeCreate:
vue實例的掛載元素el和數據對象data都爲undefined,還未初始化
created:
實例建立完成 vue實例的數據對象data有了,el尚未。並已經完成如下配置:數據觀測(data observer),屬性和方法的運算, watch/event 事件回調
此時能夠調用methods中定義的方法,修改data的數據,而且可觸發響應式變化、computed值從新計算,watch到變動等
還未掛載到DOM,不能訪問到$el屬性,$ref屬性內容爲空數組
beforeMount:
vue 實例的$el 和 data 都初始化了,但仍是掛載以前爲虛擬的 dom 節點,data.message 還未替換
mounted:
實例掛載到DOM上,此時能夠經過DOM API獲取到DOM節點,$ref屬性能夠訪問,data.message 成功渲染
beforeUpdate:
這裏的更新對象是模板,即須要虛擬 DOM 從新渲染和打補丁,beforeUpdate發生在以上兩個流程以前,此時新的虛擬DOM已經生成
若是發生變動的數據在模板中並無使用(包括直接和間接,間接:好比某個依賴該數據的計算屬性在模板中使用了),則不會觸發更新流程!!!
updated:
因爲數據更改致使的虛擬 DOM 從新渲染和打補丁,在這以後會調用該鉤子。
當這個鉤子被調用時,組件 DOM 已經更新,能夠執行依賴於 DOM 的操做
beforeDestroy:
實例銷燬以前調用。在這一步,實例仍然徹底可用,this仍能獲取到實例
通常在這一步中進行:銷燬定時器、解綁全局事件、銷燬插件對象等操做
destroyed:
Vue 實例銷燬後調用。調用後,Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬
參考 https://segmentfault.com/a/1190000000652891
閉包就是可以讀取其餘函數內部變量的函數。因爲在Javascript語言中,只有函數內部的子函數才能讀取局部變量,所以能夠把閉包簡單理解成「定義在一個函數內部的函數」。
要理解閉包,首先必須理解Javascript特殊的變量做用域。
變量的做用域無非就是兩種:全局變量和局部變量。
Javascript語言的特殊之處,就在於函數內部能夠直接讀取全局變量。
var n = 999;
function f() {
console.log(n)
}
f(); // 999
複製代碼
另外一方面,在函數外部天然沒法讀取函數內的局部變量。
function f() {
var n= 999;
}
console(n); //Uncaught ReferenceError: n is not defined
複製代碼
這裏有一個地方須要注意,函數內部聲明變量的時候,必定要使用var命令。若是不用的話,你實際上聲明瞭一個全局變量!
function f() {
n = 999;
}
f();
console.log(n); // 999複製代碼
出於種種緣由,咱們有時候須要獲得函數內的局部變量。可是,前面已經說過了,正常狀況下,這是辦不到的,只有經過變通方法才能實現。
那就是在函數的內部,再定義一個函數。
function f1() {
var n = 999;
function f2(){
console.log(n); // 999
}
}複製代碼
在上面的代碼中,函數f2就被包括在函數f1內部,這時f1內部的全部局部變量,對f2都是可見的。可是反過來就不行,f2內部的局部變量,對f1 就是不可見的。這就是Javascript語言特有的「鏈式做用域」結構(chain scope),
子對象會一級一級地向上尋找全部父對象的變量。因此,父對象的全部變量,對子對象都是可見的,反之則不成立。
既然f2能夠讀取f1中的局部變量,那麼只要把f2做爲返回值,咱們不就能夠在f1外部讀取它的內部變量了嗎!
function f1() {
n = 999;
function f2(){
console.log(n);
}
return f2;
}
var result=f1();
result();// 999複製代碼
閉包能夠用在許多地方。它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中
function f1() {
var n = 999;
nAdd = function() {
n += 1
}
function f2() {
console.log(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,能夠在函數外部對函數內部的局部變量進行操做。
特性:
1.函數嵌套函數
2.函數內部能夠引用外部的參數和變量
3.參數和變量不會被垃圾回收機制回收
閉包的缺點就是常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。
爲何要使用閉包:
爲了設計私有方法和變量,避免全局變量污染 但願一個變量長期駐紮在內存中
利用數組的 indexOf 屬性
let arr = [5,7,8,8]
console.log(arr.indexOf(5))//0
console.log(arr.indexOf(8))//2
console.log(arr.indexOf(9))//-1
function unique(origin) {
var result = [];
for (var i = 0; i < origin.length; i++) {
var item = origin[i];
if (result.indexOf(item) === -1) {
result.push(item);
}
}
return result;
}
console.log(unique(arr))//[5, 7, 8]複製代碼
數組的 filter 屬性和 indexOf 屬性
let arr = [5,7,8,8]
function unique(origin) {
var result = origin.filter(function(item, index, array) {
// 獲取元素在源數組的位置,只返回那些索引等於當前元素索引的值。
return array.indexOf(item) === index;
});
return result;
}
console.log(unique(arr))//[5, 7, 8]複製代碼
利用 ES6 Set
ES6 提供了新的數據結構 Set,它相似於數組,可是成員的值都是惟一的,沒有重複的值。向 Set 加入值的時候,不會發生類型轉變,因此 5 和 ‘5’ 是兩個不一樣的值。Set 內部判斷兩個值是否相同,用的是相似於 「===」的算法,可是區別是,在 set 內部認爲 NaN 等於 NaN
let arr = [5,7,8,8]
function unique(origin) {
return Array.from(new Set(origin));
}
console.log(unique(arr))//[5, 7, 8]複製代碼
首先盒子模型是由content、padding、border、margin 4部分組成
分類: 標準盒子模型 和 ie盒子模型
區別:
可是目前已經統一去起來了
<!DOCTYPE html>複製代碼
CSS3提供了能夠切換盒子模型模式的屬性:box-sizing,它有2個值,分別是content-box和border-box
content-box:
讓元素維持W3C的標準盒模型。元素的寬度/高度由border + padding + content的寬度/高度決定,設置width/height屬性指的是content部分的寬/高
border-box:
讓元素維持IE傳統盒模型(IE6如下版本和IE6~7的怪異模式)。設置width/height屬性指的是border + padding + content
1.對www.baidu.com這個網址進行DNS域名解析,獲得對應的IP地址
2.根據這個IP,找到對應的服務器,發起TCP的三次握手
3.創建TCP鏈接後發起HTTP請求
4.服務器響應HTTP請求,瀏覽器獲得html代碼
5.瀏覽器解析html代碼,並請求html代碼中的資源(如js、css圖片等)(先獲得html代碼,才能去找這些資源)
6.瀏覽器對頁面進行渲染呈現給用戶
三個主要過程:
最後一步對前端很重要 瀏覽器是如何對頁面進行渲染的?
a: 解析html文件構成 DOM樹,
b: 解析CSS文件構成渲染樹,
c: 邊解析,邊渲染 ,
d: JS 單線程運行,JS有可能修改DOM結構,意味着JS執行完成前,後續全部資源的下載是沒有必要的,因此JS是單線程,會阻塞後續資源下載
function Animal(name) {
this.name = name;
}
Animal.prototype.run = function() {
console.log(this.name + 'can run...');
}
var cat = new Animal('cat');
cat.run() //catcan run...
console.log(cat.__proto__ === Animal.prototype); // true複製代碼
new共經歷了四個過程。
var fn = function () { };
var fnObj = new fn();複製代碼
一、建立了一個空對象
var obj = new object();複製代碼
二、設置原型鏈
obj._proto_ = fn.prototype;複製代碼
三、讓fn的this指向obj,並執行fn的函數體
var result = fn.call(obj);複製代碼
四、判斷fn的返回值類型,若是是值類型,返回obj。若是是引用類型,就返回這個引用類型的對象。
if (typeof(result) == "object"){
fnObj = result;
} else {
fnObj = obj;
} 複製代碼
爲啥須要Symbol:
一個新規則的提出,必然是由於有需求,熟悉ES5的人都知道,ES5裏面對象的屬性名都是字符串,若是你須要使用一個別人提供的對象,你對這個對象有哪些屬性也不是很清楚,但又想爲這個對象新增一些屬性,那麼你新增的屬性名就極可能和原來的屬性名發送衝突,顯然咱們是不但願這種狀況發生的。因此,咱們須要確保每一個屬性名都是獨一無二的,這樣就能夠防止屬性名的衝突了。所以,ES6裏就引入了Symbol,用它來產生一個獨一無二的值。
Symbol是什麼:
Symbol其實是ES6引入的一種原始數據類型,除了Symbol,JavaScript還有其餘5種數據類型,分別是Undefined、Null、Boolean、String、Number,這5種數據類型都是ES5中就有的。
怎麼生成一個Symbol類型的值:
既然咱們已經知道了Symbol是一種原始的數據類型,那麼怎麼生成這種數據類型的值呢?Symbol值是經過Symbol函數生成的,以下:
let s = Symbol();
console.log(s); // Symbol()
typeof s; // "symbol"複製代碼
上面代碼中,s就是一個Symbol類型的值,它是獨一無二的。
Symbol函數前不能用new
Symbol函數不是一個構造函數,前面不能用new操做符。因此Symbol類型的值也不是一個對象,不能添加任何屬性,它只是一個相似於字符型的數據類型。若是強行在Symbol函數前加上new操做符,會報錯,以下:
let s = new Symbol();// Uncaught TypeError: Symbol is not a constructor(…)複製代碼
Symbol函數的參數
1.字符串做爲參數
用上面的方法生成的Symbol值很差進行區分,Symbol函數還能夠接受一個字符串參數,來對產生的Symbol值進行描述,方便咱們區分不一樣的Symbol值。
let s1 = Symbol('s1');
let s2 = Symbol('s2');
console.log(s1); // Symbol(s1)
console.log(s2); // Symbol(s2)
s1 === s2; // false
let s3 = Symbol('s2');
s2 === s3; // false
複製代碼
從上面代碼能夠看出:
2.對象做爲參數
若是Symbol函數的參數是一個對象,就會調用該對象的toString方法,將其轉化爲一個字符串,而後才生成一個Symbol值。因此,說到底,Symbol函數的參數只能是字符串。
set數據集合:相似於數組
set:是一個集合,相似於數組,與數組的主要區別是沒有重複的元素。主要的做用能夠進行去重。
重點:一個屬性,四個方法
一、size屬性:返回set數組的長度,相似於lenght
二、四個方法:add,delete,clear,has
let set =new Set([1,2,3,4,1]);
console.log(set.size); //4
//添加元素
set.add(7);
console.log(set); // {1, 2, 3, 4, 7}
//刪除數組中的某個元素
set.delete(3);
console.log(set); // {1, 2, 4, 7}
//檢測數組中是否含有某個元素,返回值爲布爾
console.log(set.has(2)); //true
//清空數組
set.clear();
console.log(set) //{}複製代碼
map數據集合:相似於對象
let obj={a:1};
const map = new Map([
['name','java'],
['feel','今每天氣賊好'],
['jieguo','適合看帥哥'],
[obj,'是!']
]);
console.log(map);//{"name" => "java", "feel" => "今每天氣賊好", "jieguo" => "適合看帥哥", {…} => "是!"}複製代碼
Map與Object的區別
一、Map的size屬性能夠直接獲取鍵值對個數
二、二者都是對應的keys-value鍵值對, 可是Object的keys只能是字符串或者 Symbols, Map的key能夠是任意值,好比數組、對象等
總結:Map 對象保存鍵值對。一個對象的鍵只能是字符串或者 Symbols,但一個 Map 的鍵能夠是任意值。 Set 對象容許你存儲任何類型的惟一值,Set對象是值的集合,Set中的元素只會出現一次 Symbol 是一種特殊的、不可變的數據類型,能夠做爲對象屬性的標識符使用(Symbol([description]) )
<template>
<div>
{{list[2]}}
</div>
</template>
<script>
export default {
data() {
return {
list: [1,3,5]
}
},
methods: {
},
components: {
},
computed: {
},
mounted() {
this.list[2] = 8 // 沒有效果
this.$set(this.list, 2, 8); //有效果
}
}
</script>複製代碼
對於set這個方法的解釋:
this.$set(數組或者對象,修改的下標或者對象屬性名,修改的值)
一、不改變原數組,返回新數組
一、concat() 鏈接兩個或多個數組,兩邊的原始數組都不會變化,返回被鏈接數組的一個副本。
二、join() 把數組中全部元素放入一個字符串中,返回字符串。
三、slice() 從開始到結束(不包括結束)選擇數組的一部分淺拷貝到一個新數組。
四、map() 建立一個新數組並返回,其中新數組的每一個元素由調用原始數組中的每個元素執行提供的函數得來,原始數組不會改變。
五、every() 對數組中的每一個元素都執行一次指定的回調函數,直到回調函數返回false,此時every()返回false並再也不繼續執行。若是回調函數對每一個元素都返回true,那麼every()將返回true。
六、some() 對數組中的每一個元素都執行一次指定的回調函數,直到回調函數返回true,此時some()返回true並再也不繼續執行。若是回調函數對每一個元素都返回false,那麼some()將返回false。
七、filter() 建立一個新數組, 其包含經過所提供函數實現的測試的全部元素。
二、改變原數組
一、forEach() 針對每個元素執行提供的函數。會修改原來的數組,不會返回執行結果,返回undefined。
二、pop() 刪除數組最後一個元素,返回被刪除的元素。若是數組爲空,則不改變數組,返回undefined。
三、push() 向數組末尾添加一個或多個元素,返回改變後數組的長度。 reverse() 顛倒數組中元素的位置,返回該數組的引用。
四、shift() 從數組中刪除第一個元素,並返回該元素的值。此方法更改數組的長度。
五、unshift() 將一個或多個元素添加到數組的開頭,並返回新數組的長度。
六、sort() 對數組的元素進行排序,並返回數組。排序不必定是穩定的。默認排序順序是根據字符串Unicode碼點。
七、splice() 向數組中添加/刪除項目,而後返回被刪除項目的新數組
1.GET使用URL或Cookie傳參,而POST將數據放在BODY中,這個是由於HTTP協議用法的約定。並不是它們的自己區別。
4.GET和POST最大的區別主要是GET請求是冪等性的,POST請求不是。(冪等性:對同一URL的多個請求應該返回一樣的結果。)由於get請求是冪等的,在網絡很差的隧道中會嘗試重試。若是用get請求增數據,會有重複操做的風險,而這種重複操做可能會致使反作用
一、全局的鉤子
beforeEach(to,from,next)(全局前置守衛) 頁面加載以前
afterEach(to,from,next)(全局後置守衛) 頁面加載以後,有兩個參數:to/from ,全局後置鉤子在全部路由跳轉結束的時候調用這些鉤子不會接受 next 函數也不會改變導航自己
vue router.beforeResolve(全局解析守衛)注:beforeEach和afterEach都是vue-router實例對象的屬性,每次跳轉前,beforeEach和afterEach都會執行。
二、組建內的導航鉤子:
beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave,直接在路由組件內部進行定義的
beforeRouteEnter(to, from, next) :
在渲染該組件的對應路由被確認
前調用,用法和參數與beforeEach相似,next須要被主動調用
注意:
beforeRouteEnter (to, from, next) {
// 這裏還沒法訪問到組件實例,this === undefined
next( vm => {
// 經過 `vm` 訪問組件實例
})
}複製代碼
vm
訪問組件實例進行賦值等操做beforeRouteUpdate (to, from, next) :
在當前路由改變,而且該組件被複用時調用,能夠經過this訪問實例, next須要被主動調用,不能傳回調
beforeRouteLeave (to, from, next) :
航離開該組件的對應路由時調用,能夠訪問組件實例 this
,next須要被主動調用,不能傳回調 用途:清除當前組件中的定時器,避免佔用內存;當頁面中有未關閉的窗口, 或未保存的內容時, 阻止頁面跳轉;保存相關內容到Vuex中或Session中
三、路由內的導航鉤子
主要用於寫某個指定路由跳轉時須要執行的邏輯有三個參數:to/from/next
{
path:'/',
name:'Login',
component:Login,
beforeEnter:(to,from,next)=>{
consloe.log("即將進入Login");
next();
}
}複製代碼