來到第四章。javascript
函數:前面說過,函數就是對象,特色就是還可以被調用。前端
<!-- lang: js --> var add = function (a,b) { return a + b; }
當一個函數爲對象的一個屬性時,咱們稱之爲方法。當方法被調用時,this綁定到該對象。java
<!-- lang: js --> var myObject = { value : 0, increment : function (inc) { this.value += typeof inc === 'number' ? inc : 1; } }; myObject.increment(); // 1 document.writeln(myObject.value); myObject.increment(2); // 3 document.writeln(myObject.value);
當一個函數並不是一個對象的屬性時,那麼它就是被看成一個函數來調用。它僅僅是一個函數而已。node
<!-- lang: js --> var sum = add(3,4);
注意:這樣調用函數,this 被綁定到全局對象。一個解決方案是:若是該方法定義一個變量並把它賦值爲this,那麼內部函數就能夠經過那個變量訪問到this,按照約定,那個變量命名爲that。面試
<!-- lang: js --> myObject.double = function () { var that = this; var helper = function () { that.value = add(that.value. that.value); }; helper();// 以函數形式調用helper } //以方法的形式調用double myObject.double(); document.writeln(myObject.value);
若是在一個函數前面加上new來調用,那麼背地裏會建立一個連接到該函數的prototype成員的新對象,同時this會綁定到那個新對象上。ajax
<!-- lang: js --> //建立一個名爲Quo的構造器函數。它構造一個帶有status屬性的對象 var Quo = function (string) { this.status = string; } //給Quo的全部實例提供一個名爲get_status的公共方法 Quo.prototype.get_status = function () { return this.status; } //構建一個Quo實例 var myQuo = new Quo("confused"); document.writeln(myQuo.get_status());
apply方法讓咱們構建一個參數數組傳遞給調用函數。接收兩個參數,第一個是要綁定給this的值,第二個就是一個參數數組。
實際就是把一個本來不是屬於本身的方法或函數用在本身身上,這就是apply的意思。正則表達式
<!-- lang: js --> var array = [3,4]; var sum = add.apply(null,array);//7,用了上面定義的add函數,用數組傳遞參數 //構造一個包含status成員的對象 var statusObject = { status : "A-OK" }; //statusObject 並無繼承自Quo.prototype,但咱們能夠在statusObject上調用get_status方法,儘管statusObject並無 var status = Quo.prototype.get_status.apply(statusObject);//A-OK
當函數被調用時,會獲得一個"免費"配送的參數,那就是arguments數組。數組包括傳遞給形參的參數,也包括沒有分配形式參數的多餘參數。也就是說傳遞給形參的參數能夠經過兩種途徑在函數中取得。算法
<!-- lang: js --> var sum = function (){ var sum = 0; for(var i = 0; i < arguments.length; i += 1){ sum += arguments[i]; } return sum; } document.writeln(sum(1,2,3,4));//10
(感受本身很沒注意差錯控制檢驗這方面的事情TT) 這一部分跟C++差很少,有throw就要有對應的catch。catch到的error要對應throw出來的東西。例子跑出的是一個對象,則catch的參數對應一個對象,而後把對應的信息輸出來。
書上例子先理解下。數組
<!-- lang: js --> var add = function (a,b) { if (typeof a !== 'number' || typeof b !== 'number') { throw { name : 'TypeError', message : 'add needs number' }; } return a + b; } var try_it = function () { try { add("seven"); } catch (e) { document.writeln(e.name + ': ' + e.message); } } try_it();
js可以給語言的基本類型擴充功能。第三章對象的時候,咱們能夠經過給Object.prototype添加方法,可讓該方法對全部對象均可用。這樣的方式對函數,數組,字符串,數字,正則表達式和布爾值都適用。
下面這個擴充在後面會常常用到的喔!閉包
<!-- lang: js --> Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; }
經過給Function.prototype增長方法來是的該方法對全部函數可用。咱們下次給對象增長方法時就不用鍵入prototype這幾個字符,省掉一點麻煩(不是很懂。。實際上是不是看起來帥點,優雅一點呢)
由於基本類型的原型是公用結構,因此在類庫混用時務必當心。一個保險的作法是指在肯定沒有該方法時才添加它。
<!-- lang: js --> Function.prototype.method = function (name, func) { if (!this.prototype[name]) { this.prototype[name] = func; } return this; }
遞歸就是那意思,調用函數自身去解決它的子問題。 很少說,以爲裏面一個遍歷DOM樹的函數寫的不錯。
<!-- lang: js --> var walk_the_DOM = function walk(node, func) { func(node); node = node.firstChild; while (node) { walk(node,func); node = node.nextSibling; } }
這一方面好像以前看過的幾道前端面試題目就有這個的內容,基礎要掌握啊!!
從新找回之前的一篇博文來看,才發現之前都沒怎麼理解,此次看了幾個鍾,有些收穫。牆裂建議看看:http://blog.csdn.net/rommel1/article/details/7804973
總結一下就是,像javascript這樣的解釋型語言,基本分爲兩個階段,編譯期與運行期。在預編譯階段,它是用函數來劃分做用域,而後逐層爲其以 var 聲明的變量與函數定義開闢內存空間,再而後對var變量進行特殊處理,通通賦初始值爲undefined。 在研究上文的過程當中,又看到類屬性和實例屬性的概念,又找了一篇文章,感受說的還比較全面。最重要的一點:動態共有屬性優於原型屬性。即:若是兩者都定義了同一屬性,則最終的屬性值以動態公有屬性爲準。附連接:http://evanwukong.blog.163.com/blog/static/134836495201141752910780/
閉包的好處是內部函數能夠訪問定義他們的外部函數的參數和變量(除了this和arguments) 以前的Quo構造器沒什麼意義,能夠直接訪問到的屬性爲何要用一個getter方法去訪問呢? 因此下面的方式比較推薦。
<!-- lang: js --> var quo = function (status) { return { get_status : function () { return status; } }; }; //構造一個quo實例 var myQuo = quo("amazed"); document.writeln(myQuo.get_status());
這個quo無需在前面加new,即便quo已經返回,但get_status仍能夠訪問到quo對象的status屬性。
下面是一個設置DOM節點爲黃色,而後把它漸變爲白色的函數。
<!-- lang: js --> var fade = function (node) { var level = 1; var step = function () { var hex = level.toString(16); node.style.backgroundColor = '#FFFF' + hex + hex; if (level < 15) { level += 1; setTimeout(step, 100); } }; //書中是setTimeout(step, 100);我以爲下面也能夠 step(); }; fade(document.body);
一個糟糕的例子,
<!-- lang: js --> var add_the_handleers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { alert(i); } };
這個例子的本意是想每個節點alert出不一樣的值,,可是因爲綁定的數是變量i自己,而不是函數在構造時變量i的值。 改良一下,
<!-- lang: js --> var add_the_handlers = function (nodes) { var helper = function (i) { return function (e) { alert(i); }; }; var i; for (var i = 0; i < nodes.length; i += 1) { nodes[i].onclick = helper(i); }; }
利用函數包裝成模塊,提供接口卻隱藏狀態與實現的函數或對象。 咱們想給String增長deentityify方法,用來尋找字符竄中的HTML字符實體並把他們替換爲對應的字符。這就須要保存字符實體及其對應字符。咱們不能在全局變量中保存,保存在函數內部又會帶來運行時的內耗,覺得每次執行該函數時該字面量都會被求值一次。理想方法就是放進閉包裏。
<!-- lang: js --> String.method('deentityify', function () { var entity = { quot : '"', lt : '<', gt : '>' }; return function () { return this.replace(/&([^&;]+);/g, function (a,b) { var r = entity[b]; return typeof r === 'string' ? r : a; } ); }; }()); document.writeln('<">'.deentityify());//<">
注意最後一行的括號,用()運算法馬上調用咱們剛剛構造出來的函數。由於返回的是函數,要讓調用deentityfy函數執行代碼,因此要加括號。 模塊模式的通常形式是:一個定義了私有變量和函數的函數,利用閉包建立能夠訪問私有變量和函數的特權函數,最後返回這個特權函數,或者把他們保存到一個可訪問到的地方。
這個要根據具體狀況來作,好比把一些後面須要用到的結果保存在一個數組,後面用到直接取值,就不用再去計算了。