標籤:style blog http color io os 使用 java ar
這裡列出了一些JS重要知識點(不全面,但自己感覺很重要)。徹底理解並掌握這些知識點,對於每個想要深入學習JS的朋友應該都是必須的。
講解還是以範例程式碼搭配注釋的形式,這裡做個小目錄:
JS代碼預解析原理(包括三個段落);
函數相關(包括 函數傳參,帶參數函數的調用方式,閉包);
物件導向(包括 對象建立、原型鏈,資料類型的檢測,繼承)。
JS代碼預解析原理
/****************** JS代碼預解析原理 ******************//*JS代碼預解析、變數範圍、範圍鏈等 應該能作為學習JS語言的入門必備知識。下邊給出些簡要解釋和一些典型的程式碼片段,若要瞭解更多,能從網上搜尋到更多相關樣本。引用網上的一段有關 “JS的執行順序” 的解釋:如果一個文檔流中包含多個script程式碼片段(用script標籤分隔的js代碼或引入的js檔案),它們的運行順序是:步驟1. 讀入第一個程式碼片段(js執行引擎並非一行一行地執行程式,而是一段一段地分析執行的)步驟2. 做文法分析,有錯則報語法錯誤(比如括弧不匹配等),並跳轉到步驟5步驟3. 對var變數和function定義做“預解析”(永遠不會報錯的,因為只解析正確的聲明)步驟4. 執行程式碼片段,有錯則報錯(比如變數未定義)步驟5. 如果還有下一個程式碼片段,則讀入下一個程式碼片段,重複步驟2步驟6. 結束*/// 下邊給出 三段覺得比較典型的程式碼範例:/********** 一:基本的幾條語句 **********/alert(num); // undefinedvar num = 0;alert(str); // 錯誤:str未定義str = "string"; alert(func); // undefinedvar func = function (){ alert(‘exec func‘); }test(); // exec testalert(test()); // 先exec test 後undefinedfunction test(){ alert(‘exec test‘); }/********** 二:函數名與變數名相同 **********/ //var mark = 1;function mark(x) { return x * 2;}var mark;alert(mark); // function mark(x) { return x * 2; }// 去掉前邊的var mark = 1;則會返回1/********** 三:把第二段包括在語句塊中 **********/ // 當有條件時候(程式碼封裝含在條件陳述式塊裡)if (false) { var mark1 = 1; function mark1() { alert("exec mark1"); } //var mark1; alert(mark1); }alert(mark1);mark1();// 由於解析瀏覽器解析不同,這段代碼在不同瀏覽器裡執行的結果不一致,具體原因可從網上尋找答案
函數相關(包括 函數傳參,帶參數函數的調用方式,閉包)
/****************** 函數相關 ******************//********** 一:函數傳參 **********/ /*程式設計語言大概都有 實值型別與參考型別 的區別,JS也不例外。原始類型:undefined null number boolean 均為實值型別。string比較特殊,因為它是不可改變的,String類定義的方法都不能改變字串的內容。function object array 這三種為參考型別。 *//* JavaScript 函數傳遞參數時,是值傳遞。ECMAScript 中,所有函數的參數都是按值來傳遞的。基本類型值的傳遞和基本類型變數複製一致(採用在棧內建立值),參考型別值的傳遞和參考型別變數的複製一致(棧記憶體放的是指標,指向堆中同一對象)。具體參考:http://www.xiaoxiaozi.com/2010/03/05/1719/*/function setName(obj){ //obj拷貝了person的值(person是一個對象的引用地址),所以obj也指向了person所指向的對象。 obj.name = "xiaoxiaozi"; obj = {}; // 讓obj 指向了另一個對象 obj.name = "admin";}var person = {};setName(person);alert(person.name); // xiaoxiaozi/********** 二:帶參數函數的調用方式 **********/ /* 在DOM不同版本中,函數調用方式不太一樣。標準推薦的是addEventListener和attachEvent 這兩種方式有很多資料可查。但是有些已經不被推薦的函數調用仍舊有實際應用,相關資料發現的不多。 這裡主要討論這些函數調用方式*/var g = "全域變數";function show(str) { alert("my site: " + str);}setTimeout("show(g);",100); // g是全域變數,函數正確執行function t() { var url = "www.xujiwei.cn"; var num = 2; //setTimeout("alert("+url+")", 3000); // 解析錯誤,www未定義 //setTimeout("alert("+num+")", 3000); // 解析正確,注意與上句對比 //setTimeout("show(‘url‘);", 2000); // url //setTimeout("show("+ url +");", 2000); // 解析錯誤,www未定義 //setTimeout("show(url);", 2000); // 解析錯誤,url未定義 //setTimeout(‘"show("+ url +");"‘, 2000); // 解析錯誤,url未定義 //setTimeout("show(‘"+ url +"‘);", 2000); // 正確 //setTimeout(function(){show(url);},1000); // 正確}t();/* 結論: 諸如onclick="xx();"等函數調用方式,在雙引號內的內容直接解析為js語句執行。 若調用的函數帶有參數,注意對比以上各種寫法,保證傳遞進去的參數為正確的。*/ /********** 三:閉包 **********//* 閉包,幾乎是每個學習JS的朋友都要討論的問題,因此各種相關資料應有盡有。它的作用很大,但也有弊端,例如如果使用不當,容易引起記憶體流失等問題,因此有不少人提倡少用閉包。這裡列出閉包的一種經典應用,一個有爭議的應用。*/function test1() { //通過閉包,每次能傳入不同的j值。 for (var j = 0; j < 3; j++) { (function (j) { setTimeout(function () { alert(j) }, 3000); })(j); }}test1();/* 這個是閉包的典型應用 */(function tt() { for (var i = 1; i < 4; i++) { document.getElementById("b" + i).attachEvent("onclick", new Function(‘alert("This is button‘ + i + ‘");‘)); // 在IE中測試 }})() // 立即執行函數,一個檔案是否只能有一個?把上邊函數寫成立即執行出問題,怎麼回事?/* 這個問題出現在論壇裡,有很多爭議有說是new Function動態產生個閉包結構的函數,所以能儲存外部變數。有說是跟閉包無關,new Function,就是新定義了一個function,i的值也作為這個新的function的參數固化在其內部了。*/
物件導向(包括 對象建立、原型鏈,資料類型的檢測,繼承)
/****************** 物件導向 ******************//********** 一:對象建立、原型鏈 **********//* 討論 建構函式(類方式)建立對象 ,深入理解這些內容,是很重要的*/function MyFunc() { }; //定義一個空函數var anObj = new MyFunc(); //使用new操作符,藉助MyFun函數,就建立了一個對象// 等價於:function MyFunc() { };var anObj = {}; //建立一個對象anObj.__proto__ = MyFunc.prototype;MyFunc.call(anObj); //將anObj對象作為this指標調用MyFunc函數/*用 var anObject = new aFunction() 形式建立對象的過程實際上可以分為三步:第一步:建立一個新對象(anObject);第二步:將該對象內建的原型對象(__proto__)設定為建構函式prototype引用的那個原型對象;第三步:將該對象作為this參數調用建構函式,完成成員設定等初始化工作。對象建立之後,對象上的任何訪問和操作都只與對象自身及其原型鏈上的那串對象有關,與建構函式再扯不上關係了。換句話說,建構函式只是在建立對象時起到介紹原型對象和初始化對象兩個作用。原型鏈:(參考:http://hi.baidu.com/fegro/blog/item/41ec7ca70cdb98e59152eed0.html) 每個對象(此處對象應該僅指大括弧括起來的object,不包括function、array。待驗證?) 都會在其內部初始化一個屬性,就是__proto__,當我們訪問一個對象的屬性時, 如果這個對象內部不存在這個屬性,那麼他就會去__proto__裡找這個屬性, 這個__proto__又會有自己的__proto__,於是就這樣 一直找下去,也就是我們平時所說的原型鏈的概念。*/ /* 理解了對象建立的原理,可試著分析下邊兩個樣本的結果 */var yx01 = new function() {return "圓心"}; alert(yx01); // [object Object]var yx02 = new function() {return new String("圓心")}; alert(yx02); // “圓心” /* 解釋:"圓心"是基本的字串類型,new String("圓心")則建立了一個string對象。只要new運算式之後的建構函式返回一個引用對象(數組,對象,函數等),都將覆蓋new建立的對象,如果返回一個原始類型(無 return 時其實為 return 原始類型 undefined),那麼就返回 new 建立的對象。參考:http://www.planabc.net/2008/02/20/javascript_new_function/ *//********** 二:資料類型的檢測 **********//* 判斷資料類型可能想到的方法:constructor、typeof、instanceof、Object.prototype.toString.call()*//***** 1、通過constructor屬性 *****/var myvar= new Array("a","b","c","d");function A(){}myvar.constructor = A;var c = myvar.constructor;alert(c); // function A(){}//可見,通過constructor屬性擷取類型的方法很容易被修改,不應該用來判斷類型。/***** 2、通過typeof *****//* typeof是一個操作符,而不是個函數。 typeof的實際應用是用來檢測一個對象是否已經定義或者是否已經賦值。 如if(typeof a!="undefined"){},而不要去使用if(a)因為如果a不存在(未聲明)則會出錯。 typeof檢測物件類型時一般只能返回如下幾個結果: number,boolean,string,function,object,undefined。 對於Array,Null,自訂對象 等使用typeof一律返回object, 這正是typeof的局限性。*/var num = new Number(1);var arr = [1,2,3];alert(typeof num); //object 而不是numberalert(typeof arr); //object 而不是Arrayalert(typeof null); // object/***** 3、通過 instanceof *****//* 用instanceof操作符來判斷對象是否是某個類的執行個體。 如果obj instanceof Class返回true,那麼Class的原型與obj原型鏈上的某個原型是同一個對象, 即obj要麼由Class建立,要麼由Class的子類建立。*/function t(){};t.prototype = Array.prototype;//t.prototype = [];var x = new t();alert(x instanceof t);//彈出truealert(x instanceof Array);//彈出truealert(x instanceof Object);//彈出true/*由此可知,通過 instanceof 判斷資料類型也不可靠。因為一個對象(此處x)的原型鏈可以很長,每個原型的類型可以不同。另外在iframe內也會容易出錯:即有個頁面定義了一個數組a,頁面又嵌套了一個IFrame,在Iframe裡面通過 top.a instanceof Array, 是返回false的。這個說明 父頁面和內嵌iframe裡的對象是不同的,不能混合在一起使用。改成top.a instanceof top.Array 就會返回true*//***** 4、通過 Object.prototype.toString.call() *****//* Object.prototype.toString.call() 作用是: 1、擷取對象的類名(物件類型)。 2、然後將[object、擷取的類名]組合并返回。可應用於判斷Array,Date,Function等類型的對象*/var num = new Number(1);var arr = [1,2,3];alert(Object.prototype.toString.call(num)); // [object Number]alert(Object.prototype.toString.call(arr)); // [object Array]// 擴充樣本:(apply等價於call)window.utils = { toString: Object.prototype.toString, isObject: function (obj) { return this.toString.apply(obj) === ‘[object Object]‘; }, isFunction: function (obj) { return this.toString.apply(obj) === ‘[object Function]‘; }, isArray: function (obj) { return this.toString.apply(obj) === ‘[object Array]‘; }}function A() { }window.utils.isFunction(A); //truewindow.utils.isObject(new A()); //truewindow.utils.isArray([]); //true/* jQuery等架構 就是用這個方法判斷對象的類型的,因此可以把這種方法作為權威的判斷方法。但是,如果重寫了Object.prototype.toString方法,這時候再用來判斷資料類型可能就會出錯,所以,一般不要去重寫Object.prototype.toString方法。*//********** 三:繼承 **********//* JS繼承和閉包一樣,幾乎是每個想深入學習JS的朋友都要討論的問題,因此各種相關資料應有盡有。JS繼承代碼的版本非常多,但原理都是一樣的,核心都是利用了prototype對象。為了和其他物件導向語言的風格相似,大多數都採用“類式”風格類比。繼承的詳細原理不再贅述,網上有許多資料介紹。這裡給出一個樣本:Jquery作者John Resig寫的繼承。(其中的詳細注釋是來自某個部落格,不知道是誰原創,這裡私自轉帖出來)*/(function () {// initializing變數用來標示當前是否處於類的建立階段,// - 在類的建立階段是不能調用原型方法init的// - 我們曾在本系列的第三篇文章中詳細闡述了這個問題// fnTest是一個Regex,可能的取值為(/\b_super\b/ 或 /.*/)// - 對 /xyz/.test(function() { xyz; }) 的測試是為了檢測瀏覽器是否支援test參數為函數的情況// - 不過我對IE7.0,Chrome2.0,FF3.5進行了測試,此測試都返回true。// - 所以我想這樣對fnTest賦值大部分情況下也是對的:fnTest = /\b_super\b/;var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;// 基類建構函式// 這裡的this是window,所以這整段代碼就向外界開闢了一扇窗戶 - window.Classthis.Class = function () { };// 繼承方法定義Class.extend = function (prop) { // 這個地方很是迷惑人,還記得我在本系列的第二篇文章中提到的麼 // - this具體指向什麼不是定義時能決定的,而是要看此函數是怎麼被調用的 // - 我們已經知道extend肯定是作為方法調用的,而不是作為建構函式 // - 所以這裡this指向的不是Object,而是Function(即是Class),那麼this.prototype就是父類的原型對象 // - 注意:_super指向父類的原型對象,我們會在後面的代碼中多次碰見這個變數 var _super = this.prototype; // 通過將子類的原型指向父類的一個執行個體對象來完成繼承 // - 注意:this是基類建構函式(即是Class) initializing = true; var prototype = new this(); initializing = false; // 我覺得這段代碼是經過作者最佳化過的,所以讀起來非常生硬,我會在後面詳解 for (var name in prop) { prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function (name, fn) { return function () { var tmp = this._super; // 這裡是必要的,第91行注釋代碼可說明之。 this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // 這個地方可以看出,Resig很會偽裝哦 // - 使用一個同名的局部變數來覆蓋全域變數,很是迷惑人 // - 如果你覺得拗口的話,完全可以使用另外一個名字,比如function F()來代替function Class() // - 注意:這裡的Class不是在最外層定義的那個基類建構函式 // 這裡的Class和上邊的window.Class函數不一樣,這裡是window.Class內部的函數局部變數 function Class() { // 在類的執行個體化時,調用原型方法init if (!initializing && this.init) this.init.apply(this, arguments); } // 子類的prototype指向父類的執行個體(完成繼承的關鍵) Class.prototype = prototype; // Class指代上邊的Class,並非一開始的window.Class // 修正constructor指向錯誤 // 是否可用Class.prototype.constructor = Class;來修正??? Class.constructor = Class; // 子類自動擷取extend方法,arguments.callee指向當前正在執行的函數 Class.extend = arguments.callee; return Class;};})();
http://www.cnblogs.com/huajs/archive/2011/11/05/2237091.html
JS重要知識點