var a = 1;
function changeA() {
console.log(a);
var a;
}
changeA();
複製代碼
猜猜輸出啥,undefined,由於函數做用域內發生了變量提高,函數內的代碼至關於
var a; console.log(a);
html
var a = 1;
function changeA() {
a = 2;
console.log(a);
var a;
}
changeA();
複製代碼
猜猜輸出啥,a=2,由於變量提高是直接到函數做用域頂部,在輸出a以前,a已經賦值2,且函數做用域內a是局部變量web
var a = 1;
function changeA(a) {
console.log(a);
var a;
}
changeA(a);
複製代碼
輸出 1,由於a是傳參進來的,至關於在函數做用域內a已經聲明和賦值了 另外:ajax
(function () {
a=5;
console.log(window.a) // undefined,發生了變量提高
var a = 10;
})() // 這個自執行保證了 `var a` 發生變量提高時,不會污染到全局window.a
複製代碼
函數的聲明優先級低於變量:算法
var sum = function () {
console.log(1);
}
function sum() {
console.log(2);
}
sum() // 1
複製代碼
函數聲明提高會被後面的覆蓋chrome
foo(); // 3
function foo() {
console.log( 1 );
}
function foo() {
console.log( 3 );
}
複製代碼
if-else語句裏面的函數聲明都有效json
foo(); // "b"
var a = true;
if (a) {
function foo() { console.log("a"); }
} else {
function foo() { console.log("b"); }
}
複製代碼
函數調用中,傳遞是一個數值,咱們稱爲 「值傳遞」。 函數調用中,傳遞是對象,通常稱爲 「引用傳遞」。引用類型:Object Array Fucntion;對於傳參而言,傳入的東西是不變的。segmentfault
var a = 1;
function changeA(a) {
a++;
}
changeA(a);
console.log(a);
複製代碼
這裏會輸出1,a的值沒有改變跨域
var a = [1, 2, 3];
function changeA(a) {
a[0] = 2;
}
changeA(a);
console.log(a);
複製代碼
這裏輸出[2, 2, 3],傳入的是指針,指針沒有改變,而值發生了變化
再看看下面的例子,若是數組以...arr的方式傳遞,會發生什麼數組
var a = [1, 2, 3];
function changeA(...a) {
a[0] = 2;
}
changeA(a);
console.log(a); // [1, 2, 3]
複製代碼
引用類型全等於永遠返回false,由於變量存儲的是地址值,好比new Object瀏覽器
NaN === NaN // false
+0 === -0 // true
+0 === 0 // true
-0 === 0 // true
+Infinity === Infinity // true
+Infinity === -Infinity // false
-Infinity === Infinity // false
null === undefined // false
[] === [] // false
{} === {} // false
複製代碼
Javascript是弱類型語言,之因此不一樣的數據類型之間能夠作運算,是由於JavaScript引擎在運算以前會悄悄的把他們進行了隱式類型轉換的。
如下假設爲比較 x == y的狀況,Type(x)指的是x的數據類型,Type(y)指的是y的類型,最終返回值只有true或false。
參考教程
其實第一次讀的時候,仍是有點理解不了。仍是系統的剖析一下吧,簡單點來講,比較x == y
這個表達式,當 x 和 y 相同,直接進行值比較。當 x 和 y 不一樣,最終必定會把 x 和 y 轉換爲要麼number、要麼string再作值比較。
當 x 和 y 都是 基礎類型 時,會轉化爲相同的基礎類型再比較,boolean類型會轉化爲Number類型再作比較,當有一方是Number時,最終會轉化爲Number類型再比較,Number類型的優先級是最高的,因此須要熟悉一下Number(x)
這個函數,Number(x)
和+x
效果是同樣的,枚舉一下可能碰見的狀況吧:
Number('1.5') //1.5
Number('1,2') //NaN
Number({}) //NaN
Number([]) //0
Number([2]) //2
Number(true) //1
Number(null) //0
Number(undefined) //NaN
當 x 和 y 兩個都是引用類型,好比數組、對象、Function,會直接返回false,由於 x 和 y 儲存的是地址。
當 x 和 y 一方是引用類型時,會進行 ToPrimitive(x || y)
的轉化。若是 x.valueOf
能返回基礎數據類型,則會優先調用 valueOf
方法,若是不能返回基礎數據類型,則會調用 toString
方法。toString
必定會返回基礎數據類型。
找幾個例子說說:
true == true
Number(true) == true
=>1 == Number(true)
=>1 == 1
左值true
先轉化爲數字,右值true
再轉化爲數字,而後進行值比較
答案:true
true == '123'
Number(true) == '123'
=>1 == Number('123')
=>1 == 123
true先轉化爲數字,再把'123'轉化爲數字123,再比較左值和右值
答案:false
'123' == 2
Number('123') == 2
=>123 == 2
'123'先轉化爲數字,再比較左值和右值
答案:false
[1,2] == 2
toToPrimitive([1,2]) == 2
=>'1,2' == 2
=>Number('1,2') == 2
=>NaN == 2
只解析一項,NaN也是Number類型的一種,NaN與任何數字比較,返回false
答案:false
[1,2,3].valueOf() //[1,2,3] 不是基礎數據類型
[1,2,3].toString() //1,2,3,數組類型toPrimitive會調用這個方法
複製代碼
var c = {a:1}; //必須賦值,{a:1}.valueOf()會報錯
c.valueOf() //{a:1}不是基礎數據類型
c.toString() //[object Object],toPrimitive會調用這個方法
c == '[object Object]' //true
複製代碼
解釋一下爲啥 c == '[object Object]'
返回true
toPrimitive(c) == '[object Object]'
=>'[object Object]' == '[object Object]'
function b() { return 2;}
b.valueOf() //返回函數自己,即function b() { return 2;}
b.toString() //返回字符串,即'function b() { return 2;}',toPrimitive採用這個
b == 'function b() { return 2;}' //true
複製代碼
不介紹常見的狀態碼,主要針對1XX到5XX能加分的詳細說明
100 - Continue 初始的請求已經接受,客戶應當繼續發送請求的其他部分
101 - 服務器已經理解了客戶端的請求,並將經過Upgrade消息頭通知客戶端採用不一樣的協議來完成這個請求
202 - 接受和處理、但處理未完成
204 - 服務器成功處理了請求,但沒有返回任何內容
206 - 服務器已經完成了部分用戶的GET請求
303 - 臨時重定向,和302狀態碼有着相同的功能,可是303明確表示客戶端應當採用get方式請求資源
307 - 臨時重定向,和302狀態碼有着相同的功能,當30一、30二、303響應狀態碼返回時,幾乎全部瀏覽器都會把post改爲get,並刪除請求報文內的主體,以後請求會自動再次發送。307會遵守瀏覽器標準,不會從post變爲get。可是對於處理響應時的行爲,各類瀏覽器有可能出現不一樣的狀況。
400 - 語義有誤,當前請求沒法被服務器理解。除非進行修改,不然客戶端不該該重複提交這個請求
401 - 當前請求須要用戶驗證。該響應必須包含一個適用於被請求資源的 WWW-Authenticate 信息頭用以詢問用戶信息,瀏覽器據此顯示用戶名字/密碼對話框,而後在填寫合適的Authorization頭後再次發出請求
403 - Forbidden,服務器已經理解請求,可是拒絕執行它
405 - 請求行中指定的請求方法不能被用於請求相應的資源。該響應必須返回一個Allow 頭信息用以表示出當前資源可以接受的請求方法的列表
410 - Gone,被請求的資源在服務器上已經再也不可用,並且沒有任何已知的轉發地址。這樣的情況應當被認爲是永久性的
414 - Request-URI Too Long,請求的URI 長度超過了服務器可以解釋的長度,所以服務器拒絕對該請求提供服務
415 - Unsupported Media Type,對於當前請求的方法和所請求的資源,請求中提交的實體並非服務器中所支持的格式,所以請求被拒絕
421 - too many connections,從當前客戶端所在的IP地址到服務器的鏈接數超過了服務器許可的最大範圍。一般,這裏的IP地址指的是從服務器上看到的客戶端地址(好比用戶的網關或者代理服務器地址)
500 - Internal Server Error,做爲網關或者代理工做的服務器嘗試執行請求時,從上游服務器接收到無效的響應
502 - Bad Gateway,做爲網關或者代理工做的服務器嘗試執行請求時,從上游服務器接收到無效的響應
503 - Service Unavailable,因爲臨時的服務器維護或者過載,服務器當前沒法處理請求
504 - Gateway Timeout,做爲網關或者代理工做的服務器嘗試執行請求時,未能及時從上游服務器(URI標識出的服務器,例如HTTP、FTP、LDAP)或者輔助服務器(例如DNS)收到響應
一個域名的DNS記錄會在本地有兩種緩存:瀏覽器緩存和操做系統(OS)緩存,會優先訪問瀏覽器緩存,若是未命中則訪問OS緩存,最後再訪問DNS服務器(通常是ISP提供),而後DNS服務器會遞歸式的查找域名記錄,而後返回。
DNS記錄會有一個ttl值(time to live),單位是秒,意思是這個記錄最大有效期是多少。通過實驗,OS緩存會參考ttl值,可是不徹底等於ttl值,而瀏覽器DNS緩存的時間跟ttl值無關,每種瀏覽器都使用一個固定值。 修改hosts文件以後,爲啥有時會馬上生效,有時卻一直不生效呢?在修改hosts文件後,全部OS中DNS緩存會被清空,而瀏覽器緩存則不發生變化,在 chrome://net-internals/#dns
中點擊 Clear Host Cache
會清空OS緩存。若是發現DNS更改不成功,能夠靜待幾十秒。
瀏覽器在獲取網站域名的實際IP地址後會對其IP進行緩存,減小網絡請求的損耗。每種瀏覽器都有一個固定的DNS緩存時間,其中Chrome的過時時間是1分鐘,在這個期限內不會從新請求DNS。chrome://net-internals/#dns
OS緩存會參考DNS服務器響應的TTL值,可是不徹底等於TTL值
先檢查一下本身的緩存中有沒有這個地址,有的話就直接返回,沒有的話就去根域找,從根域開始遞歸查詢IP
根域的地址是寫死在ISP DNS服務器上的,根域便是/;好比www.a.com這樣的域名,先去根域找.com的服務器對應IP,而後.com的服務器對應IP找到a.com的服務器IP...
先通關正則,打好基礎,不然會看不明白,戳我。在正則優化的狀況下,使用new RegExp會比較快。緣由是new RegExp會在代碼執行的過程當中編譯正則,編譯就是在內存中開闢一個空間存放變量或函數,字面量有個廢棄的compile方法也能夠作到這個事情。
騰訊QQ號從10000開始,最少5位
[1-9][0-9]{4,}
複製代碼
\s表示匹配一個空白符,包括空格、製表符、換頁符、換行符和其餘 Unicode 空格,具體點就是[ \f\n\r\t\v\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004 \u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f \u3000
/(^\s+)|(\s+$)/g
複製代碼
將阿拉伯數字每三位一逗號分隔,如:15000000轉化爲15,000,000
'1500000000000'.replace(/\B(?=(\d{3})+$)/g,',')
複製代碼
[\u4e00-\u9fa5]
複製代碼
DNS規定,域名中的標號都由英文字母和數字組成,每個標號不超過63個字符,也不區分大小寫字母。標號中除連字符(-)外不能使用其餘的標點符號。級別最低的域名寫在最左邊,而級別最高的域名寫在最右邊。由多個標號組成的完整域名總共不超過255個字符。
/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/
複製代碼
分析:第一,不區分大小寫;第二,帶'的也算在裏面,好比don't;第三,帶-的也算在裏面,好比x-ray
var str = "When you are old and grey and full of sleep, don't make me think. And hid his face amid a crowd of stars.";
str.match(/[a-z]+([-'][a-z]+)?/ig);
複製代碼
分析:參數名=值,不一樣參數間用&分隔開
var name= 'rqlang';
reg = new RegExp('[&?]'+name+'=([^& ]+)')
str = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E6%AD%A3%E5%88%99%E8%8E%B7%E5%8F%96%E6%9F%90%E4%B8%AA%E5%8F%82%E6%95%B0&oq=%25E6%25AD%25A3%25E5%2588%2599%25E8%258E%25B7%25E5%258F%2596%25E6%259F%2590%25E4%25B8%25AA%25E5%258F%2582%25E6%2595%25B0&rsv_pq=baac736e00031a97&rsv_t=515dwC%2Bf5ZRACPl1sr7KbYkxhUwe9G1VxfTPLWRBaQ9vh4Oa8jc6nfh0pQg&rqlang=cn&rsv_enter=1&inputT=2550&rsv_sug3=107&rsv_sug1=103&rsv_sug7=100&rsv_sug2=0&rsv_sug4=3469'
val = str.match(reg)[1];
複製代碼
升級一下,獲取url中全部的參數名
var propReg = /([?&][^=]+)/g,
str = location.search,
arr = str.match(propReg);
arr.forEach(function(val, index, arr) {
arr[index] = arr[index].replace(/^(&|\?)/, '');
});
console.log(arr); // 全部的參數名
複製代碼
再升級一下,獲取url中全部參數對鍵值,以對象的方式展示
var reg = /([&\?][^=]+)=([^& ]+)/g,
str = location.search,
arr = str.match(reg);
arr.reduce(function(obj, val, index, arr) {
var reg = /[&?]([^=]+)=([^& ]+)/,
tmpArr = val.match(reg);
obj[tmpArr[1]] = tmpArr[2];
return obj;
}, {});
複製代碼
cookie的特徵,以';'爲分隔,末字段也許不帶';',prop值前面匹配一個或0個空格
function getCookie(name) {
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
複製代碼
readyState各類值,記不住不要緊,記住這幾個狀態都是圍繞着XMLHttpRequest對象自己,且最後一個狀態是4就能夠了:
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
// readyState
if (this.readyState == 4 && this.status == 200) {
console.log(1);
}
}
xmlHttp.open('GET', 'https://www.afea.com', false);
xmlHttp.setRequestHeader('X-Requested-With');
xmlHttp.withCredentials = true; // 跨域請求攜帶cookie
xmlHttp.send(null);
複製代碼
var data = {};
object.defineProperty(data, 'text', {
set(value) {
$input.value = value;
this.value = value;
}
});
$input.onChange = function () {
data.text = e.target.value;
}
複製代碼
形如sum(1,2)(3)(4,5)
的樣子:
sum(1,2) // 返回3
sum(3)(1,2) // 返回6
sum(1,3)(5)(2) // 返回11
複製代碼
思路是利用閉包,改寫toString方法。
function add () {
var total = 0;
var args1 = [...arguments];
var sum = function (...args) {
total = args1.concat(args).reduce((total, a) => total += a, total)
return sum;
}
sum.toString = function() { // sum方法調用,return必然會調用toString方法
return total;
}
return sum;
}
複製代碼
bind原是Function.prototype上的方法,能夠修改函數的this指向
Function.prototype.bind = function (context) {
// this指向調用函數
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return this.apply(context, args.concat(Array.prototype.slice.call(arguments)))
}
}
複製代碼
淺拷貝只複製指向某個對象的引用地址,而不復制對象自己,新舊對象仍是共享同一塊內存。但深拷貝會另外創造一個如出一轍的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。淺複製只複製一層對象的屬性,而深複製則遞歸複製了全部層級。
function deepClone(target) {
if (typeof target !== 'object' ) return target;
var arr_tmp = target instanceof Array ? [] : {};
for (var i in target) {
arr_tmp[i] = typeof target[i] !== 'object' ? target[i] : deepClone(target[i]);
}
return arr_tmp;
}
複製代碼
遞歸實現,若是當前元素是數組,則繼續遞歸,不然加入最終返回的扁平化數組
const flattern = (arr) => arr.reduce((a, item) => Array.isArray(item) ? a.concat(flattern(item)) : a.concat(item), [])
複製代碼
循環利用隊列
const flattern = (arr) => {
var finalArray = [];
if (!Array.isArray(arr)) return;
while(arr.length) {
var target = arr.shift();
if (Array.isArray(target)) {
arr = target.concat(arr);
} else {
finalArray.push(target)
}
}
return finalArray;
}
複製代碼
防抖主要是爲了限制函數的執行頻次,以優化函數執行頻次太高致使的響應速度跟不上觸發頻率的問題。若是倒計時沒有結束,則清空倒計時,再從新計時。有個弊端,若是事件不斷循環觸發,而且小於等待時間,則不可能執行回調事件,因此後來又催生了節流。
function debounce(fn, wait, immediate) {
var timer;
return function() {
var that = this, args = arguments;
if (immediate) {
fn.apply(that, args);
immediate = false;
}
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(that, args);
}, wait);
}
}
複製代碼
節流主要是爲了限制函數的執行頻次,以優化函數執行頻次太高致使的響應速度跟不上觸發頻率的問題。要記住這個概念,能夠聯想水龍頭滴水,水的必定量的,會一滴一滴的流出去,可是不必定會流光,時間間隔是必定的。直接上代碼:
function throttle(fn, wait, immediate) {
var timer, previous = new Date().getTime();
return function() {
var that = this, args = arguments, now = new Date().getTime();
if (immediate) {
fn.apply(that, args);
immediate = false;
}
if (wait >= now-previous) { // 到必定時間必定執行一次
previous = now;
fn.apply(that, args);
clearTimeout(timer);
} else {
timer = setTimeout(function() {
previous = new Date().getTime();
fn.apply(that, args);
}, wait);
}
}
}
複製代碼
要求:寫一個對象,實現調用a.work().sleep(20).lunch()
,會輸出
work
sleep 20 seconds
(wait 20 seconds)
lunch
複製代碼
用隊列實現鏈式調用,當調用鏈很長的時候遞歸,實際上是會報棧溢出的,最好在run里加個setTimeout 0
let a = {
query: [],
status: false,
run: function () { // 關鍵點,遞歸執行函數
setTimeout(() => {
if (this.status) return; // 若是隊列還在運行中,則返回
if (this.query.length > 0) {
this.query.shift()(this.run.bind(this))
}
}, 0)
},
work: function () {
this.query.push((fn) => {
console.log('work');
fn();
});
this.run();
return this;
},
lunch: function () {
this.query.push((fn) => {
console.log('lunch');
fn();
});
this.run();
return this;
},
sleep: function (time) {
this.query.push((fn) => {
this.status = true; // 只有異步會阻塞隊列執行而已,因此status的更新放在這裏
console.log(`sleep ${time} seconds`);
setTimeout(() => {
this.status = false;
fn();
}, time * 1000);
});
this.run();
return this;
}
}
複製代碼
假設有一個有序數組,須要查找一個數值爲3的元素,若是存在,返回第一個元素的下標,不然返回-1。
function binarySearch(arr, target) {
var low = 0, high = arr.length-1, mid;
while(low < high) {
mid = ~~((low + high)/2);
if(arr[mid] < target) {
low = mid+1;
} else {
high = mid;
}
}
if (arr[low] === target) return low;
else return -1;
}
複製代碼
相似整理撲克牌,將每一張牌插到其餘已經有序的牌中適當的位置。冒泡和選擇就不用說了,一個正方向一個反方向,兩個for循環搞定的。
for (var i = 1; i < arr.length; i++) {
for(var j=i;j > 0 && arr[j] < arr[j-1]; j--;) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]]
}
}
複製代碼
原理戳這,插入排序的晉級版,以gap爲界限分爲一組,每一組進行插入排序計算,一開始時,通常來講gap=length/2,因此穩定的複雜度爲nlogn
function heerSort(arr) {
var gap = ~~(arr.length/2); // 取整居然比/優先級高,只能用括號補了
for (var i=gap;i>0;i=~~(i/2))
for (var j=i;j<arr.length;j+=i)
for(var k=j;k>=i&&arr[k]<arr[k-i];k-=i)
[arr[k],arr[k-i]]=[arr[k-i],arr[k]];
}
複製代碼
原理戳這,是利用歸併的思想實現的排序方法,該算法採用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題而後遞歸求解,而治(conquer)的階段則將分的階段獲得的各答案"修補"在一塊兒,即分而治之)。
function mergeSort(arr, low, high, temp) {
if (low < high) {
var mid = parseInt((low+high)/2);
mergeSort(arr, low, mid, temp);
mergeSort(arr, mid+1, high, temp);
merge(arr, low, high, temp);
}
}
function merge(arr, low, high, temp) {
var mid = parseInt((low+high)/2),
i = low,
j = mid+1,
k = 0;
while (i<=mid&&j<=high) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
while(i<=mid) {
temp[k++] = a[i++];
}
while(j<=high) {
temp[k++] = a[j++]
}
for (var i=0;i<k;i++) {
a[low+i] = temp[i];
}
}
複製代碼
簡單點來講,就是以一個數爲基準(一般是最左邊的數),把這個序列小於這個數的數放在這個數的左邊,若是大於這個數,則放在右邊。平均性能O(nlogn),最壞性能是O(n2)至關於插入排序,在正序和逆序的時候出現,遞歸劃分爲有一邊爲0個
function quickSort(arr, low, high) {
var i = low,
j = high,
temp = arr[i];
if(i>=j) return;
while(i<j) {
while(i<j&&arr[j]>=temp)
j--;
if(i<j)
arr[i]=arr[j];
while(i<j&&arr[i]<=temp)
i++;
if(i<j)
arr[j]=arr[i];
}
arr[i]=temp;
quickSort(arr, low,i-1);
quickSort(arr, i+1, high);
}複製代碼