JS的幾條基本規範:
1、不要在同一行聲明多個變量
2、請使用===/!==來比較true/false或者數值
3、使用對象字面量替代new Array這種形式
4、不要使用全局變量
5、Switch語句必須帶有default分支
6、函數不應該有時候有返回值,有時候沒有返回值
7、For循環必須使用大括號
8、IF語句必須使用大括號
9、for-in循環中的變量應該使用var關鍵字明確限定作用域,從而避免作用域污染。
JS引用方法:
// 行內引入
<body>
<input type="button" onclick="alert('行內引入')" value="按鈕"/>
<button onclick="alert(123)">點擊我</button>
</body>
// 內部引入
<script>
window.onload = function() {
alert("js內部引入!")
}
</script>
// 外部引入
<body>
<div></div>
<script type="text/javascript" src="./js/index.js"></script>
</body>
注意:
1、不推薦寫行內或者HTML中插入<script>,因爲瀏覽器解析順序的緣故,如果解析到死循環之類的JS代碼,會卡住頁面。
2、建議在onload事件之後,即等HTML、CSS渲染完畢再執行代碼
JS的基本數據類型
Undefined、Null、Boolean、Number、String
新增:Symbol
數組操作
在JavaScript中,用的較多的之一無疑是數組操作,這裏過一遍數組的一些用法:
map:遍歷數組,返回回調返回值組成的新數組
forEach:無法break,可以用try/catch中throw new Error來停止
filter:過濾
some:有一項返回true,則整體爲true
every:有一項返回false,則整體爲false
join:通過指定連接符生成字符串
push/pop:末尾推入和彈出,改變原數組,返回推入/彈出項 【有誤】
unshift/shift:頭部推入和彈出,改變原數組,返回操作項 【有誤】
sort(fn)/reverse:排序與反轉,改變原數組
concat:連接數組,不影響原數組,淺拷貝
slice(start,end):返回截斷後的新數組,不改變原數組
splice(start,number,value...):返回刪除元素組成的數組,value爲插入項,改變原數組
indexof/lastIndexOf(value,fromIndex):查找數組項,返回對應的下標
reduce/reduceRight(fn(prev,cur),defaultPrev):兩兩執行,prev爲上次化簡函數的return值,cur爲當前值(從第二項開始)
JS有哪些內置對象
Object是JavaScript中所有對象的父對象
數據封裝對象:Object、Array、Boolean、Number和String
其他對象:Function、Arguments、Math、Date、RegExp、Error
get請求傳參長度的誤區
誤區:我們經常說get請求參數的大小存在限制,而post請求的參數大小是無限制的
實際上HTTP協議從未規定GET/POST的請求長度限制是多少。對get請求參數的限制是來源與瀏覽器或web服務器,瀏覽器或web服務器限制了url的長度。爲了明確這個概念,我們必須再次強調下面幾點:
1、HTTP協議未規定GET和POST的長度限制
2、GET的最大長度顯示是因爲瀏覽器和web服務器限制了URL的長度
3、不同的瀏覽器和WEB服務器,限制的最大長度是不一樣的
4、要支持IE,則最大長度爲2083byte,若只支持Chrome,則最大長度8182byte
補充get和post請求在緩存方面的區別
get請求類似於查找的過程,用戶獲取數據,可以不用每次都與數據庫連接,所以可以使用緩存
post不同,post做的一般是修改和刪除的工作,所以必須與數據庫交互,所以不能使用緩存。因此get請求適合於請求緩存。
閉包
什麼是閉包?
函數A裏面包含了函數B,而函數B裏面使用了函數A的變量,那麼函數B被稱爲閉包。
又或者:閉包就是能夠讀取其他函數內部變量的函數
function A() {
var a=1;
function B() {
console.log(a);
}
return B();
}
閉包的特徵
函數內再嵌套函數
內部函數可以引用外層的參數和變量
參數和變量不會被垃圾回收制回收
對閉包的理解
使用閉包主要是爲了設計私有的方法和變量。閉包的優點是可以避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易造成內存泄露。在js中,函數即閉包,只有函數纔會產生作用域的概念
閉包的最大用處有兩個,一個是可以讀取函數內部的變量,另一個就是讓這些變量始終保持在內存中
閉包的另一個用處,是封裝對象的私有屬性和私有方法
閉包的好處
能夠實現封裝和緩存等
閉包的壞處
就是消耗內存、不正當使用會造成內存溢出的問題
使用閉包的注意點
由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露
解決方法:在退出函數之前,將不使用的局部變量全部刪除
//閉包的經典問題
for(var i=0;i<3; i++) {
setTimeout(function() {
console.log(i);
},1000);
}
這段代碼輸出答案:3個3
解析:首先,for循環是同步代碼,先執行三遍for,i變成了3;然後再執行異步代碼setTimeout,這時候輸出的i,只能是3個3了
有什麼辦法依次輸出0,1,2
第一種方法
// 使用let
for(let i=0; i<3; i++) {
setTimeOut(function () {
cosole.log(i)
},1000);
}
在這裏,每個let和代碼塊結合起來形成塊級作用域,當setTimeout()打印時,會尋找最近的塊級作用域的i,所以依次打印出0,1,2
如果這樣不明白,我們可以執行下邊這段代碼
for(let i=0;i<3;i++) {
console.log("定時器外部:"+i);
setTimeout(function() {
console.log(i);
},1000);
}
此時瀏覽器依次輸出的是:
定時器外部:0
定時器外部:1
定時器外部:2
即代碼還是先執行for循環,但是當for結束執行到了setTimeout的時候,它會做個標記,這樣到了console.log(i)中,i就能找到這個塊中最近的變量定義
第二種方法
// 使用立即執行函數解決閉包問題
for(let i=0;i<3;i++) {
(function(i) {
setTimeout(function() {
console.log(i)
},1000);
})(i)
}
JS作用域及作用域鏈
作用域
在JavaScript中,作用域分爲全局作用域和函數作用域
全局作用域:
代碼在程序的任何地方都能被訪問,window對象的內置屬性都擁有全局作用域
函數作用域:
在固定的代碼片段才能被訪問
例子:
作用域有上下級關係,上下級關係的確定就看函數是在哪個作用域下創建的。如上,fn作用域下創建了bar函數,那麼“fn作用域”就是“bar作用域”的上級。
作用域最大的用處就是隔離變量,不同作用域下同名變量不會有衝突。
變量取值:到創建這個變量的函數的作用域中取值
作用域鏈
一般情況下,變量取值到創建這個變量的函數的作用域中取值。
但是如果在當前作用域中沒有查到值,就會向上級作用域去查,直到查到全局作用域,這麼一個查找過程形成的鏈條就叫做作用域鏈。
原型和原型鏈
原型和原型鏈的概念
每個對象都會在其內部初始化一個屬性,就是prototype(原型),當我們訪問一個對象的屬性時,如果這個對象內部不存在這個屬性,那麼他就會去prototype裏找這個屬性,這個prototype又會有自己的prototype,於是就這樣一直找下去
原型和原型鏈的關係
instance.constructor.prototype = instance._proto_
原型和原型鏈的特點
JavaScript對象是通過引用來傳遞的,我們創建的每個新對象實體中並沒有一份屬於自己的原型副本。當我們修改原型時,與之相關的對象也會繼承這一改變
當我們需要一個屬性的時候,JavaScript引擎會先看當前對象中是否有這個屬性,如果沒有的話就會查找他的Prototype對象是否有這個屬性,如此遞推下去,一直檢索到Object內建對象
組件化和模塊化
組件化
爲什麼要組件化開發
有時候頁面代碼量太大,邏輯太多或者同一個功能組件在許多頁面均有使用,維護起來相當複雜,這個時候,就需要組件化開發來進行功能拆分、組件封裝,以達到組件通用性,增強代碼可讀性,維護成本也能大大降低
組件化開發的優點
很大程度上降低系統各個功能的耦合性,並且提高了功能內部的聚合性。這對前端工程化及降低代碼的維護來說,是有很大的好處的,耦合性的降低,提高了系統的伸展性,降低了開發的複雜度,提升開發效率,降低開發成本
組件化開發的原則
專一
可配置性
標準型
複用性
可維護性
模塊化
爲什麼要模塊化
早期的JavaScript版本沒有塊級作用域、,沒有類、沒有包、也沒有模塊,這樣會帶來一些問題,如複用、依賴、衝突、代碼組織混亂等,隨着前端的膨脹,模塊化顯得非常迫切
模塊化的好處
避免變量污染,命名衝突
提高代碼複用率
提高了可維護性
方便依賴關係管理
模塊化的幾種方法
-
函數封裝
var myModule = {
var1:1,
var2:2,
fn1:function() {
},
fn2:function() {
}
}
總結:這樣避免了變量污染,只要保證模塊名唯一即可,同時同一模塊內的成員也有了關係
缺陷:外部可以隨意修改內部成員,這樣就會產生意外的安全問題
- 立即執行函數表達式(IIFE)
var myModule = (function() {
var var1 = 1;
var var2 = 2;
function fn1() {
}
function fn2() {
}
return {
fn1:fn1,
fn2:fn2
};
})();
總結:這樣在模塊外部無法修改我們沒有暴露出來的變量、函數
缺點:功能相對較弱,封裝過程增加了工作量,仍會導致命名空間污染可能、閉包是有成本的
圖片的預加載和懶加載
- 預加載:提前加載圖片,當用戶需要查看時可直接從本地緩存中渲染
- 懶加載:懶加載的主要目的是作爲服務器前端的優化,減少請求數或延遲請求數
兩種技術的本質:兩者的行爲是相反的,一個是提前加載,一個是遲緩甚至不加載。預加載則會增加服務器前端壓力,懶加載對服務器有一定的緩解壓力作用。
mouseover和mouseenter的區別
mouseover:
當鼠標移入元素或其子元素都會觸發事件,所以有一個重複觸發,冒泡的過程。對應的移除事件是mouseout
mouseenter:
當鼠標移除元素本身(不包含元素的子元素)會觸發事件,也就是不會冒泡,對應的移除事件是mouseleave
解決異步回調地獄
promise、generator、async/await
對this對象的理解
this總是指向函數的直接調用者(而非間接調用者)
如果有new關鍵字,this指向new出來的那個對象
在事件中,this指向觸發這個事件的對象,特殊的是,IE中的attachEvent中的this總是指向全局對象Window