變數提升和函式提升
在javaScript 中,函式及變數的宣告都將被提升到函式的最頂部。變數可以在使用後宣告,也就是變數可以先使用再宣告。
x = 5; // 變數 x 設定為 5
elem = document.getElementById("demo"); // 查詢元素
elem.innerHTML = x; // 在元素中顯示 x
var x; // 宣告 x
複製程式碼
兩個例項效果一樣
var x; // 宣告 x
x = 5; // 變數 x 設定為 5
elem = document.getElementById("demo"); // 查詢元素
elem.innerHTML = x; // 在元素中顯示 x
複製程式碼
要注意javascript只有未初始化的變數才會提升,初始化的不會提升。如下y輸出undefined。
var x = 5; // 初始化 x
elem = document.getElementById("demo"); // 查詢元素
elem.innerHTML = x + " " + y; // 顯示 x 和 y
var y = 7; // 初始化 y
複製程式碼
注意:JavaScript 嚴格模式(strict mode)不允許使用未宣告的變數。
js中建立函式有多種,其中只有函式宣告式才存在函式提升!如下面f2為函式字面量式,不會發生函式提升:
console.log(f1); // function f1() {} console.log(f2); // undefined function f1() {}var f2 = function() {}複製程式碼
只所以會有以上的列印結果,是由於js中的函式提升導致程式碼實際上是按照以下來執行的:
function f1() {} // 函式提升,整個程式碼塊提升到檔案的最開始
console.log(f1); console.log(f2); var f2 = function() {}複製程式碼
attribute和property的區別
- property是DOM中的屬性,是JavaScript裡的物件;
- attribute是HTML標籤上的特性,它的值只能夠是字串;
如下:
<input id="in_1" value="1" sth="whatever">//這個標籤中新增了一個DOM中不存在的屬性“sth”
<script>
var in1 = document.getElementById('in_1');
console.log(in1);
</script>
複製程式碼
從console的列印結果,可以看到in1含有一個名為“attributes”的屬性,它的型別是NamedNodeMap,同時還有“id”和“value”兩個基本的屬性,但沒有“sth”這個自定義的屬性。
attributes: NamedNodeMap
value: "1"
id: "in_1"
複製程式碼
可以發現,標籤中的三個屬性,只有“id”和“value”會在in1上建立,而“sth”不會被建立。這是由於,每一個DOM物件都會有它預設的基本屬性,而這些屬性就是所謂的“property”而在建立的時候,它只會建立這些基本屬性,我們在TAG標籤中自定義的屬性是不會直接放到DOM中的。
現在試著執行以下語句:
console.log(in1.attibutes.sth); // 'sth="whatever"'
複製程式碼
由此可以看到HTML標籤中定義的屬性和值會儲存該DOM物件的attributes屬性裡面;
這些attribute屬性的JavaScript中的型別是Attr,而不僅僅是儲存屬性名和值這麼簡單;那麼,如果我們更改property和attribute的值會出現什麼效果呢?執行如下語句:
in1.value = 'new value of prop';
console.log(in1.value); // 'new value of prop'
console.log(in1.attributes.value); // 'value="1"'
複製程式碼
此時,頁面中的輸入欄的值變成了“new value of prop”,而propety中的value也變成了新的值,但attributes卻仍然是“1”。從這裡可以推斷,property和attribute的同名屬性的值並不是雙向繫結的。
如果反過來,設定attitudes中的值,效果會怎樣呢?
in1.attributes.value.value = 'new value of attr';
console.log(in1.value); // 'new value of attr'
console.log(in1.attributes.value); // 'new value of attr'
複製程式碼
此時,頁面中的輸入欄得到更新,property中的value也發生了變化。此外,執行下面語句也會得到一樣的結果
in1.attributes.value.nodeValue = 'new value of attr';
複製程式碼
由此,可得出結論:
- property能夠從attribute中得到同步;
- attribute不會同步property上的值;
- attribute和property之間的資料繫結是單向的,attribute->property;
- 更改property和attribute上的任意值,都會將更新反映到HTML頁面中;
document load和document DOMContentLoaded兩個事件的區別
(1)在chrome瀏覽器的開發過程中,我們會看到network皮膚中有這兩個數值,分別對應網 絡請求上的標誌線,這兩個時間數值分別代表什麼?
(2)我們一再強調將css放在頭部,將js檔案放在尾部,這樣有利於優化頁面的效能,為什麼這種方式能夠優化效能?
(3)在用jquery的時候,我們一般都會將函式呼叫寫在ready方法內,這是什麼原理?
DOMContentLoaded顧名思義,就是dom內容載入完畢。那什麼是dom內容載入完畢呢?我們從開啟一個網頁說起。當輸入一個URL,頁面的展示首先是空白的,然後過一會,頁面會展示出內容,但是頁面的有些資源比如說圖片資源還無法看到,此時頁面是可以正常的互動,過一段時間後,圖片才完成顯示在頁面。從頁面空白到展示出頁面內容,會觸發DOMContentLoaded事件。而這段時間就是HTML文件被載入和解析完成。即覽器解析完文件便能觸發 DOMContentLoaded 事件,不需要等待圖片等其他資源載入完成。
而對於load事件來說,頁面上所有的資源(圖片,音訊,視訊等)被載入以後才會觸發load事件,簡單來說,頁面的load事件會在DOMContentLoaded被觸發之後才觸發。
我們在 jQuery 中經常使用的 $(document).ready(function() { // ...程式碼... }); 其實監聽的就是 DOMContentLoaded 事件,而$(document).load(function() { // ...程式碼... }); 監聽的是 load 事件。在用jquery的時候,我們一般都會將函式呼叫寫在ready方法內,就是頁面被解析後,我們就可以訪問整個頁面的所有dom元素,可以縮短頁面的可互動時間,提高整個頁面的體驗。
onload事件所有的瀏覽器都支援,所以在使用時我們不需要什麼相容,而DOMContentLoaded不同的瀏覽器對其支援不同,所以在實現的時候我們需要做不同瀏覽器的相容。IE6、IE7不支援DOMContentLoaded,但它支援onreadystatechange事件,該事件的目的是提供與文件或元素的載入狀態有關的資訊。可用該事件代替事件。
- === 和 ==
在javascript中“==”與“===”是不相同的,一個是判斷值是否相等,一個是判斷值及型別是否完全相等。
下面的規則用於判定===運算子比較的兩個值是否相等的判斷條件
•如果兩個值的型別不同,它們就不相同。
•如果兩個值是數字,而且值相同,那麼除非其中一個或兩個都是NaN(這種情況它們不是等同的),否則它們是等同的。值NaN永遠不會與其他任何值等同,包括它自身(奇怪的傢伙),要檢測一個值是否是NaN,可以使用全域性函式isNaN()。
•如果兩個值都是字串,而且在串中同一位置上的字元完全相同,那麼它們就完全等同。如果字串的長度或內容不同,它們就不是等同的。
•如果兩個值引用的是同一個物件、陣列或函式,那麼它們完全等同。如果它們引用的是不同的物件(陣列或函式),它們就不完全等同,即使這兩個物件具有完全相同的屬性,或兩個陣列具有完全相同的元素。
•如果兩個值都是布林型true,或者兩個值都是布林型false,那麼它們等同。
•如果兩個值都是null或都是undefined,它們完全相同。
下面的規則用於判定==運算子比較的兩個值是否相等的判斷條件•
如果兩個值具有相同的型別,那麼就檢測它們的等同性。如果這兩個值完全相同,它們就相等。如果它們不完全相同,則它們不相等。
•如果兩個值的型別不同,它們仍然可能相等。用下面的規則和型別轉換來檢測它們的相等性 ◦如果一個值是null,另一個值是undefined,它們相等。
◦如果一個值是數字,另一個值是字串,把字串轉換為數字,再用轉換後的值進行比較。
◦如果一個值為true,將它轉化為1,再進行比較。如果一個值為false,把它轉化為0,再進行比較。
◦如果一個值是物件,另一個值是數字或字串,將物件轉換成原始型別的值,再埋比較。可以使用物件的toString()方法或valueOf()方法把物件轉化成原始型別的值。JavaScript核心語言的內部類通常先嚐試valueOf()方法轉換,再嘗試toString()方法轉換,但是對於Date類,則先執行toString()方法再執行valueOf()方法轉換。不屬於JavaScript核心語言的物件則可以採用JavaScript實現定義的方式把自身轉換成原始數值。
◦其他的數值組合是不相等的。
如果上面說的都懂了,那麼下面的程式碼就自然理解了
console.log([]===[])
console.log(undefined===undefined)
console.log([]==[])undefined == undefined複製程式碼
- 關於typeof
typeof的返回值共有五種:number, boolean, string, undefined, object, function.
- number
typeof(10);
typeof(NaN);
//NaN在JavaScript中代表的是特殊非數字值,它本身是一個數字型別。
typeof(Infinity);//代表無窮大
複製程式碼
2.boolean
typeof(true);
typeof(false);
複製程式碼
3.string
typeof("abc");
複製程式碼
4.undefined
typeof(undefined);
typeof(a);//不存在的變數
複製程式碼
5.object
物件,陣列,null返回object
typeof(null);
typeof(window);
複製程式碼
6.function
typeof(Array);
typeof(Date);
複製程式碼
- use strict 嚴格模式
設立"嚴格模式"的目的,主要有以下幾個:
1. 消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
2. 消除程式碼執行的一些不安全之處,保證程式碼執行的安全;
3. 提高編譯器效率,增加執行速度;
4. 為未來新版本的Javascript做好鋪墊。
注:IE6,7,8,9 均不支援嚴格模式。
缺點:
現在網站的 JS 都會進行壓縮,一些檔案用了嚴格模式,而另一些沒有。這時這些本來是嚴格模式的檔案,被 merge 後,這個串就到了檔案的中間,不僅沒有指示嚴格模式,反而在壓縮後浪費了位元組。
- javascript函式作用域
在javascript中有兩種變數:
1.全域性變數:宣告在函式外部的變數(所有沒有var直接賦值的變數都屬於全域性變數)
2.區域性變數:宣告在函式內部的變數(所有沒有var直接賦值的變數都屬於全域性變數),當區域性變數與全域性變數重名時,區域性變數會覆蓋全域性變數
var num = 1; //宣告一個全域性變數
function func() {
var num = 2; //宣告一個區域性變數
return num;
}
console.log(func()); //輸出:2 複製程式碼
在JavaScript中變數的作用域,並非和C、Java等程式語言似得,在變數宣告的程式碼段之外是不可見的,我們通常稱為塊級作用域,然而在JavaScript中使用的是函式作用域(變數在宣告它們的函式體以及這個函式體巢狀的任意函式體都是有定義的)。(如下面的例子)
function func() {
console.log(num); //輸出:undefined,而非報錯,因為變數num在整個函式體內都是有定義的
var num = 1; //宣告num 在整個函式體func內都有定義
console.log(num); //輸出:1
}
func();
複製程式碼
當宣告一個全域性變數的時候,實際上是定義了全域性物件window的一個屬性。
var num = 1; //宣告全變數num
alert(window.num) //輸出:1 宣告的全域性變數實際上就是宣告瞭一個window物件的屬性複製程式碼
- 在javascript中實現函式過載和多型
1同名函式的呼叫問題
在js中如果存在多個名稱相同的函式,則呼叫實際每次都只使用最後一個,js其實是沒有過載的,也就是說,如果定義了多個同名的函式,單引數不一樣,在呼叫時,js不管引數個數,只管前後順序
function test1(arg1) { alert("引數1:"+arg1); } function test1(arg1,arg2,arg3) { alert("引數1:"+arg1+"引數2:"+arg2+"引數3:"+arg3); } //測試程式碼 function test(){ test1("1") } 複製程式碼
雖然我們呼叫的是test1("1"),傳遞了一個引數,但實際呼叫的卻是test1(arg1,arg2,arg3),並沒有因為我們傳遞了一個引數,而呼叫只有一個引數的方法。
2函式中特殊的引數arguments
如下程式碼:
function test1(arg1,arg2,arg3) { alert("引數1:"+arg1+"引數2:"+arg2+"引數3:"+arg3); } function test1(arg1) { alert("引數1:"+arg1); } //測試程式碼 function test(){ test1("1","2") } 複製程式碼
上面程式碼呼叫的始終是test1(arg1),也就是隻有一個引數的函式,那麼如何獲取傳遞的其他引數呢?這就要用到函式中特殊的引數arguments,arguments包含了所有傳遞給函式的引數
function test1() { var text=""; for(var i=0;i<arguments.length;i++){ text+="引數"+i+":"+arguments[i]; } alert(text); } //測試程式碼 function test(){ test1("1"); test1("1","2"); test1("1","2","3"); } 複製程式碼
經過測試發現,arguments是一個陣列,包含了傳遞給函式的所有引數,並且arguments.length根據實際傳遞引數的個數的不同而不同,arguments.length代表了實際傳遞給函式引數的個數。那麼如何實現函式的過載也就很簡單了
function test1() { var text=""; if(arguments.length==1) { //呼叫一個引數的方法 } else if(arguments.length==2) { //呼叫兩個引數的方法 } else { //其他的方法 } } 複製程式碼
從某種意義上來說,多型是物件導向中重要的一部分,也是實施繼承的主要目的。個例項可以擁有多個型別,它既可以是這種型別,也可以是那種型別,這種多種狀態被稱為類的多型。
下面程式碼使用js的原型來設計類的多型特徵。
function A(){ this.get = function(){ console.log('A'); } } function B(){ this.get = function(){ console.log('B'); } } B.prototype = new A(); // 使用原型繼承,讓B類繼承A類 function C(){ this.get = function(){ console.log('C'); } } C.prototype = new A(); // 使用原型繼承,讓B類繼承A類 function F(x){ this.x = x; } F.prototype.get = function(){ // 判斷是否為A類的例項 if(this.x instanceof A){ // 如果是,呼叫例項的方法 this.x.get(); } } // 下面開始 var b = new B(); var c = new C(); var f1 = new F(b); // 此時F中的this.x 就是b了, 而b是A的一個例項 var f2 = new F(c); // 原理同上 f1.get(); // B f2.get(); // C 複製程式碼
上面的類F就包含了一個多型方法get() ,它能夠根據不同例項,來執行不同方法。
- 常用陣列api
1、將陣列轉化為字串:2種:
1、var str=String(str);
將陣列轉化為字串並分隔每個元素
2,、var str=arr.join("自定義分隔符");
將陣列轉化為字串,可定義分隔符
強調:如果join省略"",就等效於String
2、連結和獲取子陣列:
1、連線: var newArr=arr1.concat(值1,值2,arr2,...);
將concat後的內容,和arr1拼接,組成新陣列返回
強調:concat的引數中包含陣列,則打散陣列,以單個元素拼接
2、獲取子陣列:var subArr=arr.slice(starti,endi+1);
獲得arr中starti位置開始,到endi位置的所有元素組成的新陣列
強調:含頭不含尾
省略第二個引數:表示從starti一直取到結尾
可支援負數引數:-n表示倒數第n個元素,相當於length-n
3、刪除,插入,替換:(直接修改原陣列)
1、刪除:var deletes=arr.splice(starti,n);
刪除arr中starti位置開始的n個元素
返回被刪除的元素組成的臨時新陣列
2、插入:arr.splice(starti,0,值1,值2,...);
在arr中starti位置,插入新值。舊值被向後順移
強調:要插入的值,只能以單獨的引數傳入,不支援打散陣列引數
3、替換:var deletes=arr.splice(starti,n,值1,值2,...);
刪除arr中starti位置開始的n個元素,再在starti位置插入新元素
刪除的元素個數和插入的新元素個數不必相等
4、反轉陣列元素:arr.reverse();
5、升序排列:arr.sort(); (直接修改原陣列)
特點:將arr中的元素,以字串方式升序
6、結尾出入棧:
1、入棧:arr.push(值)
將值壓入陣列結尾
2、出棧:var last=arr.pop();
彈出陣列最後一個元素
優點:每次出入棧,不影響其餘元素的位置
7、開頭出入棧:
1、入棧:arr.unshift(值);
將值插入陣列開頭
2、出棧:var first=arr.shift();
取出陣列第一個元素
缺點:每次出入棧,其餘元素的位置都要順移1
- 字串api
1.三個字元方法
兩個用於訪問字串中特定字元的方法是:charAt()和charCodeAt()。這兩個方法都接收一個引數,即基於0的字元位置。
兩個方法的區別:charAt()返回給定位置的那個字元,charCodeAt()返回給定位置的字元編碼。
第三個訪問字元的方法,方括號加數字索引。但是IE7及更早版本不支援。
var stringValue = "hello world";
console.log(stringValue.charAt(1));//e
console.log(stringValue.charCodeAt(1));//101
console.log(stringValue[1]);//e
複製程式碼
2 .4個字串操作方法
1)concat():專門用來拼接字串的方法,實踐中使用更多的是加號操作符(+)。該方法可以接受任意多個引數,返回拼接得到新字串,不改變原值。
var stringValue = "hello";
var result = stringValue.concat(" world","!");
console.log(result);//hello world!
複製程式碼
(2)基於子字串建立新字串的方法:slice()、substr()和substring()。
接受一個或兩個引數:第一個引數指定子字串的開始位置,第二個參數列示字串到哪裡結束。
對原來字串沒有影響。
使用區別一:slice()和substring()的第二個引數指定的是子字串最後一個字元後面的位置(注意是後面一個位置)。而
substr()的第二個引數指定的是返回的字元的個數。
區別二:在傳遞給這些方法的引數是負值時,它們的行為不盡相同。
slice()方法將傳入的負值與字串的長度相加;substr()方法將第一個引數加上字串的長度,而將負的第二個引數轉換為0。而substring()將所有負值引數都轉換為0。
3.字串位置方法
indexOf()和lastIndexOf()與陣列的相似。
注意:1.前者從前往後搜尋,後者從後往前搜尋。2**.第二個引數指定從字串的哪個位置開始搜尋。
4.trim()方法
該方法刪除前置和字尾的所有空格,然後返回結果。不影響原字串。
5.4個字串大小寫轉換方法
toLowerCase()、toLocaleLowerCase()、toUpperCase()、toLocaleUpperCase()
toLowerCase()和toUpperCase()是兩個經典的方法,toLocaleLowerCase()和toLocaleUpperCase()是針對特定地區的。
還有一些正規表示式的方法和不常用的方法,這裡不一一舉例。