1 JavaScript基礎數(shù)據(jù)類型JavaScript數(shù)據(jù)類型包括原始類型和引用類型,原始類型有五個:Number(數(shù)值) String(字符串) Boolean(布爾
JavaScript數(shù)據(jù)類型包括原始類型和引用類型,原始類型有五個:
Number(數(shù)值) String(字符串) Boolean(布爾) Null(空) Undefined(未定義)
引用類型有一個:
Object(對象)
通過typeof(x)可以返回一個變量x的數(shù)據(jù)類型“number”、“string”、“boolean”、“undefined”、”object”,這里要注意一點:typeof運算符對于null類型返回的是object。
^ 《JavaScript高級程序設計》:
這實際上是JavaScript最初實現(xiàn)中的一個錯誤,后來被ECMAScript沿用了?,F(xiàn)在null被認為是對象的占位符,從而解釋了這一矛盾。但是從技術上來說,它仍然是原始值。
當執(zhí)行一段JavaScript代碼(全局代碼或函數(shù))時,JavaScript引擎會創(chuàng)建為其創(chuàng)建一個作用域又稱為執(zhí)行上下文(Execution Context),在頁面加載后會首先創(chuàng)建一個全局的作用域,然后每執(zhí)行一個函數(shù),會建立一個對應的作用域,從而形成了一條作用域鏈。每個作用域都有一條 對應的作用域鏈,鏈頭是全局作用域,鏈尾是當前函數(shù)作用域。
作用域鏈的作用是用于解析標識符,當函數(shù)被創(chuàng)建時(不是執(zhí)行),會將this、arguments、命名參數(shù)和該函數(shù)中的所有局部變量添加到該當前作用域 中,當JavaScript需要查找變量X的時候(這個過程稱為變量解析),它首先會從作用域鏈中的鏈尾也就是當前作用域進行查找是否有X屬性,如果沒有 找到就順著作用域鏈繼續(xù)查找,直到查找到鏈頭,也就是全局作用域鏈,仍未找到該變量的話,就認為這段代碼的作用域鏈上不存在x變量,并拋出一個引用錯誤 (ReferenceError)的異常。
JavaScript中的每個對象都有一個prototype屬性,我們稱之為原型,而原型的值也是一個對象,因此它也有自己的原型,這樣就串聯(lián)起來了一條原型鏈,原型鏈的鏈頭是object,它的prototype比較特殊,值為null。
原型鏈的作用是用于對象繼承,函數(shù)A的原型屬性(prototype property)是一個對象,當這個函數(shù)被用作構造函數(shù)來創(chuàng)建實例時,該函數(shù)的原型屬性將被作為原型賦值給所有對象實例,比如我們新建一個數(shù)組,數(shù)組的方法便從數(shù)組的原型上繼承而來。
當訪問對象的一個屬性時, 首先查找對象本身, 找到則返回; 若未找到, 則繼續(xù)查找其原型對象的屬性(如果還找不到實際上還會沿著原型鏈向上查找, 直至到根). 只要沒有被覆蓋的話, 對象原型的屬性就能在所有的實例中找到,若整個原型鏈未找到則返回undefined;
《JavaScript權威指南》中是這樣解釋的:JavaScript變量在聲明之前已經(jīng)可用,JavaScript的這個特性被非正式的稱為聲明提前(hoisting),即JavaScript函數(shù)中聲明的所有變量(但不涉及賦值)都被“提前”至函數(shù)的頂部。
從一個例子來看:
var scope = "global";
function myFunc(){
console.log(scope);
var scope = "local";
}
控制臺打印出來的不是“global”而是“undefined”,這是因為在myFunc這個函數(shù)的作用域中,局部變量scope聲明被提前至函數(shù)頂部,而此時,scope僅聲明,未賦值,因此輸出undefined。實際上,上面的代碼和下面的效果是一樣的:
var scope = "global";
function myFunc(){
var scope;
console.log(scope);
scope = "local";
}
關于閉包具體的定義文獻中給的概念很抽象,我認為閉包是一種使函數(shù)能夠都去其它函數(shù)的局部變量的語法機制。
舉個例子:
function outFunc(){
var name = "Vicfeel";
function inFunc(){
console.log(name);
}
return inFunc;
}
inFunc(); //控制臺顯示"Vicfeel"
這這個例子我們可以看出,在函數(shù)inFunc中依然可以訪問outFunc的局部變量name。
閉包應用舉例,模擬類的私有屬性,利用閉包的性質(zhì),局部變量只有在sayAge方法中才可以訪問,而name在外部也訪問,從而實現(xiàn)了類的私有屬性。
function User(){
this.name = "Vicfeel"; //共有屬性
var age = 23; //私有屬性
this.sayAge:function(){
console.log("my age is " + age);
}
}
var user = new User();
console.log(user.name); //"Vicfeel"
console.log(user.age); //"undefined"
user.sayAge(); //"my age is 23"
function User(){
this.name = "Vicfeel";
this.age = 23;
}
var user = new User();
通過new操作符,實際上在構造函數(shù)User中完成了如下操作:
創(chuàng)建一個新的對象,這個對象的類型是object;
設置這個新的對象的內(nèi)部、可訪問性和prototype屬性為構造函數(shù)(指prototype.construtor所指向的構造函數(shù))中設置的;
執(zhí)行構造函數(shù);
返回新創(chuàng)建的對象。
function User(){
//this = {};
//this.constructor = User;
this.name = "Vicfeel";
this.age = 23;
//return this;
}
var user = new User();
如果構造函數(shù)默認返回的新創(chuàng)建的this對象,如果手動return 一個變量的話,如果該變量是原始類型則無效,如果是對象,則返回該對象。
當我們需要對很多元素添加事件的時候,可以通過將事件添加到它們的父節(jié)點而將事件委托給父節(jié)點來觸發(fā)處理函數(shù)。
比如我們需要向一個ul中動態(tài)添加很多個li,需要遍歷li逐個添加點擊事件
<ul id='list'></ul>
var count = 100;
var ulList = document.getElementById("list");
//動態(tài)構建節(jié)點
for(var i = count;i--;){
var liDom = document.createElement('li');
ulList.appendChild(liDom);
}
//綁定點擊事件
var liNode = ulList.getElementByTagName("li");
for(var i=0, l = liNodes.length; i < l; i++){
liNode[i].onClick = function(){
//li點擊事件
}
}
眾 所周知,DOM操作是十分消耗性能的。所以重復的事件綁定簡直是性能殺手。而事件代理的核心思想,就是通過盡量少的綁定,去監(jiān)聽盡量多的事件。如何做呢? 答案是利用事件冒泡機制,對其父節(jié)點ul進行事件綁定(Event Bubble),然后通過event.target來判斷是哪個節(jié)點觸發(fā)的事件,從而減少很多EventHandler的綁定。
var count = 100;
var ulList = document.getElementById("list");
//動態(tài)構建節(jié)點
for(var i = count;i--;){
var liDom = document.createElement('li');
ulList.appendChild(liDom);
}
//綁定點擊事件
var liNode = ulList.getElementByTagName("li");
liNode.onClick = function(e){
if(e.target && e.target.nodeName.toUpperCase == "LI") {
// li點擊事件
}
}