答案:true true falsejavascript
第一個輸出:將[]轉爲布爾類型,爲true。只有如下類型轉爲布爾值爲false:Boolean(null)、Boolean(undefined)、Boolean(0)、Boolean(‘’)、Boolean(NaN)css
第二和第三:其餘類型與布爾類型比較,將兩個類型轉爲數字再進行比較。Number([])=0,Number(false)=0,因此第二個輸出true。Number({})=NaN,NaN==0 --> false,因此第三個輸出falsehtml
Number([2]) //2
Number(['x']) //NaN
Number(undefined); //NaN
Number(null); //0
null+1=1
undefined+1=NaN
undefined==false; //false
false=='' //true
null==undefined; //true,都轉爲boolean類型
null=undefined; //false,不強制轉換
複製代碼
HTML部分
<form action="" id='form'>
<label for="">
姓名: <input type="text" name="name">
</label>
<label for="">
文件:<input id="file" type="file" name="file">
</label>
<label for="">
<input type="button" value="保存">
</label>
</form>
複製代碼
JS部分
var form=document.getElementById('form');
var formdata=new FormData(form);
var name=formdata.get(name);
var file=formdata.get(file);
formdata.append('token','sdcsdc');
//提交數據
var xhr=new XMLHttpRequest();
xhr.open("post","http://127.0.0.1/adv");
xhr.send(formdata);
xhr.onload=function(){
if(xhr.readyState==4&&xhr.status==200){
//...
}
}
複製代碼
var formdata=new FormData();
formdata.append('name','jack');
formdata.append('name','rose');
formdata.get('name'); //jack
formdata.getAll('name'); //[jack,rose]
複製代碼
formData.append("k1", "v1");
formData.append("k1", "v2");
formData.append("k2", "v1");
var i = formData.entries();
i.next(); // {done:false, value:["k1", "v1"]}
i.next(); // {done:fase, value:["k1", "v2"]}
i.next(); // {done:fase, value:["k2", "v1"]}
i.next(); // {done:true, value:undefined}
//返回的對象的value屬性以數組的形式,第一個是key,第二個爲value
//以循環的形式
for(var pair of formData.entries()){ //pair是每次迭代獲取到的對象裏的value
console.log(pair[0]+':'+pair[1])
//k1:v1
//k1:v2
//k2:v1
}
複製代碼
js垃圾回收機制前端
答案:vue
下面輸出結果是:java
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 25node
例子webpack
let a=1,b=2,k=0;
console.log( (a++,b++,k++) ); //輸出0,注意是要把整個括起來,而後將計算後的結果返回
console.log(a); //2
console.log(b); //3
console.log(k); //1
複製代碼
例子:ios
let a=3,b;
console.log(b=a); //輸出3
複製代碼
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x);
console.log(b.x);
複製代碼
答案:nginx
undefined
{n:2}
複製代碼
分析:
const obj = new Proxy({}, {
set(target, key, value, r) {
console.log(key, value)
if (key === 'a') Reflect.set(target, key, 'isA', r);
else Reflect.set(target, key, value, r);
}
});
obj.b = obj.a= {n: 1};
// 輸出:
// "a" {n: 1}
// "b" {n: 1}
obj.a; // isA
obj.b; // {n: 1}
複製代碼
能夠得出 賦值的順序是從右邊開始到左邊的。
並且是直接 a = {n: 1}, a.x = {n:1 },而不是 a.x = a 這樣去賦值
複製代碼
再借助 Proxy 來分析一開始part1這道題,用obj.a, obj.b 來代替原題目的 a和b。
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
obj.a = {n: 1 };// setting a;
obj.b = obj.a; // getting a; setting b;
obj.a.x = obj.a = {n:2 }; // getting a; setting a;
*****************分割線**********************************
console.log(obj.a); //{n;2}
obj.a.n=3; //getting a
console.log(obj.b); //getting b {n:1,x:{n:3}}
複製代碼
第一部分:
能夠看到obj.a.x = obj.a = {n: 2}這段語句執行時,會先輸出一個 getting a 再輸出 setting a。
這就意味着在對 obj.a.x 賦值時,程序是先獲取 obj.a指向的對象的內存地址,此時觸發了 getting a,
而後再對右邊 obj.a 進行賦值,觸發了 setting a, 賦值完最後一步纔是對 obj.a.x賦值 {n:2 }。
注意:最後一步對obj.a.x賦值時,不會再去讀取obj的a,則說明此時的a還是指向原來的地址-- 對象{n:1}的地址,也是obj.b指向的地址。
賦值以後,此時obj.a(和obj.b再也不指向同一地址)和obj.b.x指向的是同一地址 -- {n:2}的地址
複製代碼
第二部分:
在分割線之下,obj.a.n=3; 讀取的a再賦值,改變了指向的對象{n:2}的值
而obj.b.x也指向該對象
複製代碼
if (!("a" in window)) {
var a = 1;
}
alert(a); //undefined
複製代碼
此時代碼的意思是,若是沒有這個屬性,則建立這個屬性且賦值。
但if不是代碼塊,存在變量提高,因此上面代碼實際爲
var a;
if (!("a" in window)) {
a = 1;
}
alert(a);
複製代碼
函數內屬於一個做用域,try和catch不屬於一個做用域
(function f() {
try{
throw new Error()
}catch (x) {
var x=1,y=2;
var t=3;
console.log(x);
}
console.log(t); //3
console.log(x); //undefined
console.log(y); //2
})();
複製代碼
在f函數中,x、y、t會變量提高
在catch中,var x=1,就近原則,此時對x的賦值實際上是對形參的賦值,不是f函數裏的x,因此函數f的x在catch外訪問依舊是undefined
複製代碼
聲明變量b是一個函數,使用函數表達式
一、let b=function(){}
二、let b=function f(){} //能夠爲函數起一個名字,可是'f'不屬於window的屬性,則沒法調用f(),至關於匿名
下面程序的輸出結果?
var a = 1;
var b = function a(x) {
x && a(--x);
};
alert(a); //1
console.log(b(a)) //undefined 由於函數沒有return,有return的話就是0
/* 在函數外部,a是number類型,在外面調用函數a會出錯,但在函數內部調用不會出錯 上面其實等同於 var b=function(x){...} */
複製代碼
例子:
function a(){console.log('function')}
var a;
console.log(typeof a) //function
//若是變量賦值了
function a(){console.log('function')}
var a=1;
console.log(typeof a) //number
複製代碼
var a = 4;
function b() {
a = 3;
console.log(a);
function a(){};
}
b(); //3
console.log(a); //4
複製代碼
函數b內的函數a會提高,而後被a=3覆蓋了值,因此輸出3
挖坑:
var a = 4;
function b() {
a = 3;
}
console.log(a); //4
複製代碼
爲何不是輸出3?—— 由於沒有調用b()....
string、number、boolean、undefined、object、function、symbol
null與undefined
JSON.stringify轉化null與undefined
JSON.stringify({1:undefined}) //「{}」
JSON.stringify({0:null}) //「{「0」:null}」
複製代碼
返回undefined的狀況
symbol
var sm=Symbol('xx');
console.log(sm); //Symbol('xx')
typeof sm; //'symbol'
var ss1=Symbol('xx');
console.log(ss==ss1); //false
ss=ss1; //由於是基本數據類型,因此能夠用=令兩個值相等
console.log(ss===ss1); //true
複製代碼
object、String、Number、Boolean、Math、Array、Function、JSON
String、Number、Boolean、Object、Function、RegExp、Error、Date、Array
Date.now():返回當前時間毫秒數
var dt=new Date();
dt.getFullYear(); //年
dt.getMonth(); //月(0-11)
dt.getDate(); //日(1-31),返回一個月中的某一天
dt.getDay(); //天(0-6),返回一星期中的第幾天(0表示星期日)
dt.getHours() //小時(0-23)
dt.getMinutes() //分鐘(0-59)
dt.getSeconds() //秒數(0-59)
dt.getTime() //返回從1970/1/1 至今的毫秒數
##有對應的set方法
複製代碼
var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]); //456
複製代碼
解析:
由於鍵的名稱只能是字符串,b/c做爲鍵會調用toString獲得的都是[object Object],因此a[b]和a[c]都是a["[object Object]"]
複製代碼
function fun1(fn){ //第二步
arguments[0](3,4); //類數組枚舉調用傳入的fun2(3,4)
}
function fun2(){
alert(this.length); //此時this是fun1的arguments,表示fun1傳入的實參個數--5
alert(arguments.length); //此時表示fun2傳入的實參個數-- 2
}
fun1(fun2,5,6,7,8); //第一步
複製代碼
for(var i=0;i<5;i++){
setTimeout(console.log(i),0) //第一個參數不是函數
//0
//1
//2
//3
//4
}
複製代碼
parseInt(string [,radix]):解析一個字符串參數,並返回一個指定基數的整數
參數:
返回值:
若是radix爲undefined或0或未指定,
一、若是string以‘0x’或‘0X’開頭,則基數是16(16進制)
二、若是string以‘0’開頭,則基數是8或10進制,具體是哪一個進制由實現環境決定
三、若是字符串string以其餘任何值開頭,則基數是10
複製代碼
例子:
['10','10','10','10','10'].map(parseInt);
// [10, NaN, 2, 3, 4]
複製代碼
實際執行的代碼是:
['10','10','10','10','10'].map((item, index) => {
return parseInt(item, index)
})
//第一個radix爲0,屬於上面所說的狀況,此時看item,不是以0或0x開頭,則基數是10,10的十進制就是10
//第二個 10,radix是1,表示10是1進製表示的數字,錯誤
....
複製代碼
1)主線程發起一個異步請求
2)相應的工做線程接收到請求後告知主線程已收到(異步函數返回)
3)主線程能夠繼續執行下面的代碼,同時工做線程執行異步任務。
4)等到工做線程完成任務後,通知主線程
5)主線程收到通知後,執行必定動做(調用回調函數)。
JS引擎中負責解釋和運行js代碼的只有一個,叫作主線程,還有其餘線程,例如處理Ajax請求的線程,處理DOM事件的線程,定時器線程等工做線程(可能存在與js引擎內或外)。
綜上,能夠總結爲兩個要素:
一、發起函數(註冊函數)-- 用來發起異步過程
二、執行回調函數 -- 用來處理結果
兩個函數都是在主線程上調用的
異步任務執行完畢後須要通知主線程,這個通知機制是如何實現的?—— 消息隊列和事件循環
異步任務執行完畢後,工做線程將回調函數封裝成消息message放到消息隊列中,主線程經過事件循環過程去取出消息
例子:
setTimeout(fn,2000)
複製代碼
setTimeout就是發起異步過程的函數,fn就是回調函數,可是回調函數並不必定要做爲發起函數的參數,如:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = xxx; // 添加回調函數
xhr.open('GET', url); //發起異步請求的函數
xhr.send(); // 發起函數
複製代碼
除了廣義的同步任務和異步任務以外,還有宏任務和微任務
不一樣的任務會進入不一樣的事件/任務隊列,好比setTimeout
和setInterval
會進入相同的Event Queue。
先執行宏任務隊列的一個,而後再執行宏任務下的全部微任務,再去渲染,完成一次事件循環。而後再執行下一個宏任務。
事件循環的一次循環,就是執行事件隊列裏的一個任務。先執行宏任務,再執行宏任務裏的微任務,這樣就是一次循環,第一輪循環執行後,在執行下一次循環
例子
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
複製代碼
結果:1,7,6,8,2,4,3,5,9,11,10,12。
第一輪事件循環:
第二輪事件循環:
第三輪事件循環:
例子
process.nextTick(() => { //微任務
console.log('nextTick')
})
Promise.resolve()
.then(() => { //微任務
console.log('then')
})
setImmediate(() => { //宏任務
console.log('setImmediate')
})
console.log('end')
複製代碼
運行結果:
end
nextTick
then
setImmediate
複製代碼
微任務裏的微任務
process.nextTick(()=>{
console.log(1)
process.nextTick(()=>{
console.log(2)
})
})
process.nextTick(()=>{
console.log(3)
})
console.log('run')
複製代碼
run
1
3
2
複製代碼
promise是異步編程的解決方案,比傳統的異步編程解決方案【事件】和【回調函數】更加合理和強大。
promise對象用於異步操做,它表示未完成且預計將來完成的異步操做
使異步變同步
async function timeout() {
return 'hello world'
}
console.log(timeout());
console.log('雖然在後面,可是我先執行');
複製代碼
結果
Promise {<resolved>: "hello world"}
test.html:85 雖然在後面,可是我先執行
複製代碼
獲取Promise的值
async function timeout() {
return 'hello world'
}
timeout().then(result => {
console.log(result);
})
console.log('雖然在後面,可是我先執行');
複製代碼
結果
雖然在後面,可是我先執行
hello world
複製代碼
await
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
} )
}
async function testResult() {
let result = await doubleAfter2seconds(30);
console.log(result); //60
}
testResult();
複製代碼
var xhr=new XMLHttpRequest();
xhr.open('get','/index.js',true) //true表示異步
xhr.onreadystatechange=function () {
if(xhr.readyState===4)
if(xhr.status===200){
console.log(xhr.responseText)
}
}
xhr.send(null); //將請求發送給服務端,僅用於post請求
複製代碼
xhr.open()的第三個參數表示是否異步
XMLHttpRequest 對象若是要用於 AJAX 的話,其 open() 方法的 async 參數必須設置爲 true
xhr.send():當使用初始化)send方法還未調用
1:(載入)已調用send方法,正在發送請求
2:(載入完成)send方法已經執行完畢,已經接收到全部響應內容
3:(交互)正在解析響應內容
4:(完成)響應內容解析完成,能夠在客戶端調用
複製代碼
cookie
storage
共同點:
數據有效時間
做用域不一樣:
DOMContentLoaded:顧名思義,就是dom內容加載完畢。那什麼是dom內容加載完畢呢?咱們從打開一個網頁提及。當輸入一個URL,頁面的展現首先是空白的,而後過一會,頁面會展現出內容,可是頁面的有些資源好比說圖片資源還沒法看到,此時頁面是能夠正常的交互,過一段時間後,圖片才完成顯示在頁面。從頁面空白到展現出頁面內容,會觸發DOMContentLoaded事件。而這段時間就是HTML文檔被加載和解析完成。
onload:頁面上全部的資源(圖片,音頻,視頻等)被加載之後纔會觸發load事件
var str='https://www.bilibili.com/text.html'
console.log(escape(str));
console.log(encodeURI(str));
console.log(encodeURIComponent(str));
encodeURI("http://www.w3school.com.cn/My first/")
複製代碼
輸出
https%3A//www.bilibili.com/text.html
test.html:59 https://www.bilibili.com/text.html
test.html:60 https%3A%2F%2Fwww.bilibili.com%2Ftext.html
http://www.w3school.com.cn/My%20first/
複製代碼
所以,defer與async的區別就是js加載以後什麼時候執行:
它們在加載腳本都是異步執行
async在加載以後就會當即執行
若是存在多個有defer屬性的腳本,那麼它們是按照加載順序執行腳本的
都只適用於外部腳本文件,對與內聯的 script 標籤是不起做用
複製代碼
因此若是必定要將script放在頭部,但又不想阻塞頁面的加載,則加上defer屬性
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瀏覽器
}
複製代碼
假設:一個英文字符佔用一個字節,一箇中文字符佔用兩個字節
function getBytes(str){
var len = str.length; //獲取字符串長度
var bytes = len;
for(var i = 0; i < len; i++){
if (str.charCodeAt(i) > 255) bytes++; //中文則+1
}
return bytes;
}
alert(getBytes("你好,as"));
複製代碼
function f(){}
typeof f.prototype; //"object"
typeof f.__proto__; //"function"
f.__proto__===Function.prototype //true
typeof Function.prototype; //"function"
複製代碼
web 前端是應用服務器處理以前的部分,前端主要包括:HTML、CSS、javascript、image 等各類資源,針對不一樣的資源有不一樣的優化方式。
內容優化
服務器優化
CSS 優化
javascript 優化
圖像優化
應用:將節點列表 (NodeList) 轉換爲數組
若是你運行 document.querySelectorAll("p") 方法,它可能會返回一個 DOM 元素的數組 — 節點列表對象。 但這個對象並不具備數組的所有方法,如 sort(),reduce(), map(),filter()。 爲了使用數組的那些方法,你須要把它轉換爲數組。
var elements = document.querySelectorAll("p"); // NodeList
var arrayElements = [].slice.call(elements); // 如今 NodeList 是一個數組
var arrayElements = Array.from(elements); // 這是另外一種轉換 NodeList 到 Array 的方法
複製代碼
例子
var a4 = { a: 1 }
var a3 = Object.create(a4);
console.log("a3.__proto__:", a3.__proto__); // Object {a: 1}
console.log(oo.constructor.prototype==Object.prototype)//true
console.log(a3.__proto__ === a3.constructor.prototype); //false
//通常obj.__proto__=obj.constructor.prototype,除了Object.create()建立的對象
複製代碼
function out(x){
return function () {
let ins='inside'
console.log(x)
}
}
let inside=out('xx');
inside();
複製代碼
打斷點以後能看到Scopes裏的closure對象就是閉包,裏面保存着引用的變量
在JavaScript中,有一個很重要的安全性限制,被稱爲同源策略。這一策略對於JavaScript代碼可以訪問的頁面內容作了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下(協議+域名+端口號相同)的內容。
script、image、link都是能跨域的,是發送get請求。在頁面上有三種資源是能夠與頁面自己不一樣源的。它們是:js腳本,css樣式文件,圖片
總結:
瀏覽器將CORS請求分爲兩類:簡單請求和非簡單請求,對於非簡單請求,在正式通訊以前,增長一次HTTP查詢請求,稱爲「預檢」請求。
簡單請求:
一、get、post、head
二、HTTP頭信息不超過如下幾種字段:Accept、Accept-Language、Content-Language、Content-Type(application/x-www-form-urlencoded 或 multipart/form-data 或 text/plain)
複製代碼
非簡單請求:
對服務器有特殊要求的請求,好比put、delete,或者Content-Type類型是application/json
複製代碼
util.js文件
//一、先定義後導出
let num1=1; let num2=2;
export {num1,num2};
//二、直接導出定義的變量
export var num=1;
export var num2=2;
main.js
//接收
import {num1,num2} from 'util.js'
//能夠起別名
import {num1:n1,num2:n2} from 'util.js'
//能夠一次性全接收
import * as obj from 'util.js';
console.log(obj.num1,obj.num2);
---------------------------
util.js文件
//三、導出默認數據
export default const obj={}
main.js
//接收
import data from 'util.js'; //能夠隨意命名一個變量
-------------------------------
//同時導出默認的和部分的,export default只能使用一次
export default const example3 = {
birthday : '2018 09 20'
}
export let name = 'my name'
export let age = 'my age'
export let getName = function(){ return 'my name'}
// 導入默認與部分
import example3, {name, age} from './example1.js'
複製代碼
用ES5實現迭代器的建立
function generator(arr) {
let i=0;
return {
next(){
let done=(i>=arr.length);
let value=!done? arr[i++]:undefined
return{done,value}
}
}
}
let iterator=generator([1,2,3]); //獲得迭代器對象
let obj=iterator.next();
while (!obj.done) {
console.log(obj.value);
obj=iterator.next();
}
複製代碼
用ES6的生成器建立迭代器,迭代數組
function *Generator(arr) {
for(let i=0;i<arr.length;i++){
yield arr[i]
}
}
let it=Generator([1,2,3]);
let obj_=it.next();
while (!obj_.done){
console.log(obj_.value);
obj_=it.next()
}
複製代碼
可迭代對象有:數組、字符串、map、set對象,這些都是含有Symbol.iterator屬性
es6模塊
export const person = {
name: 'lc',
friends: ['xm','xh']
};
setTimeout(() => person.name ='liangcheng', 500);
複製代碼
commonjs模塊
var dog = {
name: 'xsz', // 不要猜想是誰的名字
owner: ['lc']
};
module.exports = dog;
setTimeout(() => dog.name = 'xushizhou' ,500);
複製代碼
引用模塊
import { person } from './a1';
const dog = require('./a2');
setTimeout(() => console.log(person.name, dog.name), 1000); //liangcheng xushizhou
複製代碼
三要素:封裝、繼承、多態
# 1.封裝
// 假設須要登記學籍,分別記錄小明和小紅的學籍號、姓名
let name1 = "小明"
let num1 = "030578001"
let name2 = "小紅"
let num2 = "030578002"
// 若是須要登記大量的數據,則弊端會很是明顯,並且很差維護,那麼咱們會使用如下方法來登陸,這也是面向對象的特性之一:封裝
let p1 = {
name:"小明",
num:"030578001"
}
let p2 = {
name:"小紅",
num:"030578002"
}
# 2.繼承
// 從已有的對象上,獲取屬性、方法
function Person(){
this.name = "邵威儒"
}
Person.prototype.eat = function(){
console.log("吃飯")
}
let p1 = new Person()
p1.eat() // 吃飯
let p2 = new Person()
p2.eat() // 吃飯
# 3.多態
// 同一操做,針對不一樣對象,會有不一樣的結果
let arr = [1,2,3]
arr.toString() // 1,2,3
let obj = new Object()
obj.toString() // [object Object]
複製代碼
總結: 面向對象的底層仍是面向過程,面向過程抽象成類,而後封裝,方便使用就是面向對象。
(Parent.apply(this))
class Parent{
constructor(){
this.x='parent'
}
}
class Son extends Parent{
constructor(){
var s=super()
console.log(s === this); //true
}
}
var son=new Son()
複製代碼
好處:
壞處:
$.fn.repaint = function () {
this.each(function (item) {
return item.clientHeight;
});
}
複製代碼
CSS3動畫與javascript模擬動畫有如下區別:
做用:使前端實現模塊化開發,可以使用require與exports,export與import
優勢:
原理:一切皆爲模塊,因爲 webpack 並不支持除 .js 之外的文件,從而須要使用 loader 轉換成 webpack 支持的模塊,plugin插件用於擴展 webpack 的功能,在 webpack 構建生命週期的過程在合適的時機作了合適的事情。
webpack 從構建到輸出文件結果的過程
vue.js是經過Object.defineProperty以及發佈訂閱模式來進行數據劫持和監聽
v-model實際是個語法糖,原理是用v-bind綁定表單組件的value/selected/checked,和v-on綁定input/change事件。一般用在表單控件的雙向綁定。
對於添加了v-model指令的組件(若是含表單控件input),會默認利用value和input事件。但若是是其餘類型的表單控件checkbox,就會利用checked和change事件,因此爲了通用性,組件將會有model對象屬性,定義了prop和event屬性來定義綁定的值和事件類型
<base-checkbox v-model="lovingVue"></base-checkbox>
複製代碼
<base-checkbox v-bind:checked="lovingVue" v-on:change="lovingVue=arguments[0]"></base-checkbox>
複製代碼
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
//checkbox表單控件本身有change事件,它的事件監聽函數觸發的是這個vue組件的change事件,是父組件綁定的事件,兩個change事件是屬於不一樣對象的
>
`
})
複製代碼
綜上:父組件的lovingVue傳遞給子組件,子組件用checked接收,注意在子組件要在props屬性顯示指出checked這個prop。當子組件的表單控件發生變化,觸發父組件綁定的事件,將表單控件的值做爲事件函數的參數傳遞過去,從而改變父組件的lovingVue值。實現子組件更新父組件的值。
相同:二者都是能監聽/依賴一個數據,並進行相應的處理
computed:相似於過濾器,對綁定到視圖的數據進行處理
watch:watch是一個偵聽的動做,用來觀察和響應 Vue 實例上的數據變更
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
複製代碼
watch執行異步操做,限制咱們執行該操做的頻率,並在咱們獲得最終結果前,設置中間狀態。這些都是計算屬性沒法作到的:
<!-- 由於 AJAX 庫和通用工具的生態已經至關豐富,Vue 核心代碼沒有重複 -->
<!-- 提供這些功能以保持精簡。這也可讓你自由選擇本身更熟悉的工具。 --> <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script> <script> var watchExampleVM = new Vue({ el: '#watch-example', data: { question: '', answer: 'I cannot give you an answer until you ask a question!' }, watch: { // 若是 `question` 發生改變,這個函數就會運行 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, created: function () { // `_.debounce` 是一個經過 Lodash 限制操做頻率的函數。 // 在這個例子中,咱們但願限制訪問 yesno.wtf/api 的頻率 // AJAX 請求直到用戶輸入完畢纔會發出。想要了解更多關於 // `_.debounce` 函數 (及其近親 `_.throttle`) 的知識, // 請參考:https://lodash.com/docs#debounce this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) }, methods: { getAnswer: function () { if (this.question.indexOf('?') === -1) { this.answer = 'Questions usually contain a question mark. ;-)' return } this.answer = 'Thinking...' var vm = this axios.get('https://yesno.wtf/api') .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = 'Error! Could not reach the API. ' + error }) } } }) </script> 複製代碼
總結:computed主要用於對同步數據的處理,watch則主要用於觀測某個值的變化去完成一段開銷較大的複雜業務邏輯。
有兩種常見的試圖改變一個prop的情形:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
複製代碼
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
複製代碼
不能。
雖然Object.defineProperty()能夠監聽到數組下標的變化,
可是爲了性能優化,vue源碼並無使用Object.defineProperty()來劫持數組類型的數據:
複製代碼
如何解決?
難點:比較兩棵虛擬DOM樹的差別
比較兩棵 DOM 樹的差別是 Virtual DOM 算法最核心的部分,這也是所謂的 Virtual DOM 的 diff 算法。
因此 Virtual DOM 只會對同一個層級的元素進行對比:
上面的 div 只會和同一層級的 div 對比,第二層級的只會跟第二層級對比。這樣算法複雜度就能夠達到 O(n)。深度優先遍歷,記錄差別
在實際的代碼中,會對新舊兩棵樹進行一個深度優先的遍歷,這樣每一個節點都會有一個惟一的標記:
在深度優先遍歷的時候,每遍歷到一個節點就把該節點和新的的樹進行對比。若是有差別的話就記錄到一個對象裏面。
new一個Vue實例時,實例只有默認的事件和生命週期函數
最先能訪問到this是:beforeCreate函數
在beforeCreate函數以後created以前:data、methods、watch、computed屬性進行初始化
在created函數能訪問data、methods、watch、computed屬性,除了el屬性
在created函數以後,beforeMount函數以前:進行模板編譯
beforeMounte函數:此時內存已構建出虛擬的dom樹,視圖還未更新,不能訪問dom上一些動態變量,此時訪問el還是未各類表達式{{}}
beforeMounte函數以後:建立vm.$el,與el進行替換
Mounted函數:已將虛擬dom掛載到頁面上,能訪問dom上的動態變量
//父組件
<child>
<div slot="xxx">父組件動態插入數據</div>
<div slot="yyy">父組件動態插入數據</div>
</child>
//子組件
<template>
<div>
<slot name='xxx'></slot>
<div>組件肯定的標籤結構</div>
<slot name='yyy'></slot>
</div>
</template>
複製代碼
// component-a 子組件
export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
複製代碼
// 父組件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 彈窗
}
}
</script>
複製代碼
//event-bus.js文件
export default const install=function(Vue){
Vue.prototype.$bus=new Vue({
methods:{
emit(event,...args){
this.$emit(event,...args);
},
on(event,callback){
this.$on(event,callback);
},
off(event,callback){
this.$off(event,callback)
}
}
})
}
複製代碼
在入口文件main.js引入插件
import bus from 'event-bus.js'
Vue.use(bus)
複製代碼
使用$bus綁定事件this.$bus.on()要注意:
一、綁定在哪一個生命週期函數,才能確保在其餘組件觸發事件以前已經綁定好事件,即組件間生命週期函數的執行順序
二、在組件銷燬以前將事件解除,不然當再有組件綁定該事件,添加事件監聽函數時,此時事件的監聽函數還保留着以前的事件監聽函數,即會觸發多個回調。
vuex
pubsub消息發佈/訂閱
//訂閱消息
mounted: {
PubSub.subscribe("deleteTodo",(messageName, todosIndex)=>{
this.deleteTodo(todosIndex);
});
//發佈消息
methods: {
PubSub.publish("deleteTodo", this.index);
}
複製代碼
多級組件嵌套須要傳遞數據時,一般使用的方法是經過vuex。但若是僅僅是傳遞數據,而不作中間處理,使用 vuex 處理,未免有點大材小用。
$attrs
:包含了從父組件接收的(來自父做用域的),但props沒有定義的數據 (class 和 style 除外)。 當一個組件沒有聲明任何 prop 時,$attrs這裏會包含全部父做用域的綁定 (class 和 style 除外),而且能夠經過 v-bind="$attrs" 傳入內部組件 這是惟一一次v-bind直接與=相連,通常要傳給子組件且props能接收的要v-bind:變量名=數據
$listeners
:包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它能夠經過 v-on="$listeners" 傳入內部組件
總結:
父向子傳遞數據:props,$attrs/$listeners、slot(傳遞的是標籤數據)
子向父傳遞數據:事件綁定和觸發$emit事件回調,$ref獲取組件實例
兄弟間:bus,vuex,pubsub訂閱發佈消息
跨級通訊:bus,vuex,pubsub訂閱發佈消息,$attrs/$listeners
任意級別通訊:bus、vuex、pubsub訂閱發佈消息
一、state是什麼?
二、mapState輔助函數 mapState是語法糖,不用在computed裏屢次定義函數,而後返回不一樣的this.$store.state.數據名,能夠直接將store的state直接添加到computed裏
import { mapState } from 'vuex'
...
computed: mapState({
count: 'count', // 第一種寫法,count爲state裏定義的,後面的count能夠省略
sex: (state) => state.sex, // 第二種寫法
from: function (state) { // 用普通函數this指向vue實例,要注意
return this.str + ':' + state.from
},
// 注意下面的寫法看起來和上面相同,事實上箭頭函數的this指針並無指向vue實例,所以不要濫用箭頭函數
// from: (state) => this.str + ':' + state.from
//也能夠在mapState裏定義不使用到state的函數
myCmpted: function () {
// 這裏不須要state,測試一下computed的原有用法
return '測試' + this.str
}
})
複製代碼
三、...mapState
computed:{
//原來的繼續保留
fn1(){ return ...},
fn2(){ return ...},
fn3(){ return ...}
......
//再維護vuex
...mapState( //這裏的...不是省略號了,是對象擴展符
[ count ]
)
}
複製代碼
語法:
Vue.directive('指令名',{
bind(el,binding,vnode){
//一開始綁定時調用,只調用一次
//el:綁定指令的dom元素,binding傳遞的是綁定的值,存儲的是對象
},
update(el,binding,vnode){
//綁定的數據發生更新時調用,0-屢次
}
})
複製代碼
<p v-focus:top="10"></p>
Vue.directive('focus',{
bind(el,binding,vnode){
el.style[binding[arg]]=binding[value]+'px'
},
update(el,binding,vnode){
//綁定的數據發生更新時調用,0-屢次
}
})
複製代碼
directives:{
focus:{
binding(el,binding){
}
}
}
複製代碼
當Vue用 v-for 正在更新已渲染過的元素列表是,它默認用「就地複用」策略。若是數據項的順序被改變,Vue將不是移動DOM元素來匹配數據項的改變,而是簡單複用此處每一個元素,而且確保它在特定索引下顯示已被渲染過的每一個元素。
一、當使用路由參數切換路由,實現同一個路由路徑+不一樣的參數來展現不一樣的內容,即:先後都是同一組件,但顯示的內容不同
watch:{
$route(to,from){
//to:就是切換後的路由
//根據需求更改數據
}
}
複製代碼
二、使用路由切換組件時,一切換以前的組件就會被銷燬,再次切換到該組件時,以前的狀態不會保存,由於是再次建立的
vue-router 提供的導航守衛主要用來經過跳轉或取消的方式守衛導航。 有多種機會植入路由導航過程當中:全局的, 單個路由獨享的, 或者組件級的。
展現一種全局的,更多的查看連接:
定義路由的時候能夠配置 meta 字段,屬性值是對象
meta:能夠與上面的導航守衛搭配使用,好比切換到網站的主頁路由時,須要用戶登陸纔可成功跳轉,顯示組件,那就在該路由加配置meta字段,當跳轉路由時,就會觸發router.beforeEach鉤子函數,在裏面進行對路由的meta裏自定義的一些標誌位的判斷,再決定是否放行。
SPA:僅在Web頁面初始化時加載相應的HTML、JavaScript、CSS,加載完成後,不會由於用戶的操做而進行頁面的從新加載或跳轉。取而代之的是利用路由機制來實現HTML內容的變換,避免頁面的從新加載。
優勢:
缺點:
服務端渲染SSR的優缺點:
在vue中,不能檢測到如下對數組的操做:
緣由?
vue只能監聽如下八種對數組操做的方法:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
複製代碼
vue中爲了對對象的每層屬性都實現監聽,須要不斷遍歷對象的屬性,使用Object.defineProperty()來實現數據劫持,則Object.defineProperty()只能劫持數據的屬性,若是能直接劫持一個完整的對象而非對象上的屬性,則無需層層遍歷屬性了。
Proxy:
Proxy 能夠理解成,在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這裏表示由它來「代理」某些操做,能夠譯爲「代理器」。
var proxy=new Proxy(target,handler)
參數:
返回值:
let obj = {};
let handler = {
get(target, property) {
console.log(`${property} 被讀取`);
return property in target ? target[property] : 3;
},
set(target, property, value) {
console.log(`${property} 被設置爲 ${value}`);
target[property] = value;
}
}
let p = new Proxy(obj, handler);
p.name = 'tom' //name 被設置爲 tom
p.age; //age 被讀取 3
複製代碼
p 讀取屬性的值時,實際上執行的是 handler.get() :在控制檯輸出信息,而且讀取被代理對象 obj 的屬性。
p 設置屬性值時,實際上執行的是 handler.set() :在控制檯輸出信息,而且設置被代理對象 obj 的屬性的值。
handler能夠定義多種攔截的函數:Proxy
綜上:取代Object.defineProperty()的Proxy有如下兩個優勢
能直接劫持整個對象,無需經過遞歸和遍歷data對象來實現對數據的監控
有多種劫持操做
複製代碼