摘要:復(fù)習(xí)基礎(chǔ)到底是什么的應(yīng)用場合極其廣泛。常量不可以通過重新賦值改變其值,也不可以在代碼運行時重新聲明。布爾對象是原始布爾數(shù)據(jù)類型的一個包裝器整數(shù)整數(shù)可以用十進制基數(shù)為十六進制基數(shù)為八進制基數(shù)為以及二進制基數(shù)為表示。
復(fù)習(xí)js day1 js基礎(chǔ) JavaScript 到底是什么
JavaScript 的應(yīng)用場合極其廣泛。簡單到幻燈片、照片庫、浮動布局和響應(yīng)按鈕點擊。復(fù)雜到游戲、2D 和 3D 動畫、大型數(shù)據(jù)庫驅(qū)動程序,等等。
JavaScript 相當簡潔,卻非常靈活。開發(fā)者們基于 JavaScript 核心編寫了大量實用工具,可以使 開發(fā)工作事半功倍。其中包括:
瀏覽器應(yīng)用程序接口(API)—— 瀏覽器內(nèi)置的 API 提供了豐富的功能,比如:動態(tài)創(chuàng)建 HTML 和設(shè)置 CSS 樣式、從用戶的攝像頭采集處理視頻流、生成3D 圖像與音頻樣本,等等。
第三方 API —— 讓開發(fā)者可以在自己的站點中整合其它內(nèi)容提供者(Twitter、Facebook 等)提供的功能。
第三方框架和庫 —— 用來快速構(gòu)建網(wǎng)站和應(yīng)用。
變量 變量的定義和數(shù)據(jù)類型在應(yīng)用程序中,使用變量來作為值的符號名。變量的名字又叫做標識符,其需要遵守一定的規(guī)則。
一個 JavaScript 標識符必須以字母、下劃線(_)或者美元符號($)開頭;后續(xù)的字符也可以是數(shù)字(0-9)。因為 JavaScript 語言是區(qū)分大小寫的,所以字母可以是從“A”到“Z”的大寫字母和從“a”到“z”的小寫字母。
變量 | 解釋 |
---|---|
string | 字符串(一串文本)。字符串的值必須將用引號(單雙均可,必須成對)擴起來。 |
Number | 數(shù)字。無需引號。 |
Boolean | 布爾值(真 / 假)。 true/false 是 JS 里的特殊關(guān)鍵字,無需引號。 |
Array | 數(shù)組,用于在單一引用中存儲多個值的結(jié)構(gòu) |
Object | 對象,JavaScript 里一切皆對象,一切皆可儲存在變量里。這一點要牢記于心。 |
字符串轉(zhuǎn)換為數(shù)字
有一些方法可以將內(nèi)存中表示一個數(shù)字的字符串轉(zhuǎn)換為對應(yīng)的數(shù)字。parseInt()和parseFloat()
parseInt 方法只能返回整數(shù),所以使用它會丟失小數(shù)部分。另外,調(diào)用 parseInt 時最好總是帶上進制(radix) 參數(shù),這個參數(shù)用于指定使用哪一種進制。
將字符串轉(zhuǎn)換為數(shù)字的另一種方法是使用一元加法運算符。
"1.1" + "1.1" = "1.11.1" (+"1.1") + (+"1.1") = 2.2 // 注意:加入括號為清楚起見,不是必需的。聲明變量
var 聲明全局變量和局部變量
let 聲明塊作用域的局部變量
const 聲明一個常量
使用var和let聲明的變量時沒有賦予初始值的,其值為undefined,
var a; console.log("The value of a is " + a); // a 的值是 undefined console.log("The value of b is " + b);// b 的值是 undefined var b; console.log("The value of c is " + c); // 未捕獲的引用錯誤: c 未被定義 let x; console.log("The value of x is " + x); // x 的值是 undefined console.log("The value of y is " + y);// 未捕獲的引用錯誤: y 未被定義 let y;
你可以使用 undefined 來判斷一個變量是否已賦值。在以下的代碼中,變量input未被賦值,因此 if 條件語句的求值結(jié)果是 true
var input; if(input === undefined){ doThis(); } else { doThat(); }
undefined 值在布爾類型環(huán)境中會被當作 false 。
數(shù)值類型環(huán)境中 undefined 值會被轉(zhuǎn)換為 NaN。
當你對一個 null 變量求值時,空值 null 在數(shù)值類型環(huán)境中會被當作0來對待,而布爾類型環(huán)境中會被當作 false
var n = null; console.log(n * 32); // 在控制臺中會顯示 0變量的作用域
在函數(shù)之外聲明的變量,叫做全局變量,因為它可被當前文檔中的任何其他代碼所訪問。在函數(shù)內(nèi)部聲明的變量,叫做局部變量,因為它只能在當前函數(shù)的內(nèi)部訪問。
ECMAScript 6 之前的 JavaScript 沒有 語句塊 作用域;相反,語句塊中聲明的變量將成為語句塊所在函數(shù)(或全局作用域)的局部變量。例如,如下的代碼將在控制臺輸出 5,因為 x 的作用域是聲明了 x 的那個函數(shù)(或全局范圍),而不是 if 語句塊。
if (true) { var x = 5; } console.log(x); // 5
如果使用 ECMAScript 6 中的 let 聲明,上述行為將發(fā)生變化。
if (true) { let y = 5; } console.log(y); // ReferenceError: y 沒有被聲明變量提升
JavaScript 變量的另一個不同尋常的地方是,你可以先使用變量稍后再聲明變量而不會引發(fā)異常。這一概念稱為變量提升;JavaScript 變量感覺上是被“提升”或移到了函數(shù)或語句的最前面。但是,提升后的變量將返回 undefined 值。因此在使用或引用某個變量之后進行聲明和初始化操作,這個被提升的變量仍將返回 undefined 值。
/** * 例子1 */ console.log(x === undefined); // true var x = 3; /** * 例子2 */ // will return a value of undefined var myvar = "my value"; (function() { console.log(myvar); // undefined var myvar = "local value"; })(); //詳細解釋就是 /** * 例子1 */ var x; console.log(x === undefined); // true x = 3; /** * 例子2 */ var myvar = "my value"; (function() { var myvar; console.log(myvar); // undefined myvar = "local value"; })();
由于存在變量提升,一個函數(shù)中所有的var語句應(yīng)盡可能地放在接近函數(shù)頂部的地方。這個習(xí)慣將大大提升代碼的清晰度。
在 ECMAScript 6 中,let(const)將不會提升變量到代碼塊的頂部。因此,在變量聲明之前引用這個變量,將拋出引用錯誤(ReferenceError)。這個變量將從代碼塊一開始的時候就處在一個“暫時性死區(qū)”,直到這個變量被聲明為止。
console.log(x); // ReferenceError let x = 3;函數(shù)提升
對于函數(shù)來說,只有函數(shù)聲明會被提升到頂部,而函數(shù)表達式不會被提升。
/* 函數(shù)聲明 */ foo(); // "bar" function foo() { console.log("bar"); } /* 函數(shù)表達式 */ baz(); // 類型錯誤:baz 不是一個函數(shù) var baz = function() { console.log("bar2"); };常量
你可以用關(guān)鍵字 const 創(chuàng)建一個只讀的常量。常量標識符的命名規(guī)則和變量相同:必須以字母、下劃線(_)或美元符號($)開頭并可以包含有字母、數(shù)字或下劃線。
const PI = 3.14;
常量不可以通過重新賦值改變其值,也不可以在代碼運行時重新聲明。它必須被初始化為某個值。
常量的作用域規(guī)則與 let 塊級作用域變量相同。若省略const關(guān)鍵字,則該標識符將被視為變量。
在同一作用域中,不能使用與變量名或函數(shù)名相同的名字來命名常量。
然而,對象屬性被賦值為常量是不受保護的,所以下面的語句執(zhí)行時不會產(chǎn)生錯誤。
const MY_OBJECT = {"key": "value"}; MY_OBJECT.key = "otherValue";
同樣的,數(shù)組的被定義為常量也是不受保護的,所以下面的語句執(zhí)行時也不會產(chǎn)生錯誤。
const MY_ARRAY = ["HTML","CSS"]; MY_ARRAY.push("JAVASCRIPT"); console.log(MY_ARRAY); //logs ["HTML","CSS","JAVASCRIPT"];字面量 (Literals)
譯注:字面量是由語法表達式定義的常量;或,通過由一定字詞組成的語詞表達式定義的常量
在JavaScript中,你可以使用各種字面量。這些字面量是腳本中按字面意思給出的固定的值,而不是變量。(譯注:字面量是常量,其值是固定的,而且在程序腳本運行中不可更改
數(shù)組字面量 (Array literals)數(shù)組字面值是一個封閉在方括號對([])中的包含有零個或多個表達式的列表,其中每個表達式代表數(shù)組的一個元素。當你使用數(shù)組字面值創(chuàng)建一個數(shù)組時,該數(shù)組將會以指定的值作為其元素進行初始化,而其長度被設(shè)定為元素的個數(shù)。
下面的示例用3個元素生成數(shù)組coffees,它的長度是3。
var coffees = ["French Roast", "Colombian", "Kona"]; var a=[3]; console.log(a.length); // 1 console.log(a[0]); // 3 //注意 這里的數(shù)組字面值也是一種對象初始化器。
若在頂層(全局)腳本里用字面值創(chuàng)建數(shù)組,JavaScript語言將會在每次對包含該數(shù)組字面值的表達式求值時解釋該數(shù)組。另一方面,在函數(shù)中使用的數(shù)組,將在每次調(diào)用函數(shù)時都會被創(chuàng)建一次。
數(shù)組字面值同時也是數(shù)組對象。有關(guān)數(shù)組對象的詳情請參見數(shù)組對象一文。
數(shù)組字面值中的多余逗號
(譯注:聲明時)你不必列舉數(shù)組字面值中的所有元素。若你在同一行中連寫兩個逗號(,),數(shù)組中就會產(chǎn)生一個沒有被指定的元素,其初始值是undefined。以下示例創(chuàng)建了一個名為fish的數(shù)組:
var fish = ["Lion", , "Angel"];
在這個數(shù)組中,有兩個已被賦值的元素,和一個空元素(fish[0]是"Lion",fish[1]是undefined,而fish[2]是"Angel";譯注:此時數(shù)組的長度屬性fish.length是3)。
如果你在元素列表的尾部添加了一個逗號,它將會被忽略。在下面的例子中,數(shù)組的長度是3,并不存在myList[3]這個元素(譯注:這是指數(shù)組的第4個元素噢,作者是在幫大家復(fù)習(xí)數(shù)組元素的排序命名方法)。元素列表中其它所有的逗號都表示一個新元素(的開始)。
注意:尾部的逗號在早期版本的瀏覽器中會產(chǎn)生錯誤,因而編程時的最佳實踐方式就是移除它們。
(譯注:而“現(xiàn)代”的瀏覽器似乎鼓勵這種方式,這也很好解釋原因。尾部逗號可以減少向數(shù)組的最后添加元素時,因為忘記為這最后一個元素加逗號 所造成的錯誤。)
var myList = ["home", , "school", ];
在下面的例子中,數(shù)組的長度是4,元素myList[0]和myList[2]缺失(譯注:沒被賦值,因而是undefined)。
var myList = [ , "home", , "school"];
再看一個例子。在這里,該數(shù)組的長度是4,元素myList[1]和myList[3]被漏掉了。(但是)只有最后的那個逗號被忽略。
var myList = ["home", , "school", , ];
理解多余的逗號(在腳本運行時會被如何處理)的含義,對于從語言層面理解JavaScript是十分重要的。但是,在你自己寫代碼時:顯式地將缺失的元素聲明為undefined,將大大提高你的代碼的清晰度和可維護性。
布爾字面量 (Boolean literals)布爾類型有兩種字面量:true和false。
不要混淆作為布爾對象的真和假與布爾類型的原始值true和false。布爾對象是原始布爾數(shù)據(jù)類型的一個包裝器
整數(shù) (Integers)整數(shù)可以用十進制(基數(shù)為10)、十六進制(基數(shù)為16)、八進制(基數(shù)為8)以及二進制(基數(shù)為2)表示。
十進制整數(shù)字面量由一串數(shù)字序列組成,且沒有前綴0。
八進制的整數(shù)以 0(或0O、0o)開頭,只能包括數(shù)字0-7。
十六進制整數(shù)以0x(或0X)開頭,可以包含數(shù)字(0-9)和字母 a~f 或 A~F。
二進制整數(shù)以0b(或0B)開頭,只能包含數(shù)字0和1。
嚴格模式下,八進制整數(shù)字面量必須以0o或0O開頭,而不能以0開頭。
整數(shù)字面量舉例:
0, 117 and -345 (十進制, 基數(shù)為10)對象字面量 (Object literals)
015, 0001 and -0o77 (八進制, 基數(shù)為8)
0x1123, 0x00111 and -0xF1A7 (十六進制, 基數(shù)為16或"hex")
0b11, 0b0011 and -0b11 (二進制, 基數(shù)為2)
對象字面值是封閉在花括號對({})中的一個對象的零個或多個"屬性名-值"對的(元素)列表。你不能在一條語句的開頭就使用對象字面值,這將導(dǎo)致錯誤或產(chǎn)生超出預(yù)料的行為, 因為此時左花括號({)會被認為是一個語句塊的起始符號。(譯者:這 里需要對語句statement、塊block等基本名詞的解釋)
以下是一個對象字面值的例子。對象car的第一個元素(譯注:即一個屬性/值對)定義了屬性myCar;第二個元素,屬性getCar,引用了一個函數(shù)(即CarTypes("Honda"));第三個元素,屬性special,使用了一個已有的變量(即Sales)。
var Sales = "Toyota"; function CarTypes(name) { return (name === "Honda") ? name : "Sorry, we don"t sell " + name + "." ; } var car = { myCar: "Saturn", getCar: CarTypes("Honda"), special: Sales }; console.log(car.myCar); // Saturn console.log(car.getCar); // Honda console.log(car.special); // Toyota
更進一步的,你可以使用數(shù)字或字符串字面值作為屬性的名字,或者在另一個字面值內(nèi)嵌套上一個字面值。如下的示例中使用了這些可選項。
var car = { manyCars: {a: "Saab", "b": "Jeep"}, 7: "Mazda" }; console.log(car.manyCars.b); // Jeep console.log(car[7]); // Mazda
對象屬性名字可以是任意字符串,包括空串。如果對象屬性名字不是合法的javascript標識符,它必須用""包裹。屬性的名字不合法,那么便不能用.訪問屬性值,而是通過類數(shù)組標記("[]")訪問和賦值。
var unusualPropertyNames = { "": "An empty string", "!": "Bang!" } console.log(unusualPropertyNames.""); // 語法錯誤: Unexpected string console.log(unusualPropertyNames[""]); // An empty string console.log(unusualPropertyNames.!); // 語法錯誤: Unexpected token ! console.log(unusualPropertyNames["!"]); // Bang!字符串字面量 (String literals)
字符串字面量是由雙引號(")對或單引號(")括起來的零個或多個字符。字符串被限定在同種引號之間;也即,必須是成對單引號或成對雙引號。下面的例子都是字符串字面值:
"foo" "bar" "1234" "one line another line" "John"s cat"
你可以在字符串字面值上使用字符串對象的所有方法——JavaScript會自動將字符串字面值轉(zhuǎn)換為一個臨時字符串對象,調(diào)用該方法,然后廢棄掉那個臨時的字符串對象。你也能用對字符串字面值使用類似String.length的屬性:
console.log("John"s cat".length) // 將打印字符串中的字符個數(shù)(包括空格) // 結(jié)果為:10
在ES2015中,還提供了一種模板字符串(template literals),模板字符串提供了一些語法糖來幫你構(gòu)造字符串。這與Perl、Python還有其他語言中的字符串插值(string interpolation)的特性非常相似。除此之外,你可以在通過模板字符串前添加一個tag來自定義模板字符串的解析過程,這可以用來防止注入攻擊,或者用來建立基于字符串的高級數(shù)據(jù)抽象。
// Basic literal string creation `In JavaScript " " is a line-feed.` // Multiline strings `In JavaScript this is not legal.` // String interpolation var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` // Construct an HTTP request prefix is used to interpret the replacements and construction POST`http://foo.org/bar?a=${a}&b=${b} Content-Type: application/json X-Credentials: ${credentials} { "foo": ${foo}, "bar": ${bar}}`(myOnReadyStateChangeHandler);
除非有特別需要使用字符串對象,否則,你應(yīng)當始終使用字符串字面值。
js流程控制與錯誤處理 條件判斷語句條件判斷語句指的是根據(jù)指定的條件所返回的結(jié)果(真或假或其它預(yù)定義的),來執(zhí)行特定的語句。JavaScript 支持兩種條件判斷語句:if...else和switch。
let iceCream = "chocolate"; if (iceCream === "chocolate") { alert("我最喜歡巧克力冰激淋了。"); } else { alert("但是巧克力才是我的最愛呀……"); }異常處理語句
你可以用 throw 語句拋出一個異常并且用 try...catch 語句捕獲處理它。
throw 語句
try...catch 語句
Promises從 ECMAScript 6 開始,JavaScript 增加了對 Promise 對象的支持,它允許你對延時和異步操作流進行控制。
Promise 對象有以下幾種狀態(tài):
pending:初始的狀態(tài),即正在執(zhí)行,不處于 fulfilled 或 rejected 狀態(tài)。
fulfilled:成功的完成了操作。
rejected:失敗,沒有完成操作。
settled:Promise 處于 fulfilled 或 rejected 二者中的任意一個狀態(tài), 不會是 pending。
展現(xiàn)了 Promise 的工作流
function imgLoad(url) { return new Promise(function(resolve, reject) { var request = new XMLHttpRequest(); request.open("GET", url); request.responseType = "blob"; request.onload = function() { if (request.status === 200) { resolve(request.response); } else { reject(Error("Image didn"t load successfully; error code:" + request.statusText)); } }; request.onerror = function() { reject(Error("There was a network error.")); }; request.send(); }); }循環(huán)和迭代
JavaScript中提供了這些循環(huán)語句:
for 語句
do...while 語句
while 語句
labeled 語句
break 語句
continue 語句
for...in 語句
for...of 語句
for 語句一個for循環(huán)會一直重復(fù)執(zhí)行,直到指定的循環(huán)條件為fasle。 JavaScript的for循環(huán)和Java與C的for循環(huán)是很相似的。
for ([initialExpression]; [condition]; [incrementExpression])
statement
當一個for循環(huán)執(zhí)行的時候,會發(fā)生以下事件:
如果有初始化表達式initialExpression,它將被執(zhí)行。這個表達式通常會初始化一個或多個循環(huán)計數(shù)器,但語法上是允許一個任意復(fù)雜度的表達式的。這個表達式也可以聲明變量。
計算condition表達式的值。如果condition的值是true,循環(huán)中的statement會被執(zhí)行。如果condition的值是false,for循環(huán)終止。如果condition表達式整個都被省略掉了,condition的值會被認為是true。
循環(huán)中的 statement被執(zhí)行。如果需要執(zhí)行多條語句,可以使用塊 ({ ... })來包裹這些語句。
如果有更新表達式incrementExpression,執(zhí)行它.
然后流程回到步驟2。
do...whiledo...while 語句一直重復(fù)直到指定的條件求值得到假(false)。 一個 do...while 語句看起來像這樣:
do statement while (condition);
statement 在檢查條件之前會執(zhí)行一次。要執(zhí)行多條語句(語句塊),要使用塊語句 ({ ... }) 包括起來。 如果 condition 為真(true),statement 將再次執(zhí)行。 在每個執(zhí)行的結(jié)尾會進行條件的檢查。當 condition 為假(false),執(zhí)行會停止并且把控制權(quán)交回給 do...while 后面的語句。
在下面的例子中, 這個 do 循環(huán)將至少重復(fù)一次并且一直重復(fù)直到 i 不再小于 5。
do { i += 1; console.log(i); } while (i < 5);while 語句
一個 while 語句只要指定的條件求值為真(true)就會一直執(zhí)行它的語句塊。一個 while 語句看起來像這樣:
while (condition)statement
如果這個條件變?yōu)榧伲h(huán)里的 statement 將會停止執(zhí)行并把控制權(quán)交回給 while 語句后面的代碼。
條件檢測會在每次 statement 執(zhí)行之前發(fā)生。如果條件返回為真, statement 會被執(zhí)行并緊接著再次測試條件。如果條件返回為假,執(zhí)行將停止并把控制權(quán)交回給 while 后面的語句。
要執(zhí)行多條語句(語句塊),要使用塊語句 ({ ... }) 包括起來。
例子 1
下面的 while 循環(huán)只要 n 小于 3就會一直執(zhí)行:
var n = 0; var x = 0; while (n < 3) { n++; x += n; }
在每次循環(huán)里, n 會增加1并被加到 x 上。所以, x 和 n 的變化是:
第一次完成后: n = 1 和 x = 1
第二次完成后: n = 2 和 x = 3
第三次完成后: n = 3 和 x = 6
在三次完成后, 條件 n < 3 結(jié)果不再為真,所以循環(huán)終止了。
例子 2
避免無窮循環(huán)(無限循環(huán))。保證循環(huán)的條件結(jié)果最終會變成假;否則,循環(huán)永遠不會停止。下面這個 while 循環(huán)會永遠執(zhí)行因為條件永遠不會變成假:
while (true) { console.log("Hello, world"); }函數(shù)
函數(shù) 用來封裝可復(fù)用的功能。如果沒有函數(shù),一段特定的操作過程用幾次就要重復(fù)寫幾次,而使用函數(shù)則只需寫下函數(shù)名和一些簡短的信息
瀏覽器內(nèi)置函數(shù)和用戶定義的函數(shù)
定義函數(shù) 函數(shù)聲明一個函數(shù)定義(也稱為函數(shù)聲明,或函數(shù)語句)由一系列的function關(guān)鍵字組成,依次為:
函數(shù)的名稱。
函數(shù)參數(shù)列表,包圍在括號中并由逗號分隔。
定義函數(shù)的 JavaScript 語句,用大括號{}括起來。
例如,以下的代碼定義了一個簡單的square函數(shù):
function square(number) { return number * number; }
函數(shù)square使用了一個參數(shù),叫作number。這個函數(shù)只有一個語句,它說明該函數(shù)將函數(shù)的參數(shù)(即number)自乘后返回。函數(shù)的return語句確定了函數(shù)的返回值:
return number * number;
原始參數(shù)(比如一個具體的數(shù)字)被作為值傳遞給函數(shù);值被傳遞給函數(shù),如果被調(diào)用函數(shù)改變了這個參數(shù)的值,這樣的改變不會影響到全局或調(diào)用函數(shù)。
如果你傳遞一個對象(即一個非原始值,例如Array或用戶自定義的對象)作為參數(shù),而函數(shù)改變了這個對象的屬性,這樣的改變對函數(shù)外部是可見的,如下面的例子所示:
function myFunc(theObject) { theObject.make = "Toyota"; } var mycar = {make: "Honda", model: "Accord", year: 1998}; var x, y; x = mycar.make; // x獲取的值為 "Honda" myFunc(mycar); y = mycar.make; // y獲取的值為 "Toyota" // (make屬性被函數(shù)改變了)函數(shù)表達式
雖然上面的函數(shù)聲明在語法上是一個語句,但函數(shù)也可以由函數(shù)表達式創(chuàng)建。這樣的函數(shù)可以是匿名的;它不必有一個名稱。例如,函數(shù)square也可這樣來定義:
var square = function(number) { return number * number; }; var x = square(4); // x gets the value 16
然而,函數(shù)表達式也可以提供函數(shù)名,并且可以用于在函數(shù)內(nèi)部代指其本身,或者在調(diào)試器堆棧跟蹤中識別該函數(shù):
var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)}; console.log(factorial(3));
當將函數(shù)作為參數(shù)傳遞給另一個函數(shù)時,函數(shù)表達式很方便。下面的例子演示了一個叫map的函數(shù)如何被定義,而后使用一個表達式函數(shù)作為其第一個參數(shù)進行調(diào)用:
function map(f,a) { var result = [],i; //創(chuàng)建一個新數(shù)組 for (i = 0; i != a.length; i++) result[i] = f(a[i]); return result; }
下面的代碼:
function map(f, a) { var result = []; // 創(chuàng)建一個數(shù)組 var i; // 聲明一個值,用來循環(huán) for (i = 0; i != a.length; i++) result[i] = f(a[i]); return result; } var f = function(x) { return x * x * x; } var numbers = [0,1, 2, 5,10]; var cube = map(f,numbers); console.log(cube);
返回 [0, 1, 8, 125, 1000]。
在 JavaScript 中,可以根據(jù)條件來定義一個函數(shù)。比如下面的代碼,當num 等于 0 的時候才會定義 myFunc :
var myFunc; if (num == 0){ myFunc = function(theObject) { theObject.make = "Toyota" } }
除了上述的定義函數(shù)方法外,你也可以在運行時用 Function 構(gòu)造器由一個字符串來創(chuàng)建一個函數(shù) ,很像 eval() 函數(shù)。
當一個函數(shù)是一個對象的屬性時,稱之為方法。了解更多關(guān)于對象和方法的知識 使用對象。
調(diào)用函數(shù)調(diào)用函數(shù)節(jié)
定義一個函數(shù)并不會自動的執(zhí)行它。定義了函數(shù)僅僅是賦予函數(shù)以名稱并明確函數(shù)被調(diào)用時該做些什么。調(diào)用函數(shù)才會以給定的參數(shù)真正執(zhí)行這些動作。例如,一旦你定義了函數(shù)square,你可以如下這樣調(diào)用它:
square(5);
上述語句通過提供參數(shù) 5 來調(diào)用函數(shù)。函數(shù)執(zhí)行完它的語句會返回值25。
函數(shù)一定要處于調(diào)用它們的域中,但是函數(shù)的聲明可以被提升(出現(xiàn)在調(diào)用語句之后),如下例:
console.log(square(5)); /* ... */ function square(n) { return n*n }
函數(shù)域是指函數(shù)聲明時的所在的地方,或者函數(shù)在頂層被聲明時指整個程序。
提示:注意只有使用如上的語法形式(即 function funcName(){})才可以。而下面的代碼是無效的。就是說,函數(shù)提升僅適用于函數(shù)聲明,而不適用于函數(shù)表達式。
console.log(square); // square is hoisted with an initial value undefined. console.log(square(5)); // TypeError: square is not a function var square = function (n) { return n * n; }
函數(shù)的參數(shù)并不局限于字符串或數(shù)字。你也可以將整個對象傳遞給函數(shù)。函數(shù) show_props(其定義參見 用對象編程)就是一個將對象作為參數(shù)的例子。
函數(shù)可以被遞歸,就是說函數(shù)可以調(diào)用其本身。例如,下面這個函數(shù)就是用遞歸計算階乘:
function factorial(n){ if ((n == 0) || (n == 1)) return 1; else return (n * factorial(n - 1)); }
你可以計算1-5的階乘如下:
var a, b, c, d, e; a = factorial(1); // 1賦值給a b = factorial(2); // 2賦值給b c = factorial(3); // 6賦值給c d = factorial(4); // 24賦值給d e = factorial(5); // 120賦值給e
還有其它的方式來調(diào)用函數(shù)。常見的一些情形是某些地方需要動態(tài)調(diào)用函數(shù),或者函數(shù)的實參數(shù)量是變化的,或者調(diào)用函數(shù)的上下文需要指定為在運行時確定的特定對象。顯然,函數(shù)本身就是對象,因此這些對象也有方法(參考Function )。作為此中情形之一,apply()方法可以實現(xiàn)這些目的。
函數(shù)作用域在函數(shù)內(nèi)定義的變量不能在函數(shù)之外的任何地方訪問,因為變量僅僅在該函數(shù)的域的內(nèi)部有定義。相對應(yīng)的,一個函數(shù)可以訪問定義在其范圍內(nèi)的任何變量和函數(shù)。換言之,定義在全局域中的函數(shù)可以訪問所有定義在全局域中的變量。在另一個函數(shù)中定義的函數(shù)也可以訪問在其父函數(shù)中定義的所有變量和父函數(shù)有權(quán)訪問的任何其他變量。
// 下面的變量定義在全局作用域(global scope)中 var num1 = 20, num2 = 3, name = "Chamahk"; // 本函數(shù)定義在全局作用域 function multiply() { return num1 * num2; } multiply(); // 返回 60 // 嵌套函數(shù)的例子 function getScore() { var num1 = 2, num2 = 3; function add() { return name + " scored " + (num1 + num2); } return add(); } getScore(); // 返回 "Chamahk scored 5"作用域和函數(shù)堆棧 遞歸
一個函數(shù)可以指向并調(diào)用自身。有三種方法可以達到這個目的:
函數(shù)名
arguments.callee
作用域下的一個指向該函數(shù)的變量名
例如,思考一下如下的函數(shù)定義:
var foo = function bar() { // statements go here };
在這個函數(shù)體內(nèi),以下的語句是等價的:
bar()
arguments.callee()
foo()
調(diào)用自身的函數(shù)我們稱之為遞歸函數(shù)。在某種意義上說,遞歸近似于循環(huán)。兩者都重復(fù)執(zhí)行相同的代碼,并且兩者都需要一個終止條件(避免無限循環(huán)或者無限遞歸)。例如以下的循環(huán):
var x = 0; while (x < 10) { // "x < 10" 是循環(huán)條件 // do stuff x++; }
可以被轉(zhuǎn)化成一個遞歸函數(shù)和對其的調(diào)用:
function loop(x) { if (x >= 10) // "x >= 10" 是退出條件(等同于 "!(x < 10)") return; // 做些什么 loop(x + 1); // 遞歸調(diào)用 } loop(0);
不過,有些算法并不能簡單的用迭代來實現(xiàn)。例如,獲取樹結(jié)構(gòu)中所有的節(jié)點時,使用遞歸實現(xiàn)要容易得多:
function walkTree(node) { if (node == null) // return; // do something with node for (var i = 0; i < node.childNodes.length; i++) { walkTree(node.childNodes[i]); } }
跟loop函數(shù)相比,這里每個遞歸調(diào)用都產(chǎn)生了更多的遞歸。
將遞歸算法轉(zhuǎn)換為非遞歸算法是可能的,不過邏輯上通常會更加復(fù)雜,而且需要使用堆棧。事實上,遞歸函數(shù)就使用了堆棧:函數(shù)堆棧。
這種類似堆棧的行為可以在下例中看到:
function foo(i) { if (i < 0) return; console.log("begin:" + i); foo(i - 1); console.log("end:" + i); } foo(3); // 輸出: // begin:3 // begin:2 // begin:1 // begin:0 // end:0 // end:1 // end:2 // end:3嵌套函數(shù)和閉包
你可以在一個函數(shù)里面嵌套另外一個函數(shù)。嵌套(內(nèi)部)函數(shù)對其容器(外部)函數(shù)是私有的。它自身也形成了一個閉包。一個閉包是一個可以自己擁有獨立的環(huán)境與變量的的表達式(通常是函數(shù))。
既然嵌套函數(shù)是一個閉包,就意味著一個嵌套函數(shù)可以”繼承“容器函數(shù)的參數(shù)和變量。換句話說,內(nèi)部函數(shù)包含外部函數(shù)的作用域。
可以總結(jié)如下:
內(nèi)部函數(shù)只可以在外部函數(shù)中訪問。
內(nèi)部函數(shù)形成了一個閉包:它可以訪問外部函數(shù)的參數(shù)和變量,但是外部函數(shù)卻不能使用它的參數(shù)和變量。
下面的例子展示了嵌套函數(shù):
function addSquares(a, b) { function square(x) { return x * x; } return square(a) + square(b); } a = addSquares(2, 3); // returns 13 b = addSquares(3, 4); // returns 25 c = addSquares(4, 5); // returns 41
由于內(nèi)部函數(shù)形成了閉包,因此你可以調(diào)用外部函數(shù)并為外部函數(shù)和內(nèi)部函數(shù)指定參數(shù):
function outside(x) { function inside(y) { return x + y; } return inside; } fn_inside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give it result = fn_inside(5); // returns 8 result1 = outside(3)(5); // returns 8多層嵌套函數(shù)
函數(shù)可以被多層嵌套。例如,函數(shù)A可以包含函數(shù)B,函數(shù)B可以再包含函數(shù)C。B和C都形成了閉包,所以B可以訪問A,C可以訪問B和A。因此,閉包可以包含多個作用域;他們遞歸式的包含了所有包含它的函數(shù)作用域。這個稱之為作用域鏈。(稍后會詳細解釋)
思考一下下面的例子:
function A(x) { function B(y) { function C(z) { console.log(x + y + z); } C(3); } B(2); } A(1); // logs 6 (1 + 2 + 3)
在這個例子里面,C可以訪問B的y和A的x。這是因為:
B形成了一個包含A的閉包,B可以訪問A的參數(shù)和變量
C形成了一個包含B的閉包
B包含A,所以C也包含A,C可以訪問B和A的參數(shù)和變量。換言之,C用這個順序鏈接了B和A的作用域
反過來卻不是這樣。A不能訪問C,因為A看不到B中的參數(shù)和變量,C是B中的一個變量,所以C是B私有的。
當同一個閉包作用域下兩個參數(shù)或者變量同名時,就會產(chǎn)生命名沖突。更近的作用域有更高的優(yōu)先權(quán),所以最近的優(yōu)先級最高,最遠的優(yōu)先級最低。這就是作用域鏈。鏈的第一個元素就是最里面的作用域,最后一個元素便是最外層的作用域。
看以下的例子:
function outside() { var x = 5; function inside(x) { return x * 2; } return inside; }
outside()(10); // returns 20 instead of 10
命名沖突發(fā)生在return x上,inside的參數(shù)x和outside變量x發(fā)生了沖突。這里的作用鏈域是{inside, outside, 全局對象}。因此inside的x具有最高優(yōu)先權(quán),返回了20(inside的x)而不是10(outside的x)。
閉包是 JavaScript 中最強大的特性之一。JavaScript 允許函數(shù)嵌套,并且內(nèi)部函數(shù)可以訪問定義在外部函數(shù)中的所有變量和函數(shù),以及外部函數(shù)能訪問的所有變量和函數(shù)。但是,外部函數(shù)卻不能夠訪問定義在內(nèi)部函數(shù)中的變量和函數(shù)。這給內(nèi)部函數(shù)的變量提供了一定的安全性。此外,由于內(nèi)部函數(shù)可以訪問外部函數(shù)的作用域,因此當內(nèi)部函數(shù)生存周期大于外部函數(shù)時,外部函數(shù)中定義的變量和函數(shù)將的生存周期比內(nèi)部函數(shù)執(zhí)行時間長。當內(nèi)部函數(shù)以某一種方式被任何一個外部函數(shù)作用域訪問時,一個閉包就產(chǎn)生了。
var pet = function(name) { //外部函數(shù)定義了一個變量"name" var getName = function() { //內(nèi)部函數(shù)可以訪問 外部函數(shù)定義的"name" return name; } //返回這個內(nèi)部函數(shù),從而將其暴露在外部函數(shù)作用域 return getName; }; myPet = pet("Vivie"); myPet(); // 返回結(jié)果 "Vivie"
實際上可能會比上面的代碼復(fù)雜的多。在下面這種情形中,返回了一個包含可以操作外部函數(shù)的內(nèi)部變量方法的對象。
var createPet = function(name) { var sex; return { setName: function(newName) { name = newName; }, getName: function() { return name; }, getSex: function() { return sex; }, setSex: function(newSex) { if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) { sex = newSex; } } } } var pet = createPet("Vivie"); pet.getName(); // Vivie pet.setName("Oliver"); pet.setSex("male"); pet.getSex(); // male pet.getName(); // Oliver
在上面的代碼中,外部函數(shù)的name變量對內(nèi)嵌函數(shù)來說是可取得的,而除了通過內(nèi)嵌函數(shù)本身,沒有其它任何方法可以取得內(nèi)嵌的變量。內(nèi)嵌函數(shù)的內(nèi)嵌變量就像內(nèi)嵌函數(shù)的保險柜。它們會為內(nèi)嵌函數(shù)保留“穩(wěn)定”——而又安全——的數(shù)據(jù)參與運行。而這些內(nèi)嵌函數(shù)甚至不會被分配給一個變量,或者不必一定要有名字。
var getCode = (function(){ var secureCode = "0]Eal(eh&2"; // A code we do not want outsiders to be able to modify... return function () { return secureCode; }; })(); getCode(); // Returns the secret code
盡管有上述優(yōu)點,使用閉包時仍然要小心避免一些陷阱。如果一個閉包的函數(shù)用外部函數(shù)的變量名定義了同樣的變量,那在外部函數(shù)域?qū)⒃僖矡o法指向該變量。
var createPet = function(name) { // Outer function defines a variable called "name" return { setName: function(name) { // Enclosed function also defines a variable called "name" name = name; // ??? How do we access the "name" defined by the outer function ??? } } }箭頭函數(shù) this 的詞法
在箭頭函數(shù)出現(xiàn)之前,每一個新函數(shù)都重新定義了自己的 this 值(在嚴格模式下,一個新的對象在構(gòu)造函數(shù)里是未定義的,以“對象方法”的方式調(diào)用的函數(shù)是上下文對象等)。以面向?qū)ο蟮木幊田L(fēng)格,這樣著實有點惱人。
function Person() { // The Person() constructor defines `this` as itself. this.age = 0; setInterval(function growUp() { // In nonstrict mode, the growUp() function defines `this` // as the global object, which is different from the `this` // defined by the Person() constructor. this.age++; }, 1000); } var p = new Person();
在ECMAScript 3/5里,通過把this的值賦值給一個變量可以修復(fù)這個問題。
function Person() { var self = this; // Some choose `that` instead of `self`. // Choose one and be consistent. self.age = 0; setInterval(function growUp() { // The callback refers to the `self` variable of which // the value is the expected object. self.age++; }, 1000); }
另外,創(chuàng)建一個約束函數(shù)可以使得 this值被正確傳遞給 growUp() 函數(shù)。
箭頭函數(shù)捕捉閉包上下文的this值,所以下面的代碼工作正常。
function Person(){ this.age = 0; setInterval(() => { this.age++; // |this| properly refers to the person object }, 1000); } var p = new Person();預(yù)定義函數(shù)
JavaScript語言有好些個頂級的內(nèi)建函數(shù):
eval()
eval()方法會對一串字符串形式的JavaScript代碼字符求值。
uneval()
uneval()方法創(chuàng)建的一個Object的源代碼的字符串表示。
isFinite()
isFinite()函數(shù)判斷傳入的值是否是有限的數(shù)值。 如果需要的話,其參數(shù)首先被轉(zhuǎn)換為一個數(shù)值。
isNaN()
isNaN()函數(shù)判斷一個值是否是NaN。注意:isNaN函數(shù)內(nèi)部的強制轉(zhuǎn)換規(guī)則十分有趣; 另一個可供選擇的是ECMAScript 6 中定義Number.isNaN() , 或者使用 typeof來判斷數(shù)值類型。
parseFloat()
parseFloat() 函數(shù)解析字符串參數(shù),并返回一個浮點數(shù)。
parseInt()
parseInt() 函數(shù)解析字符串參數(shù),并返回指定的基數(shù)(基礎(chǔ)數(shù)學(xué)中的數(shù)制)的整數(shù)。
decodeURI()
decodeURI() 函數(shù)對先前經(jīng)過encodeURI函數(shù)或者其他類似方法編碼過的字符串進行解碼。
decodeURIComponent()
decodeURIComponent()方法對先前經(jīng)過encodeURIComponent函數(shù)或者其他類似方法編碼過的字符串進行解碼。
encodeURI()
encodeURI()方法通過用以一個,兩個,三個或四個轉(zhuǎn)義序列表示字符的UTF-8編碼替換統(tǒng)一資源標識符(URI)的某些字符來進行編碼(每個字符對應(yīng)四個轉(zhuǎn)義序列,這四個序列組了兩個”替代“字符)。
encodeURIComponent()
encodeURIComponent() 方法通過用以一個,兩個,三個或四個轉(zhuǎn)義序列表示字符的UTF-8編碼替換統(tǒng)一資源標識符(URI)的每個字符來進行編碼(每個字符對應(yīng)四個轉(zhuǎn)義序列,這四個序列組了兩個”替代“字符)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/110263.html
摘要:一個閉包就是當一個函數(shù)返回時,一個沒有釋放資源的棧區(qū)所以參數(shù)和變量不會被垃圾回收機制回收。使用不當會很容易造成內(nèi)存泄露。最后,垃圾回收器完成內(nèi)存清除工作,銷毀那些帶標記的值并回收它們所占用的內(nèi)存空間。 1.什么是閉包?閉包有啥特性以及存在什么問題? 概念:閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。下面的outer就形成了一個閉包: function outer(){ co...
摘要:語法和數(shù)據(jù)類型正文開始本章節(jié)復(fù)習(xí)的是中的基本語法,變量聲明,數(shù)據(jù)類型和字面量。聲明一個塊作用域的局部變量,可賦一個初始值。變量聲明有三種方式如,聲明局部變量和全局變量。 最近開始把精力放在重新復(fù)習(xí)JavaScript的基礎(chǔ)知識上面,不再太追求各種花枝招展的前端框架,框架再多,適合實際項目才是最重要。 上星期在掘金發(fā)布了幾篇文章,其中最大塊算是 【復(fù)習(xí)資料】ES6/ES7/ES8/ES...
摘要:前情提要本人是一個學(xué)渣非科班入行年了吧前端東西真的好多啊又不斷更新需要不斷的學(xué)學(xué)學(xué)在去年年底開始我就開始不斷的尋找學(xué)習(xí)的方法如何更加高效的學(xué)習(xí)如何才能學(xué)的又快又好在這半年來不斷的總結(jié)慢慢找到了一些方法和訣竅此文章不是網(wǎng)格布局的教學(xué)文章只前情提要 ??本人是一個學(xué)渣,非科班入行2年了吧,前端東西真的好多啊,又不斷更新.需要不斷的學(xué)學(xué)學(xué), showImg(https://user-gold-c...
閱讀 1075·2021-11-22 14:56
閱讀 1520·2019-08-30 15:55
閱讀 3358·2019-08-30 15:45
閱讀 1655·2019-08-30 13:03
閱讀 2868·2019-08-29 18:47
閱讀 3334·2019-08-29 11:09
閱讀 2641·2019-08-26 18:36
閱讀 2615·2019-08-26 13:55