JavaScript
(參考了《JavaScript高級程序設計》和部分知乎答案)
作用域
指可訪問變量、對象随橘、函數(shù)的集合还栓。
局部作用域:在函數(shù)內(nèi)聲明,只能在函數(shù)內(nèi)部訪問
全局作用域:在函數(shù)外定義敬锐,所有腳本和函數(shù)均可使用(函數(shù)內(nèi)賦值但未聲明背传,仍為全局變量)
變量生命周期:全局變量在頁面關(guān)閉后銷毀,局部變量在函數(shù)執(zhí)行完畢后銷毀
ps:在HTML中所有數(shù)據(jù)屬于window對象
數(shù)據(jù)類型
原始數(shù)據(jù)類型:棧台夺,占據(jù)空間小径玖,大小固定
String
Number
Boolean
Object
Function
不含值的數(shù)據(jù)類型:
Undefined:不存在的值,或變量被聲明了但未賦值
Null:一個對象被定義了颤介,但是為空值(沒有任何屬性和方法)
引用對象類型:堆梳星,占據(jù)空間大,大小不固定(在棧中儲存了指針滚朵,指向堆中的起始位置)
Object
Date
Array
boolean類型轉(zhuǎn)換
數(shù)據(jù)類型 | true | false |
---|---|---|
Boolean | true | false |
String | 任何非字符串 | ""(空) |
Number | 任何非零數(shù)字值 | 0和NaN |
Object | 任何對象 | Null |
Undefined | n/a(不適用) | underfined |
例子:
var message = "Hello world!";
if (message) {
alert("Value is true");
}
正則表達式
由一個字符串序列形成的搜索模式冤灾。
如:
var str = "Visit W3cSchool!";
var n = str.search(/W3cSchool/i);
//n=6 (不區(qū)分大小寫)
變量提升
函數(shù)聲明和變量聲明會被解釋器自動提升到方法體的最頂部
var x;
但是初始化的變量不會提升
var x = 5;
x = 5
為避免問題的出現(xiàn),通常在作用域開始前聲明變量
注意:函數(shù)聲明也會自動提升始绍,而傳遞給初始化變量的函數(shù)則不會
function fn () {} //提升
var fn = function () {} //不提升
函數(shù)調(diào)用的四種方法
1.方法調(diào)用模式
var myobject = {
value: 0,
inc: function() {
alert(this.value);
}
}
myobject.inc();
//this指向myobject
2.函數(shù)調(diào)用模式
var add = function(a,b) {
alert(this);
return a+b;
}
var sum = add(3,4);
arert sum;
3.構(gòu)造器調(diào)用模式(摒棄)
4.apply調(diào)用
var arr = [10,20];
var sum = add.apply(myobject,arr);
aleat(sum);
原型鏈
當從一個對象那里讀取屬性或調(diào)用方法時瞳购,如果該對象不存在這樣的屬性或方法话侄,就會去自己關(guān)聯(lián)的prototype對象那里尋找亏推,直到找到或追溯過程結(jié)束為止。(即對象的屬性和方法追溯機制)
”讀“屬性會沿著原型鏈搜索年堆,”新增“屬性時則不會去看原型鏈
(obj)
name:'obj' valueOf
__proto__ ——> toString
constructor
...
__proto__ ——> null
閉包
先看兩個典型例子
function foo () {
var local = 1;
function bar () {
local++;
return local;
}
return bar;
}
var func = foo();
func(); //2
func(); //3
bar函數(shù)調(diào)用了外層函數(shù)的local變量吞杭,函數(shù)運行完畢后local仍然保存在內(nèi)存中,所以每次運行函數(shù)后local都會增加1
var add =(function () {
var counter = 0;
return function () {
return counter += 1;
}
})();
add(); //1
add(); //2
add(); //3
和上一個例子的原理是一樣的变丧,內(nèi)層函數(shù)調(diào)用了外層函數(shù)的全局變量count導致其始終存在于內(nèi)存中芽狗,而內(nèi)部函數(shù)的存在也依賴于外層函數(shù),導致其也始終再內(nèi)存中痒蓬,不會在調(diào)用結(jié)束后被垃圾回收機制回收童擎。
這就叫閉包,一句話概括:閉包就是函數(shù)和函數(shù)內(nèi)部能訪問到的變量的總和攻晒。
或者說顾复,如果存在有權(quán)訪問另一個函數(shù)作用域中變量的函數(shù),那就構(gòu)成了閉包鲁捏。
它提供了一中間接的方式能夠訪問到函數(shù)內(nèi)部的數(shù)據(jù)芯砸,有以下兩種情況:
1.函數(shù)作為返回值
2.函數(shù)作為參數(shù)傳遞
正確運用閉包由一個好處,就是讓一些數(shù)據(jù)更安全,只能通過特定的接口來訪問假丧。
當然双揪,有時候閉包也會導致一些問題,如下面的例子:
for (var i=0; i<10; i++) {
arr[i] = function() {
return i;
}
}
arr[0]; //10
arr[6]; //10
function內(nèi)訪問了外部變量i包帚,構(gòu)成了一個閉包渔期,我們先不寫內(nèi)部的i,每次賦值的結(jié)果如下
arr[0] = function() { return i };
arr[1] = function() { return i };
...
arr[10] = function() { return i };
而i的作用域是整個for的花括號內(nèi)婴噩,因此這些函數(shù)返回的是同一個i擎场,遍歷完后產(chǎn)生了這11個結(jié)果,此時我們再調(diào)用函數(shù)的時候几莽,i已經(jīng)為10了迅办,因此無論調(diào)用哪個函數(shù),結(jié)果都是遍歷完后的i章蚣。
this
var obj = {
foo: function () {
console.log(this);
}
}
var bar = obj.foo;
obj.foo(); //obj
bar(); //window
要理解this站欺,首先要知道函數(shù)的三種調(diào)用形式
func(p1,p2);
obj.child.method(p1,p2);
func.call(context,p1,p2); //正常形式
context就是this,
上面的例子中纤垂,bar()等價于func.call(undefined)矾策,這種情況下this為window,
而obj.foo()等價于obj.foo.call(obj)峭沦,this為obj
有一個特例:
function fn() {
console.log(this);
}
var arr = [fn,fn2];
arr[0]; //這里的this指什么
我們把arr[0]假想為arr.0()
也就相當于arr.0.call(arr)
所以this為arr
對某些框架有其他方法來理解贾虽,
如在jQuery,this關(guān)鍵字指向的是當前正在執(zhí)行事件的元素吼鱼。
new操作
1.創(chuàng)建一個空對象蓬豁,并且this變量引用該對象,繼承該對象的原型
var obj = {}
2.屬性和方法被加入到this引用的對象中
obj.__proto__ = Base.prototype;
3.新創(chuàng)建的對象又this所引用菇肃,并且隱式地返回this
Base.call(obj);
在以下例子中
var o = new myObject();
一旦沒有加上new地粪,myObject()內(nèi)部的this將指向全局對象
事件類型
UI事件
1.load事件:頁面完全加載后觸發(fā)
2.unload事件:頁面完全卸載后觸發(fā)
3.resize事件:當瀏覽器窗口被調(diào)整到一個新的高度或?qū)挾葧r觸發(fā)
4.scroll事件:文檔被滾動期間觸發(fā)
焦點事件
1.focusout:在失去焦點的元素上觸發(fā)
2.focusin:在獲得焦點的元素上觸發(fā)
3.blur:在失去焦點的元素上觸發(fā)
4.DOMFocusOut:在失去焦點的元素上觸發(fā)
5.focus:在獲得焦點的元素上觸發(fā)
6.DOMFocusIn:在獲得焦點的元素上觸發(fā)
鼠標與滾輪事件
1.click:單擊鼠標或按下回車鍵時觸發(fā)
2.dbclick:雙擊鼠標按鈕時觸發(fā)
3.mousedown:按下任意鼠標按鈕時觸發(fā)
4.mouseup:用戶釋放鼠標按鈕時觸發(fā)
5.mousewheel:通過鼠標滾輪滾動頁面時觸發(fā)
6.mouseenter:鼠標光標首次從元素外部移動到元素范圍內(nèi)時觸發(fā)(只限于被選元素)
7.mouseover:鼠標指針位于一個元素外部,用戶將其首次移入另一個元素邊界之內(nèi)時觸發(fā)(任何元素琐谤,不限于被選元素)
8.mouseleave:位于元素上方的鼠標光標移動到元素范圍之外時觸發(fā)(只限于被選元素)
9.mouseout:鼠標指針位于一個元素的上方蟆技,移入到另一個元素時觸發(fā)(任何元素,不限于被選元素)
10.mousemove:鼠標指針在元素內(nèi)部移動時重復地觸發(fā)
鍵盤與文本事件
1.keydown:按下鍵盤的任意鍵時觸發(fā)
2.keypress:按下鍵盤的字符鍵時觸發(fā)
3.keyup:釋放鍵盤上的鍵時觸發(fā)
除此之外斗忌,還有
變動事件
H5事件
觸摸與手勢事件
DOM事件處理程序
DOM0級事件處理程序
var btn = document.getElementById("myBtn");
btn.onclick = function () {
alert("clicked");
}
DOM2級事件處理程序
var btn = document.getElementById("myBtn");
btn,addElementListener("click",function(){
alert("clicked");
},false);
//true: 在捕獲階段調(diào)用事件處理程序
//false: 在冒泡階段調(diào)用事件處理程序
跨瀏覽器的事件處理程序(封裝)
var EventUtil = {
addHandler: function(element,type,hander) {
if (element.addEventListener) {
element.addEventListener(type,handler,false);
} else if (element.attachEvent) { //IE
element.attachEvent("on"+type,handler);
} else {
element["on"+type] = handler; //HTML事件
}
}
}
事件委托的原理以及優(yōu)缺點
function delegateEvent (interfaceEle,selector,type,fn) {
if (interfaceEle.addEventListener) {
interfaceEle.addEventListener(type,eventfn);
} else { //IE
interfaceEle.attachEvent("on"+type,eventfn);
}
function eventfn (e) {
var e = e || window.event;
var target = e.target || e.srcElement; //兼容fireFox
if (matchSelector(target,selector)) {
if (fn) {
fn,call(target,e); //將fn內(nèi)部的this指向target
}
}
}
}
var odiv = document.getElementById("iDiv");
delegateEvent(odiv,'click',function(){
alert("clicked");
});
優(yōu)點:
1.大量減少內(nèi)存占用质礼,減少事件注冊
2.新增子對象無需再次對其綁定事件
原生Javascript實現(xiàn)事件代理
<ul id="parentList">
<li id="post-1">1</li>
<li id="post-2">2</li>
</ul>
<script>
function delegateEvent(interfaceEle,type,fn) {
if(interfaceEle.addEventListener) {
interfaceEle.addEventListener(type,fn);
} else { //IE
interfaceEle.attachEvent("on"+type,fn);
}
}
var parentList = document.getElementById("parentList");
delegateEvent(parentList,"click",function(e){
var e = e || window.event; //兼容IE
var target = e.target || e.srcElement; //兼容fireFox
alert(target.id);
});
</script>
這段代碼監(jiān)聽了整個<ul>標簽并且啟用了事件代理,點擊<li>標簽的時候會返回具體的li標簽的id织阳,對新增的對象元素也同樣有效
事件冒泡和事件捕獲
事件冒泡:事件開始時由最具體的元素接收眶蕉,然后逐級向上,傳播到較為不具體的節(jié)點
事件捕獲:不太具體的節(jié)點更早地接收到事件陈哑,而最具體的節(jié)點最后接收到事件
DOM事件流:同時支持兩種事件模型:捕獲型事件和冒泡型事件妻坝,但是伸眶,捕獲型事件先發(fā)生。兩種事件流會觸及DOM中的所有對象刽宪,從document對象開始厘贼,也在document對象結(jié)束。
支持W3C標準的瀏覽器在添加事件時用addEventListener(event,fn,useCapture)方法圣拄,基中第3個參數(shù)useCapture是一個Boolean值嘴秸,用來設置事件是在事件捕獲時執(zhí)行,還是事件冒泡時執(zhí)行庇谆。而不兼容W3C的瀏覽器(IE)用attachEvent()方法岳掐,此方法沒有相關(guān)設置,不過IE的事件模型默認是在事件冒泡時執(zhí)行的饭耳,也就是在useCapture等于false的時候執(zhí)行串述,所以把在處理事件時把useCapture設置為false是比較安全,也實現(xiàn)兼容瀏覽器的效果寞肖。
JSON
json的全稱為JavaScript Object Notation纲酗,即對象表示法,是一種約定的數(shù)據(jù)格式新蟆。
AJAX
ajax的全稱為Asynchronous JavaScript and XML觅赊,即異步的JavaScript和XML,
理解它的過程很簡單琼稻,就是用JS發(fā)起一個請求吮螺,并得到服務器返回的內(nèi)容,原生JS寫法如下:
//第1步 聲明一個對象
var request;
//第2步 根據(jù)瀏覽器賦予對象http請求的方法
if (window.XMLHttpRequest) { //code for IE7+, Firefox, Chrome, Opera, Safari
request = new XMLHttpRequest();
} else { //code for IE6, IE5
request = new ActiveXObject("Microsoft.XMLHTTP");
}
//第3步 確定服務器返回的方法
request.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
console.log(request.responseText);
}
}
//第4步 在之前聲明的基礎上發(fā)起請求
request.open("GET","filename",true);
request.send();
注意以下要點:
IE6及以下版本用的是ActiveXObject方法帕翻,其余用XMLHttpRequest方法鸠补。
XMLHttpRequest對象三個重要的屬性:
onreadystagechange 存儲函數(shù)(或函數(shù)名),每當 readyState 屬性改變時熊咽,就會調(diào)用該函數(shù)莫鸭。
readyState 存有 XMLHttpRequest 的狀態(tài)闹丐。從 0 到 4 發(fā)生變化横殴。
0: 請求未初始化
1: 服務器連接已建立
2: 請求已接收
3: 請求處理中
4: 請求已完成,且響應已就緒
status
200:"ok"
404:未找到頁面
XMLHttpRequest對象的open()和send()方法:
open(method,url,async);
//規(guī)定請求的類型卿拴、URL 以及是否異步處理請求衫仑。
//method:請求的類型;GET 或 POST
//url:文件在服務器上的位置
//async:true(異步)或 false(同步)
send(string);
//將請求發(fā)送到服務器堕花。
//string:僅用于 POST 請求
與 POST 相比文狱,GET 更簡單也更快,并且在大部分情況下都能用缘挽。
然而瞄崇,在以下情況中呻粹,請使用 POST 請求:
1.無法使用緩存文件(更新服務器上的文件或數(shù)據(jù)庫)
2.向服務器發(fā)送大量數(shù)據(jù)(POST 沒有數(shù)據(jù)量限制)
3.發(fā)送包含未知字符的用戶輸入時,POST 比 GET 更穩(wěn)定也更可靠
jQuery封裝了AJAX的方法苏研,只需要一行代碼:
$.get('filename').then(function(response){
//do something
});
原型繼承與擴展
Child.prototype = new Parent();
把父類對象賦值給子類構(gòu)造函數(shù)的原型等浊,這樣子類就可以訪問到父類以及父類的原型,這就叫原型繼承摹蘑。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
alert ("hello, I'm" + this.name);
};
var BillGates = new Person("Bill Gates"); //建立對象
BillGates.sayHello(); //hello, I'm Bill Gates
Person.prototype.Retire = function() {
alert("poor"+this.name+"byebye!");
} //建立對象后再動態(tài)擴展
BillGates.Retire(); //poor BillGates byebye!
這種建立對象后再動態(tài)擴展的情況筹燕,就叫做原型擴展,新方法仍然可被之前建立的對象調(diào)用
js延遲加載
defer和async是動態(tài)創(chuàng)建dom的兩種方式衅鹿,defer是延遲加載撒踪,async是異步下載腳本
在其他情況下,瀏覽器會按照script元素在頁面中出現(xiàn)的順序依次進行解析
封裝與模塊化開發(fā)
使代碼規(guī)范好用大渤,使用簡單化制妄,只需要知道參數(shù)和返回值,可以轉(zhuǎn)化編程思維角度
var modulel = (function(){
var _count = 0;
var m1 = function(){
//...
}
var m2 = function(){
//...
}
return {
m1:m1,
m2:m2
};
})();
跨域問題的解決方法
1.jsonp(jQuery的$.getScript方法就是利用jsonp跨域解決的)
2.iframe
3.window.name
4.window.postMessage
5.服務器上設置代理頁面
DOM操作
(1)創(chuàng)建新節(jié)點
createDocumentFragment() //創(chuàng)建一個DOM片段
createElement() //創(chuàng)建一個具體元素
createTextNode() //創(chuàng)建一個文本節(jié)點
(2)添加泵三、移除忍捡、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有子節(jié)點前插入新的節(jié)點
(3)查找
getElementsByTagName() //通過標簽名稱
getElementsByName() //通過元素Name屬性值
getElementById() //通過元素id切黔,唯一砸脊,所以Element沒有s
前端模塊化開發(fā)
發(fā)展歷程
1.函數(shù)封裝
function f1 () {
statement
}
function f2 () {
statement
}
污染了全局變量,容易發(fā)生沖突纬霞,且模塊成員之間沒有聯(lián)系
2.對象的寫法
var myModule = {
var1: 1,
var2: 2,
fn1: function () { },
fn2: function () { }
}
調(diào)用myModule.fn2(); 避免了變量污染凌埂,只需要保證模塊名唯一即可
缺陷是外部可以隨意修改內(nèi)部成員:
myModule.var1 = 100;
3.立即執(zhí)行函數(shù)的寫法
var myModule = (function(){
var var1 = 1;
var var2 = 2;
function fn1 () { };
function fn2 () { };
return {
fn1: fn1,
fn2: fn2
};
}) ();
console.info(myModule.var1); //underfined
這樣就無法修改暴露出來的變量和函數(shù),就是模塊化的基礎诗芜。
模塊化的規(guī)范 CMD和AMD
commonJS 通用模塊定義
1.定義模塊
每一個模塊都是一個單獨的作用域瞳抓,無法被其他模塊讀取
2.模塊輸出
模塊只有一個出口,module.exports對象伏恐,把模塊希望輸出的全部內(nèi)容放入該對象
3.加載模塊
加載模塊使用require方法孩哑,該方法讀取一個文件并執(zhí)行返回文件內(nèi)部的module.exports對象
例子
//模塊定義myModel.js
var name = 'Byron';
function printName () {
console.log(name);
}
function printFullName(firstName) {
console.log(firstName + name);
}
module.exports = {
printName: printName,
printFullName: printFullName
}
//加載模塊
var nameModule = require(./myModel.js);
nameModule.printName
AMD 異步模塊定義
綜合知識
關(guān)于緩存的三個關(guān)鍵字
cookie:是儲存在瀏覽器上的一小段數(shù)據(jù),用來記錄某些當頁面關(guān)閉或刷新后仍然需要記錄的信息翠桦。
session:是一種讓服務器能夠識別某個用戶的機制横蜒。
localStorage:HTML5本地儲存web storage特性的API之一,用于將大量數(shù)據(jù)保存在瀏覽器中销凑。
前端性能優(yōu)化
1.減少http請求次數(shù)丛晌,JS、CSS源碼壓縮
2.前端模版斗幼,JS+數(shù)據(jù)澎蛛,減少DOM操作次數(shù),優(yōu)化js性能
3.用innerHTML代替DOM操作蜕窿,減少DOM操作次數(shù)谋逻,優(yōu)化js性能
4.設置className呆馁,少用內(nèi)聯(lián)style
5.少用全局變量,緩存DOM節(jié)點查找的結(jié)果
6.圖片壓縮和預加載毁兆,將樣式表放在頂部智哀,腳本放在底部,加上時間戳
7.避免使用table荧恍,顯示比div+css布局慢
頁面加載過程
1.瀏覽器根據(jù)請求的URL交給DNS域名解析瓷叫,找到真實IP,向服務器發(fā)起請求
2.服務器交給后臺處理完成后返回數(shù)據(jù)送巡,瀏覽器接收文件(HTML摹菠、JS、CSS骗爆、圖片等)
3.瀏覽器對加載的資源進行語法解析次氨,建立相應的內(nèi)部數(shù)據(jù)結(jié)構(gòu)
4.載入解析到的資源文件,渲染頁面摘投,完成
瀏覽器渲染過程
1.瀏覽器解析html源碼煮寡,然后創(chuàng)建一個DOM樹,每一個標簽都有一個對應的節(jié)點犀呼,并且每一個文本也有一個對應的文本節(jié)點(DOM樹的根節(jié)點就是documentElement幸撕,對應html標簽)
2.瀏覽器解析CSS代碼,計算最終的樣式數(shù)據(jù)(對CSS中非法的語法會忽略掉外臂,按優(yōu)先級排列:瀏覽器默認設置>用戶設置>內(nèi)聯(lián)樣式>外鏈樣式>html中的style)
3.構(gòu)建渲染樹(忽略不需要渲染的元素)
4.根據(jù)渲染樹把頁面繪制到屏幕上
XHTML
可拓展超文本標記語言坐儿,將HTML作為XML的應用而重新定義的標準
其中script需要寫成以下形式才能被識別
<script>
//<![CDATA[
//內(nèi)容
//]]>
</script>
CSS
css定義的權(quán)重
div:1
.class:10
#
id:100
內(nèi)聯(lián)樣式表:1000
#
id div:100+1=101
.class div:10+1=11
.class1 .class2 div:10+10+1=21
權(quán)重相同時,最后定義的樣式會起作用
盒模型
標準模式:box-sizing:content-box;
怪異模式:box-sizing:border-box;
兩種模式的區(qū)別:
標準模式會被設置的padding撐開宋光,而怪異模式則相當于將盒子的大小固定好貌矿,再將內(nèi)容裝入盒子。盒子的大小并不會被padding所撐開罪佳。
.a a a a