標籤:c style class blog code java
1.在javascript的塊級範圍是以函數來決定的.
if(true){ var scopeName = "felayman"; } print(scopeName);上述雖然scopeName是在{}內聲明的,但是它不屬於塊級範圍,在{}範圍內依然能進行訪問.因此,如果想在javascript中聲明一個塊級範圍,需要藉助於函數來實現,如
<span style="font-size:18px;"> function scope(){ if(true){ var scopeName = "felayman"; } }</span>這樣,scopeName才屬於一個函數級(塊級)範圍,在函數外是無法訪問scopeName的.
2.javascript中的變數尋找鏈,如
<span style="font-size:18px;"> var scope = "golbalScope"; var fun = function(){ alert(scopr); var scope = "funcScope"; }; fun();</span>其結果為undefined,上述的代碼可能跟我們所想的有所不同,沒有輸出golbalScope,也沒有輸出funcScope,而是輸出了undefined?這是聲明原因呢?這個是javascript的一個重要特性---範圍搜尋順序,當我們去訪問某個變數的時候,它會優先搜尋scope所在環境,因此搜尋到了var scope = "funcScope";,因此會
拋棄掉上一級環境的var scope = "golbalScope";也就是var scope = "golbalScope";會被變數scope所在的環境給屏蔽掉,然後當程式執行到alert(scope);的時候,但是
scope在當前環境中並沒有進行初始化,因而輸出了undefined.
3.javascript的函數範圍的嵌套
<span style="font-size:18px;"> var fun = function(){ var scope = "scope_one"; (function(){ alert(scope); var scope = "scope_two"; (function(){ alert(scope); })(); })();};fun();</span>
這個例子的結果為,undefined和scope_two,正如上面一個例子一樣,我們可以理解其結果行為,但是我們必須去注意,函數範圍的嵌套關係是在定義的時候決定的,而不是調用的時候決定的,也就是說,javascript的範圍是靜態範圍,也叫詞法範圍,這個例子主要是想說明閉包,在javscript中,一般情況下閉包就是匿名函數,而關於閉包的定義,有這麼一種說法:由函數及其封閉的自由變數組成的集合體.,因此在一般情況下,在javascript我們視函數即為閉包,我們看一個例子
<span style="font-size:18px;">function print(s){document.write(s+"<br/>")}; var getResult = function(){ var count = 0; var get = function(){ count++; return count; }; return get;};var counter = getResult();print(counter());print(counter());print(counter());</span>結果為1,2,3.如果熟悉其他語言,或許會恨困惑這個結果,因為函數,俗稱方法,在其他語言中就是一個儲存臨時變數和結果的一個容器,當函數執行完畢後,會釋放該函數內的所有的局部變數和臨時結果,因此當我們每次調用同一個函數的時候,其結果是不變的,但是在jabascript,卻因為閉包改變了這個規則.這個也是閉包的特性---當一個函數返回它內部定義的一個函數的時候,就產生了一個閉包,閉包不但包括被返回的函數,還包括這個函數的定義環境,上面的例子,當函數getResult()的內建函式get被一個外部變數counter引用的時候,counter和getResult()的局部變數就形成一個閉包.因此上述的counter()其實就是get(),列印counter()的值,就是列印get()的值,但是於此同時get()函數屬於getResult()函數內部的一個閉包(或者可以理解一個布局變數,或者理解get()在getResult()函數的範圍內),在該閉包內依然引用著它的上一級範圍,即getResult()函數範圍,具體的說,在get()函數內部使用了getResult()函數內的變數,因此,雖然我們在getResult()函數外部調用了一個看似與getResult()函數關係不大的counter()函數,但是卻是在getResult()函數內部環境去執行的counter()函數.也就是說print(counter());
print(counter());print(counter());)這段代碼看似是在getResult()外部執行,其實本質上是在getResult()函數內部執行的,因此上述代碼完全等效於下述代碼:
<span style="font-size:18px;">/** * Created by felayman on 2014/5/27. */ function print(s){document.write(s+"<br/>")}; var getResult = function(){ var count = 0; var get = function(){ count++; return count; }; print(get()); print(get()); print(get());};getResult()</span>;但是如果我們在使用的時候這樣使用的話,就沒有閉包的優勢了----與外界失去了聯絡,也就是與其他語言中的函數(方法)一樣的,而javascript中的函數,也正是由於閉包的特性,才被稱為最實用於函數式編程的語言.
4.this所指向的內容
function print(s){document.write(s+"<br/>")} var func1 = { name:"func1scope", func:function(){ print(this.name); } }; var func2 = { name:"func2scope" }; func1.func(); //輸出func1 func2.func = func1.func; func2.func(); //輸出func2 var name = "windowscopr"; window.func = func1.func; func();
上述代碼會輸出:func1scope func2scope windowscopr,因此,可以看出this的指向是隨著其環境而發生變化,但是我們只需要記著一句話即可---this始終指向其執行期間的環境對象,而非定義時的環境對象.
5.call和apply
如果說在javascript中最讓人困惑和難以理解的,
其中call和apply的功能是以不同的對象作為上下文來調用某個對象的函數,或者--就是允許在一個對象中去調用另一個對象中的同名函數(沒有會被複製)並執行(摘自--NodeJS開發指南).如下面的例子:
<span style="font-size:18px;">var oneUser = { name:"oneUser", getName:function(){ alert(this.name); }};var otherUser = { name:"otherUser",};oneUser.getName.call(otherUser);</span>可以這樣理解,我們以每一個對象內部都有一個call對象(函數就是對象),該對象沒有具體指向,但是允許指向任何一個對象內部的另一個對象.而applay與call的用法沒有上面區別,只是接受的參數不同而已.
6.原型
如果要給原型一個定義,百度百科上說的很詳細,但是很抽象,很難理解,而在NodeJs開發指南中,作者用了一個很好的例子----原型就好像是一件藝術品的原件,我們通過一台100%精確的機器把這個原件複製出很多件,如果我們瞭解類似java的物件導向語言的話,就會知道,在java中所有的對象都來自於類,而C++中所有的對象來自於構造器,但是javascript是一種基於物件導向語言,它既有類似C++的構造器,也有類似類的原型.其實我更傾向於一篇博文中的理解,在javascript中
,原型就是java中的一個類的一個對象,該Object Storage Service該類的所有靜態屬性或方法.我更建議使用這種方法來理解原型.因為的確是這樣的,javascript中使用原型的目的就是讓某一類型的所有對象共用一些屬性和函數,提供代碼的複用性,同時也隱藏一些屬性和函數的細節,即提高其內聚性和封裝性,這也是javascript基於物件導向的特性.
<span style="font-size:18px;">function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getInfo = function(){ return "name ="+this.name+",age = "+ this.age+",job = " +this.job; }}</span>
上述代碼沒有使用,原型,因此我們在使用new關鍵字來建立對象的時候,可以根據不同的name,age,job來構件許許多多的Person的對象,這些對象的屬性都不相同,但是作為常識,我們都知道Person都有一個共同的行為--說話(不去鑽牛角尖),當然我們可以改寫上面的代碼來滿足我們的需求:
<span style="font-size:18px;">function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getInfo = function(){ return "name ="+this.name+",age = "+ this.age+",job = " +this.job; } this.say = function(){ alert("人都會說話"); }}</span>
這樣的代碼是允許的,但是不是被建議的,因為我們在建立一個Person對象的時候,就會在記憶體中產生say()函數的內容空間,或許在少量的這樣的行為的時候我們允許這麼做,但是我們再思考一下,如果一個函數中有大量的行為或者屬性都像say()函數一樣是每一個Person對象所共同擁有的,必然Person會說話,會跑,會思考,會苦,會笑.........如果有了大量類似的屬性或行為的話,我們還是寫在函數內部的話,就不好了,因為我們在建立基量很大的對象數量的時候,我們就額外增加了非必須且可避免的記憶體開銷了,因為每個對象在記憶體中都會開闢其所擁有的方法空間,這是不建議的,因此,我們使用了原型的話,這個問題就可以避免了,我們只需要在記憶體的其他地方建立一個原型對象,改對象儲存著Person所有的共同屬性或者方法,只要在每個Person執行個體對象中儲存一個指向改記憶體空間的引用或控制代碼即可.這樣當我們即使建立再多Person執行個體對象,也沒有多少記憶體開銷,這個理解就是javascript原型的特性之一.因此上述代碼改寫成下面就好了:
<span style="font-size:18px;">function Person(name,age,job){ this.name = name; this.age = age; this.job = job;}Person.prototype.getInfo = function(){ return "name = "+this.name+",age = "+this.age+",job ="+this.job;};Person.prototype.say =function(){ console.log("人都會說話");};</span>