前言:js由三部分組成,1. 核心(ECMAScript),語法標準 2.文檔對象模型(DOM) 3.瀏覽器對象模型(BOM)
JavaScript 的核心語言特性在ECMA-262中是以名爲ECMAScript的僞語言的形式來定義的。ECMAScript中包含了所有基本的語法,操作符,數據類型以及完成基本的計算任務所必需的對象,但沒有對取得輸入和產生輸出的機製作出規定。
1.在web中引入js
<script></script> 使html與js混合
defer : 延遲到頁面全部解析完後再加載js
type : mime類型, 默認"text/javascript"。可省略
引入外部js:
<script src=""></script> 可引入不用域的js,引入外部js時同時在標籤內嵌入js代碼會被忽略。
注意:不要在js中出現 </script> ,會造成瀏覽器錯誤解析(以爲js已經結束)。
小tips:瀏覽器按照<script>標籤先後順序解析,如果將js代碼放head裏,瀏覽器會先解析js,然後呈現body中的頁面元素(頁面延遲,呈現空白)
2.數據類型
ECMAScript 中規定的5種基本數據類型: Undefined,Null,Boolean,Number,String
1種複雜數據類型: Object 由一組無序的鍵值對組成
typeof操作符: 用於返回檢測的變臉類型 : undefined,object,boolean,number,string,function
typeof(null); //返回"object" 空對象指針(對空對象的引用)
Undefined類型
只有一個值 undefined,變量聲明後未初始化,值就是undefined。(未聲明和聲明後未定義值都是undefined)var name;alert(name);//"undefined"alert(age);//"undefined"
Null類型
同樣只有一個值 null。var name = null;alert(typeof name);//"object" 對空指針的引用小tips:在定義一個變量用來保存對象時,初始化時最後給null,這樣就只需要檢查null值就可以判斷變量是否保存了對象的引用.如:
var person = null;
if(person!=null){
}
alert(null == undefined); //true, undefined值是派生自null值的Boolean類型
Boolean(""); //false, 非空字符串返回true
Boolean(0);//0和NaN 返回 false , 非零(包括無窮大)返回true
Boolean(null);//返回false
Boolean(undefined);//false
使用if語句進行流程控制時,會自動調換用Boolean()函數進行轉換
Number類型
var num1 = 55; //十進制
var num2 = 070; //八進制 0開頭 56
var num3 = 0x1f; // 十六進制 0x開頭 31
- 浮點數值
由於保存浮點數值需要的存儲空間是整數值的兩倍,所以ECMAScript會儘可能將浮點數轉成整數。var floatNum1 = 10.0; //保存的是整數10注意:0.1+0.2 !=0.3 浮點數精度問題(永遠不要測試某個特定的浮點數值)
Number.MAX_VALUE,Number.MIN_VALUE,Number.NEGATIVE_INFINITY,Number.POSTIVE_INFINITYInfinity正無窮, -Infinity負無窮。isFinite()函數來判斷是否在這個範圍內,超出範圍的數值會被自動轉成相應的無窮大值
任何數值除以0返回NaN,不會拋錯(不會影響後面代碼的執行)注意:任何涉及NaN的操作都會返回NaN。NaN與任何值都不相等(包括NaN)alert(NaN == NaN); //falseisNaN();//判斷是否 "不是數值"
alert(isNaN(NaN)); // true
alert(isNaN(10)); //false
alert(isNaN("10")); //false,可轉換成數值
alert(isNaN("blue")); //true,不可轉成數值
alert(isNaN(true)); //false 可轉成數值
Number(null); // 0Number(undefined); //NaNNumber("hehe"); //NaNNumber(""); //0parseInt("0xAF",16); //175parseInt("AF"); //NaN 需指定進制parseInt("10",2); // 2 (二進制)
String 類型
由零或多個 16位 Unicode字符組成的字符序列注意:字符串一旦創建,值就不能改變。如果改變,是先銷燬原來的字符串,然後用新字符串來填充該變量字符串轉換:toString();幾乎每個值都有該方法toString(2); //可指定數值的轉換進制null和undefined沒有 toString()函數可以使用String()函數來轉換:String(null); // "null"String(undefined); //"undefined"
Object類型 (所有對象的基礎)
Obejct屬性和方法Constructor:構造函數(用於創建當前對象的函數)hasOwnProperty(propertyName):檢索當前對象實例中的屬性(不是實例原型)是否存在,參數必須是字符串isPrototypeOf(object):傳入的對象是否是另一個對象的原型propertyIsEnumerable(propertyName):屬性是否能用for-in 遍歷toLocalString():返回對象的字符串表示toString():valueOf():返回對象的字符串,數值或布爾值表示。通常與toString()返回相同
3.操作符
1.位操作符
32位二進制表示整數,第32位爲符號位~ 按位非(NOT)& 按位與(AND)| 按位或(OR)^ 按位異或(XOR)<< 左移,不影響符號位>> 有符號右移,保留符號位,不影響正負<<< 無符號右移,連着符號位一起右移
2.關係操作符
4.語句
1.for-in 精準的迭代語句,可以用來枚舉對象的屬性
for(var propName in window){
document.write(propName);
}
輸出BOM中 window對象的所有屬性
迭代前最好先檢測對象是否爲null或undefined。(ECMAScript5中不執行循環體,以前會報錯)
2.label語句,給代碼添加標籤
3.break和continue語句(break跳出循環,直接執行循環後的代碼。continue跳出當前循環,接着進入下一次循環)
var num =0;
outer:
for(var i=0;i<10;i++){
for(var j=0;j<10;j++){
if(i == 5 && j==5){
continue outer;
}
num++;
}
}
alert(num); //95
4. with語句(不建議使用,可讀性差)
5. switch語句
var num = 15;
switch (true){ //輸出 Between 10 and 20,如果是false就輸出 Less than 0
case num<0:
alert("Less than 0");
break;
case num>=0&&num<=1:
alert("Between 0 and 1");
break;
case num>10&&num<=20:
alert("Between 10 and 20");
break;
default:
alert("More than 20");
}
6.函數
function關鍵字來定義函數,注意:函數無所謂傳進來多少個參數。因爲在內部是用一個數組來表示的,在函數體內可以通過arguments來訪問這個數組。arguments對象只是與數組類似(並不是array的實例)function doAdd(num1,num2){ arguments[1] = 10; //會同步影響 num2的值,這種影響是單向的,反過來修改num2並不會影響arguments中的值 alert(arguments[0]+num2); }
注意:arguments對象長度由傳入的參數個數決定,不由定義函數時聲明的參數個數決定,沒有傳值的參數自動賦值undefined
5.變量,作用域,內存
5.1 基本類型和引用類型
基本類型值指的是簡單的數據段,而引用類型值指那些可能由多個值構成的對象。基本類型是按值訪問,所以可以操作保存在變量中的實際的值。引用類型的值是保存在內存中的對象,js不允許直接 訪問內存中的位置(不能直接操作對象的內存空間),實際操作的是對象的引用。
var person = new Object();
person.name = "張三"; //給引用類型變量動態添加屬性並賦值
alert(person.name);// 張三
//無法給基本類型變量添加屬性,不報錯但是無效。
變量複製: 基本類型
引用類型:var num1 = 5; var num2 = num1;// num1和num2是兩個獨立的值,在進行任何操作時不會相互影響。
var obj1 = new Object(); var obj2 = obj1; obj1.name = "張三"; alert(obj2.name); // 張三 //obj1和obj2指向的是堆內存中的同一個對象
參數傳遞:
ECMAScript中所有函數的參數都是按值傳遞的。在傳遞基本類型值是,被傳遞的值會賦給一個局部變量(命名參數,arguments對象中的一個元素)
注意:傳遞引用類型時,會把這個值在內存中的地址賦值給一個局部變量(對堆內存中對象的引用),所以指向的還是同一個內存空間,因此對參數的修改會影響到函數外。
類型檢測:function setName(obj){ obj.name = "張三";//obj引用指向堆中的person對象,person.name = "張三" obj = new Object();//給obj(參數,局部變量) 指向新的對象(該對象在setName函數中創建,是局部的,函數結束,對象銷燬) obj.name = "李四";//給obj引用的局部變量動態添加name屬性賦值爲"李四" } var person = new Object(); setName(person); alert(person.name); //張三
通常用typeof來檢測基本類型
instanceof來檢測引用類型,根據它的原型鏈來識別。
所有引用類型都是Object的實例,用instanceof檢測所有基本數據類型,都會返回false。基本類型 不是對象。
5.2 執行環境及作用域
說明:執行環境定義了變量或函數有權訪問的其他數據,決定了他們各自的行爲。每個執行環境都有一個與之關聯的變量對象(variable object),環境中定義的所有變量和函數都保存在這個對象中。雖然我們編寫的代碼無法訪問這個對象,但解析器在處理數據是會在後臺使用它。
全局執行環境是最外圍的一個執行環境。在web瀏覽器中,全局執行環境就是window對象,因此所有全局變量和函數都是作爲window對象的屬性和方法
創建的。某個執行環境中的所有代碼執行完後,保存在其中的變量和函數定義也隨之銷燬。
每個函數都有自己的執行環境。當執行流進入一個函數時,函數環境會被推入環境棧。執行完後彈出棧,控制權交給之前的執行環境
作用域鏈(scope chain)。保證對執行環境有權訪問的所有變量和函數的有序訪問
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var temColor = color;
color = anotherColor;
anotherColor = temColor;
}
}
延長作用域鏈:
1.try-catch 中的catch塊
2.with語句
with語句接收的是location對象,因此其變量對象中就包含了location對象的所有屬性和方法,而這個變量對象被添加到了作用域鏈的前端,所以在函數內部可以訪問。function buildUrl(){ var qs = "?debug=true"; with(location){ var url = href + qs; //location.href,url添加到最近的環境(buildUrl函數中) } return url; }
沒有塊級作用域:
js沒有塊級作用域,不像java等語言,if(){},for(){}語句中定義的都是局部變量
js中會將if,for中聲明的變量添加到當前的執行環境中for(var i=0;i<10;i++){ doSomething(i); } alert(i); // 10
變量聲明:
使用var聲明的變量會自動被添加到最接近的環境中。在函數內部,最接近的就是當前函數的局部環境;在with語句中,最接近的環境是函數環境。如果初始化變量時沒有var聲明,則自動添加到全局環境。function(num1,num2){ sum = num1+num2; //sum未聲明,添加到全局環境 return sum; } var result = add(10,20); //30 alert(sum); //30,可訪問到
查詢標識符:同名變量,從最近環境開始搜索,優先取最近環境中的值然後停止搜索。
5.3 垃圾收集
說明:JavaScript具有自動垃圾收集機制,執行環境會負責管理代碼執行過程中使用的內存
局部變量只在函數執行的過程中存在。在這個過程中,會爲局部變量在棧(或堆)內存上分配相應的空間,以便存儲它們的值。在函數執行結束,局部變量就沒有存在的必要,因此可以釋放它們的內存。
標記清除
JavaScript中最常用的垃圾收集方式是標記清除(mark-and-sweep)。當變量進入環境時(如:在函數中聲明一個變量),就將這個變量標記爲"進入環境",當變量離開環境時,則將其標記爲"離開環境"。
垃圾收集器在運行的時候會給存儲在內存中的所有變量加上標記,然後會去除環境中的變量以及被環境中的變量引用的變量的標記。
在此之後再被加上標記的變量將被視爲準備刪除的變量(環境中變量已經無法訪問這些變量了)。最後,垃圾收集器完成內存清除工作,銷燬帶標記的值並回收內存空間。
引用計數
說明:引用技術會跟蹤每個值被引用的次數。當聲明瞭一個變量並將一個引用類型值賦給該變量,則這個值的引用次數就是1。如果該值又被賦給另外一個變量,則該值的引用次數加1。相反,如果對這個值引用的變量取得了另外一個值,則這個值的引用次數減一。當這個值的引用次數變成0的時候,就說明沒法再訪問這個值了,就可以將其佔用的內存回收。當垃圾收集器下次再運行的時候就會釋放這些空間。問題:對象之間循環引用會造成內存無法回收
A,B對象通過各自的屬性相互引用。它們之間的引用次數永遠不會是0。如果這個函數被重複多次調用,就會導致大量內存無法回收。function problem(){ var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.anotherObject = objectA; }
問題:IE中有一部分並不是原生JS對象,BOM和DOM中的對象就是使用C++以COM(Component Object Model,組件對象模型)對象的形式實現的,而COM對象的垃圾收集機制採用的就是引用計數策略。所以,即使IE的JS引擎是使用標記清除策略,但JS訪問的COM對象依然是基於引用計數策略的。這就照成只要在IE中涉及COM對象就會存在內存引用的問題。
var element = document.getElementById("some_element"); var myObject = new Object(); myObject.element = element; element.somObject = myObject;//DOM元素和JS對象相互引用,循環引用照成即使DOM從頁面中移除,它也不會被回收。
可以通過賦值爲null來手動斷開這兩個元素間的引用。如:
myObject.element =null;element.somObject = null;
IE9中把BOM和DOM對象都轉成了JS對象,也可以避免兩種垃圾回收算法並存的問題(消除常見的內存泄漏現象)。性能問題
說明:垃圾收集器是週期性運行的,原IE7策略問題:根據內存分配量運行,達到256個變量,4096個對象(或數組)或者64KB其中任一標準,垃圾收集器就會運行,這就導致如果一個腳本中包含這麼多變量,那麼這個腳本在器生命週期中很可能會一直保有這麼多變量。這樣一來,垃圾收集器就會頻繁運行,導致性能急劇下降。
解決方法:如果回收的內存分配量低於15%,則臨界值加倍。如果到了回收85%的內存分配量,就將臨界值重置回默認值。這樣循環往復,看似簡單,實則極大提升了IE在運行包含大量JS的頁面時的性能。
5.4 內存管理
說明:出於安全考慮,瀏覽器的可用內存數量比較少(防止運行JS的網頁耗盡全部系統內存而導致系統崩潰)。內存限制不僅影響給變量分配內存,同時還會影響調用棧以及在一個線程中能夠同時執行的語句數量。
tips:一旦數據不再有用,最好通過將其值設置爲null來釋放其引用(解除引用dereferencing)。該做法適用大多數全局變量和全局對象屬性。局部變量會在它們離開執行環境時自動被解除引用
function createPerson(name){ var localPerson = new Object(); //localPerson局部變量,不用手工解除 localPerson.name = name; return localPerson; } var globalPerson = createPerson(); //手動解除globalPerson的引用 globalPerson = null;
解除globalPerson的引用,讓值脫離執行環境,以便垃圾收集器下次運行時將其回收
小結:
- 基本類型值在內存中佔據固定大小的空間,被保存在棧內存中
- 從一個變量向另一個變量複製基本類型的值,會創建這個值的一個副本
- 引用類型的值是對象,保存在堆內存中
- 包含引用類型值的變量實際上包含的並不是對象本身,而是一個指向該對象的指針
- 從一個變量向另一個變量複製引用類型的值,複製的其實是指針,因此兩個變量最終都指向同一個對象
- 確定一個值是哪種基本類型使用typeof(),確定值是哪種引用類型使用instanceof()
所有變量都存在於一個執行環境(作用域)中,這個執行環境決定了變量的生命週期,以及哪一部分代碼可以訪問其中的變量。
- 執行環境有全局執行環境和函數執行環境之分;
- 每次進入一個新執行環境,都會創建一個用於搜索變量和函數的作用域鏈;
- 函數的局部環境不僅有權訪問函數作用域中的變量,而且有權訪問其父環境,乃至全句環境中的變量;
- 全局環境只能訪問在全局環境中定義的變量和函數,而不能直接訪問局部環境中的任何數據;
- 變量的執行環境有助於確定應該何時釋放內存
JavaScript是自動進行垃圾回收的,開發人員不必關心內存分配和回收問題。
- 離開作用域的值將被自動標記爲可以回收,因此將在垃圾收集期間被刪除。
- "標記清除"是目前主流的垃圾收集算法,思想就是給當前不使用的值加上標記,然後再回收其內存
- "引用計數"的思想是跟蹤記錄所有值被引用的次數,JS引擎目前都不使用這種算法
- 在代碼中循環引用時,"引用計數"算法會出現問題(內存泄漏)
- 解除變量引用不僅有助於消除循環引用現象,而且對垃圾收集也有好處。爲了確保有效地回收內存,應該及時解除不再使用的全局對象,全局對象屬性及循環引用變量的引用。