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