Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
HTML是用來開發網頁的,它是開發網頁的語言
全稱HyperText Mark-up Language,超文本標記語言
標記就是標簽
<標簽名稱></標簽名稱> 比如 <html></html> <h1></h1>等,標簽大多數都是成對出現的。
超文本 兩層含義:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>網頁標題</title>
</head>
<body>
網頁顯示內容
</body>
</html>
第一行<!DOCTYPE html>是文檔聲明
用來指定頁面所使用的html的版本, 這里聲明的是一個html5的文檔
<html>...</html>標簽是開發人員在告訴瀏覽器
整個網頁是從<html>這里開始的,到</html>結束
也就是html文檔的開始和結束標簽
<head>...</head>標簽用于定義文檔的頭部
是負責對網頁進行設置標題、編碼格式以及引入css和js文件的
<body>...</body>標簽是編寫網頁上顯示的內容
網頁文件的后綴是.html, 一個html文件就是一個網頁,html文件用編輯器打開顯示的是文本,可以用文本的方式編輯它,如果用瀏覽器打開,瀏覽器會按照標簽描述內容將文件渲染成網頁
VS Code全拼是 Visual Studio Code 是由微軟研發的一款免費、開源的跨平臺代碼編輯器
目前是前端(網頁)開發使用最多的一款軟件開發工具
下載網址: https://code.visualstudio.com/Download
選擇對應的安裝包進行下載:
安裝一切默認
1 標簽不區分大小寫,但是推薦使用小寫
2 根據標簽的書寫形式,標簽分為雙標簽(閉合標簽)和單標簽(空標簽) 2.1 雙標簽是指由開始標簽和結束標簽組成的一對標簽,這種標簽允許嵌套和承載內容,比如: div標簽 2.2 單標簽是一個標簽組成,沒有標簽內容, 比如: img標簽
標簽的使用形式
列表標簽
網頁效果
表格標簽
<table>標簽:表示一個表格
<tr>標簽:表示表格中的一行
<td>標簽:表示表格中的列
<th>標簽:表示表格中的表頭
屬性設置
border: 1px solid black:設置邊框和顏色
border-collapse: collapse:設置邊框合并
網頁效果
表單標簽
表單用于搜集不同類型的用戶輸入的數據,然后可以把用戶數據提交到web服務器
<form>標簽 表示表單標簽,定義整體的表單區域
一個表單中有很多信息組成,比如 姓名,愛好,地址等,這些內容有很多其他標簽來承載
這些標簽稱為表單元素標簽
網頁效果
表單用于搜集不同類型的用戶輸入的數據,然后可以把用戶數據提交到web服務器
兩種方式的區別:
表單元素屬性設置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!--
姓名 type="text" 定義單行文本輸入框
密碼 type="password" 定義密碼輸入框
性別 type="radio" 定義單選框
愛好 type="checkbox" 定義復選框
照片 type="file" 定義上傳文件
個人描述 <textarea></textarea> 定義多行文本輸入框
地址 <select></select> 定義下拉列表
提交 type="submit" 定義提交按鈕
重置 type="reset" 定義重置按鈕
按鈕 type="button" 定義一個普通按鈕
-->
<form action="http://192.168.1.106:8080" method="POST">
<label>姓名:</label>
<input type="text" name="username" >
<br>
<label>密碼:</label>
<input type="password" name="password">
<br>
<label>性別:</label>
<input type="radio" name="sex" value="1">男
<input type="radio" name="sex" value="0">女
<br>
<label>愛好:</label>
<input type="checkbox" name="like" value="睡覺">睡覺
<input type="checkbox" name="like" value="吃飯">吃飯
<input type="checkbox" name="like" value="打豆豆">打豆豆
<br>
<label>照片:</label>
<input type="file" name="pic">
<br>
<label>個人描述:</label>
<textarea name="desc"></textarea>
<br>
<label>地址:</label>
<select name="addr">
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">廣州</option>
<option value="4">深圳</option>
</select>
<br>
<input type="submit" value="提交">
<input type="reset" value="重置">
<input type="button" value="按鈕">
</form>
</body>
</html>
點擊提交:
可以看到服務器收到了請求報文。
擊標題下「異步社區」可快速關注
本文包括以下內容:
理解函數為何如此重要
函數為何是第一類對象
定義函數的方式
參數賦值之謎
在本文這一部分討論JavaScript基礎時,也許你會感到驚訝,我們的第一個論點是函數(function)而非對象(object)。當然,第3部分會用大量筆墨解釋對象,但歸根結底,你要理解一些基本事實,像普通人一樣編寫代碼和像“忍者”一樣編寫代碼的最大差別在于是否把JavaScript作為函數式語言(functional language)來理解。對這一點的認知水平決定了你編寫的代碼水平。
如果你正在閱讀這本文,那么你應該不是一位初學者。對于后續內容,我們假設你已經足夠了解面向對象基礎(當然,我們會在以后章節詳細討論對象的高級概念),但真正理解JavaScript中的函數才是你能使用的唯一一件重要武器。函數是如此重要,所以本文及接下來兩章將帶領你徹底理解JavaScript中的函數。
JavaScript中最關鍵的概念是:函數是第一類對象(first-class objects),或者說它們被稱作一等公民(first-class citizens)。函數與對象共存,函數也可以被視為其他任意類型的JavaScript對象。函數和那些更普通的JavaScript數據類型一樣,它能被變量引用,能以字面量形式聲明,甚至能被作為函數參數進行傳遞。本文一開始會介紹面向函數編程帶來的差異,你會發現,在需要調用某函數的位置定義該函數,能讓我們編寫更緊湊、更易懂的代碼。其次,我們還會探索如何把函數用作第一類對象來編寫高性能函數。你能學到多種不同的函數定義方式,甚至包括一些新類型,例如箭頭(arrow)函數,它能幫你編寫更優雅的代碼。最后,我們會學習函數形參和函數實參的區別,并重點關注ES6的新增特性,例如剩余參數和默認參數。
讓我們通過了解函數式編程的優點來開始學習吧。
你知道嗎?
回調函數在哪種情況下會同步調用,或者異步調用呢?
箭頭函數和函數表達式的區別是什么?
你為什么需要在函數中使用默認參數?
1.1 函數式的不同點到底是什么
函數及函數式概念之所以如此重要,其原因之一在于函數是程序執行過程中的主要模塊單元。除了全局JavaScript代碼是在頁面構建的階段執行的,我們編寫的所有的腳本代碼都將在一個函數內執行。
由于我們的大多數代碼會作為函數調用來執行,因此,我們在編寫代碼時,通用強大的構造器能賦予代碼很大的靈活性和控制力。本文的大部分內容解釋了如何利用函數作為第一類對象的特性獲益。首先瀏覽一下對象中我們能使用的功能。JavaScript中對象有以下幾種常用功能。
對象可通過字面量來創建{}。
對象可以賦值給變量、數組項,或其他對象的屬性。
1var ninja = {}; ?--- 為變量賦值一個新對象
2ninjaArray.push({}); ?--- 向數組中增加一個新對象
3ninja.data = {}; ?--- 給某個對象的屬性賦值為一個新對象
對象可以作為參數傳遞給函數。
1function hide(ninja){
2 ninja.visibility = false;
3}
4hide({}); ?--- 一個新創建的對象作為參數傳遞給函數
對象可以作為函數的返回值。
1function returnNewNinja() {
2 return {}; ?--- 從函數中返回了一個新對象
3}
對象能夠具有動態創建和分配的屬性。
1var ninja = {};
2ninja.name = "Hanzo"; ?--- 為對象分配一個新屬性
其實,不同于很多其他編程語言,在JavaScript中,我們幾乎能夠用函數來實現同樣的事。
1.1.1 函數是第一類對象
JavaScript中函數擁有對象的所有能力,也因此函數可被作為任意其他類型對象來對待。當我們說函數是第一類對象的時候,就是說函數也能夠實現以下功能。
通過字面量創建。
1function ninjaFunction() {}
賦值給變量,數組項或其他對象的屬性。
1var ninjaFunction = function() {}; ?--- 為變量賦值一個新函數
2ninjaArray.push(function(){}); ?--- 向數組中增加一個新函數
3ninja.data = function(){}; ?--- 給某個對象的屬性賦值為一個新函數
作為函數的參數來傳遞。
1function call(ninjaFunction){
2 ninjaFunction();
3}
4call(function(){}); ?--- 一個新函數作為參數傳遞給函數
作為函數的返回值。
1function returnNewNinjaFunction() {
2 return function(){}; ?--- 返回一個新函數
3}
具有動態創建和分配的屬性。
1var ninjaFunction = function(){};
2ninjaFunction.ninja = "Hanzo"; ?--- 為函數增加一個新屬性
對象能做的任何一件事,函數也都能做。函數也是對象,唯一的特殊之處在于它是可調用的(invokable),即函數會被調用以便執行某項動作。
{JavaScript中的函數式編程!}
把函數作為第一類對象是函數式編程(functional programming)的第一步,函數式編程是一種編程風格,它通過書寫函數式(而不是指定一系列執行步驟,就像那種更主流的命令式編程)代碼來解決問題。函數式編程可以讓代碼更容易測試、擴展及模塊化。不過這是一個很大的話題,因此本文僅對這個特性做了肯定。如果你對如何在JavacScript中利用函數式編程感興趣,推薦閱讀Luis Atencio著(由Manning出版社2016年出版)的《JavaScript函數式編程》,購買方式見www.manning.com/ books/functional-programming- in-JavaScript。
第一類對象的特點之一是,它能夠作為參數傳入函數。對于函數而言,這項特性也表明:如果我們將某個函數作為參數傳入另一個函數,傳入函數會在應用程序執行的未來某個時間點才執行。大家所知道的更一般的概念是回調函數(callback function)。下面我們來學習這個重要概念。
1.1.2 回調函數
每當我們建立了一個將在隨后調用的函數時,無論是在事件處理階段通過瀏覽器還是通過其他代碼,我們都是在建立一個回調(callback)。這個術語源自于這樣一個事實,即在執行過程中,我們建立的函數會被其他函數在稍后的某個合適時間點“再回來調用”。
有效運用JavaScript的關鍵在于回調函數,相信你已經在代碼中使用了很多回調函數——不論是單擊一次按鈕、從服務端接收數據,還是UI動畫的一部分。
本節我們會看一些實際使用回調函數的典型例子,例如處理事件、簡單的排序集合。這部分內容會有點復雜,所以在深入學習之前,先透徹、完整地理解回調函數的概念,用最簡單的形式來展現它。下面我們用一個簡單例子來闡明這個概念,此例中的函數完全沒什么實際用處,它的參數接收另一個函數的引用,并作為回調調用該函數:
1function useless(ninjaCallback) {
2 return ninjaCallback();
3}
這個函數可能沒什么用,但它反映了函數的一種能力,即將函數作為另一個函數的參數,隨后通過參數來調用該函數。
我們可以在清單1.1中測試一下這個名為useless的函數。
清單1.1 簡單的回調函數例子
1var text = "Domo arigato!";
2report("Before defining functions");
3function useless(ninjaCallback) {
4 report("In useless function");
5 return ninjaCallback();
6} ?--- 函數定義,參數為一個回調函數,其函數體內會立即調用該回調函數
7function getText() {
8 report("In getText function");
9 return text;
10} ?--- 簡單的函數定義,僅返回一個全局變量
11report("Before making all the calls");
12assert(useless(getText) === text,
13 "The useless function works! " + text); ?--- 把gerText作為回調函數傳入上面的useless函數
14report("After the calls have been made");
在這個代碼清單中,我們使用自定義函數report(在本文附錄B中定義)來輸出代碼執行過程中的信息,這樣一來我們就能通過這些信息來跟蹤程序的執行過程。我們還使用了第1章中的斷言函數assert。該函數通常使用兩個參數。第一個參數是用于斷言的表達式。本例中,我們需要確定使用參數getText調用useless函數返回的值與變量text是否相等(useless(getText) === text)。若第一個參數的執行結果為true,斷言通過;反之,斷言失敗。第二個參數是與斷言相關聯的信息,通常會根據通過/失敗來輸出到日志上。(附錄B中概括地探討了測試,以及我們對assert函數和report函數的簡單實現)。
這段代碼執行完畢后,執行結果如圖1.1所示。可以看到,使用getText參數調用useless回調函數后,得到了期望的返回值。
圖1.1 清單1.1中代碼的執行結果
我們還可以看看這個簡單的回調函數具體是如何執行的。如圖1.2所示,getText函數作為參數傳入了useless函數。從該圖中可以看到,在useless函數體內,通過callback參數可以取得getText函數的引用。隨后,回調函數callback()的調用讓getText函數得到執行,而我們作為參數傳入的getText函數則通過useless函數被回調。
圖1.2 執行useless(getText)調用后的執行流。getText作為參數傳入useless函數并調用。useless函數體內對傳入函數進行調用,本例中觸發了getText函數的執行(即我們對getText函數進行回調)。
完成這個過程是很容易的,原因就在于JavaScript的函數式本質讓我們能把函數作為第一類對象。更進一步說,我們的代碼可以寫成如下形式:
1<pre class="代碼無行號"><code>var text = 'Domo arigato!';
2function useless(ninjaCallback) {
3 return ninjaCallback();
4}
5assert(useless(<strong>function () { return text;}</strong>) === text, ?--- 直接以參數形式定義回調函數
6 "The useless function works! " + text); </code></pre>
JavaScript的重要特征之一是可以在表達式出現的任意位置創建函數,除此之外這種方式能使代碼更緊湊和易于理解(把函數定義放在函數使用處附近)。當一個函數不會在代碼的多處位置被調用時,該特性可以避免用非必須的名字污染全局命名空間。
在回調函數的前述例子中,我們調用的是我們自己的回調。除此之外瀏覽器也會調用回調函數,回想一下第2章中的下述例子:
1document.body.addEventListener("mousemove", function() {
2
3 var second = document.getElementById("second")
4;
5 addMessage(second, "Event: mousemove"
6);
7});
上例同樣是一個回調函數,作為mousemove事件的事件處理器,當事件發生時,會被瀏覽器調用。
{注意 }
本小節介紹的回調函數是其他代碼會在隨后的某個合適時間點“回過來調用”的函數。你已經學習了我們自己的代碼調用回調(useless函數例子),也看到了當某事件發生時瀏覽器發起調用(mousemove例子)。注意這些很重要,不同于我們的例子,一些人認為回調會被異步調用,因此第一個例子不是一個真正的回調。這里之所以提到這些是以防萬一你偶爾會遇見這類激烈的爭論。
現在讓我們看一個回調函數的用法,它能極大地簡化集合的排序。
使用比較器排序
一般情況下只要我們拿到了一組數據集,就很可能需要對它進行排序。假如有一組隨機序列的數字數組:0, 3, 2, 5, 7, 4, 8, 1。也許這個順序沒什么問題,但很可能早晚需要重新排列它。
通常來說,實現排序算法并不是編程任務中最微不足道的;我們需要為手中的工作選擇最佳算法,實現它以適應當前的需要(使這些選項是按照特定順序排列),并且需要小心仔細不能引入故障。除此之外,唯一特定于應用程序的任務是排列順序。幸運的是,所有的JavaScript數組都能用sort方法。利用該方法可以只定義一個比較算法,比較算法用于指示按什么順序排列。
這才是回調函數所要介入的!不同于讓排序算法來決定哪個值在前哪個值在后,我們將會提供一個函數來執行比較。我們會讓排序算法能夠獲取這個比較函數作為回調,使算法在其需要比較的時候,每次都能夠調用回調。該回調函數的期望返回值為:如果傳入值的順序需要被調換,返回正數;不需要調換,返回負數;兩個值相等,返回0。對于排序上述數組,我們對比較值做減法就能得到我們所需要的值。
1<pre class="代碼無行號"><code>var values = [0, 3, 2, 5, 7, 4, 8, 1];
2values.sort<strong>(function(value1, value2) {</strong>
3 return value1 - value2;
4<strong>}</strong>);</code></pre>
沒有必要思考排序算法的底層細節(甚至是選擇了什么算法)。JavaScript引擎每次需要比較兩個值的時候都會調用我們提供的回調函數。
函數式方式讓我們能把函數作為一個單獨實體來創建,正像我們對待其他類型一樣,創建它、作為參數傳入一個方法并將它作為一個參數來接收。函數就這樣顯示了它一等公民的地位。
1.2 函數作為對象的樂趣
本節我們會考察函數和其他對象類型的相似之處。也許讓你感到驚訝的相似之處在于我們可以給函數添加屬性:
1var ninja = {};
2ninja.name = "hitsuke"; ?--- 創建新對象并為其分配一個新屬性
3var wieldSword = function(){};
4wieldSword.swordType = "katana"; ?--- 創建新函數并為其分配一個新屬性
我們再來看看這種特性所能做的更有趣的事:
在集合中存儲函數使我們輕易管理相關聯的函數。例如,某些特定情況下必須調用的回調函數。
記憶讓函數能記住上次計算得到的值,從而提高后續調用的性能。
讓我們行動起來吧。
1.2.1 存儲函數
某些例子中(例如,我們需要管理某個事件發生后需要調用的回調函數集合),我們會存儲元素唯一的函數集合。當我們向這樣的集合中添加函數時,會面臨兩個問題:哪個函數對于這個集合來說是一個新函數,從而需要被加入到該集合中?又是哪個函數已經存在于集合中,從而不需要再次加入到集合中?一般來說,管理回調函數集合時,我們并不希望存在重復函數,否則一個事件會導致同一個回調函數被多次調用。
一種顯著有效的簡單方法是把所有函數存入一個數組,通過循環該數組來檢查重復函數。令人遺憾的是,這種方法的性能較差,尤其作為一個“忍者”要把事情干得漂亮而不僅是做到能用。我們可以使用函數的屬性,用適當的復雜度來實現它,如清單1.2所示。
清單1.2 存儲唯一函數集合
1var store = {
2 nextId: 1, ?--- 跟蹤下一個要被復制的函數
3 cache: {}, ?--- 使用一個對象作為緩存,我們可以在其中存儲函數
4 add: function(fn) {
5 if (!fn.id) {
6 fn.id = this.nextId++;
7 this.cache[fn.id] = fn;
8 return true;
9 }
10 } ?--- 僅當函數唯一時,將該函數加入緩存
11};
12function ninja(){}
13assert(store.add(ninja),
14 "Function was safely added.");
15assert(!store.add(ninja),
16 "But it was only added once."); ?--- 測試上面代碼按預期工作
在這個清單中,我們創建了一個對象賦值給變量store,這個變量中存儲的是唯一的函數集合。這個對象有兩個數據屬性:其一是下一個可用的id,另外一個緩存著已經保存的函數。函數通過add()方法添加到緩存中。
1add: function(fn) {
2 if (!fn.id) {
3 fn.id = this.nextId++;
4 this.cache[fn.id] = fn;
5 return true;
6 }
7...
在add函數內,我們首先檢查該函數是否已經存在id屬性。如果當前的函數已經有id屬性,我們則假設該函數已經被處理過了,從而忽略該函數,否則為該函數分配一個id(同時增加nextId)屬性,并將該函數作為一個屬性增加到cache上,id作為屬性名。緊接著該函數的返回值為true,從而可得知調用了add()后,函數是什么時候被添加到存儲中的。
在瀏覽器中運行該程序后,頁面顯示:測試程序嘗試兩次添加ninja()函數,而該函數只被添加一次到存儲中,如圖1.3所示。第9章展示了用于操作合集的更好技術,它利用了ES6的新的對象類型集合(Set)。
圖1.3 給函數附加一個屬性后,我們就能夠引用該屬性。本例通過這種方式可以確保該ninja函數僅被添加到函數中一次
另外一種有用的技巧是當使用函數屬性時,可以通過該屬性修改函數自身。這個技術可以用于記憶前一個計算得到的值,為之后計算節省時間。
1.2.2 自記憶函數
如同前面所提到的,記憶化(memoization)是一種構建函數的處理過程,能夠記住上次計算結果。在這個果殼里,當函數計算得到結果時就將該結果按照參數存儲起來。采用這種方式時,如果另外一個調用也使用相同的參數,我們則可以直接返回上次存儲的結果而不是再計算一遍。像這樣避免既重復又復雜的計算可以顯著地提高性能。對于動畫中的計算、搜索不經常變化的數據或任何耗時的數學計算來說,記憶化這種方式是十分有用的。
看看下面的這個例子,它使用了一個簡單的(也的確是效率不高的)算法來計算素數。盡管這是一個復雜計算的簡單例子,但它經常被應用到大計算量的場景中(例如可以引申到通過字符串生成MD5算法),這里不便展示。
從外表來說,這個函數和任何普通函數一樣,但在內部我們會構建一個結果緩存,它會保存函數每次計算得到的結果,如清單1.3所示。
清單1.3 計算先前得到的值
1function isPrime(value) {
2 if (!isPrime.answers) {
3 isPrime.answers = {};
4 } ?--- 創建緩存
5 if (isPrime.answers[value] !== undefined) {
6 return isPrime.answers[value];
7 } ?--- 檢查緩存的值
8 var prime = value !== 0 && value !== 1; // 1 is not a prime
9 for (var i = 2; i < value; i++) {
10 if (value % i === 0) {
11 prime = false;
12 break;
13 }
14 }
15 return isPrime.answers[value] = prime; ?--- 存儲計算的值
16}
17assert(isPrime(5), "5 is prime!");
18assert(isPrime.answers[5], "The answer was cached!"); ?--- 測試該函數是否正常工作
在isPrime函數中,首先通過檢查它的answers屬性來確認是否已經創建了一個緩存,如果沒有創建,則新建一個:
1if (!isPrime.answers) {
2 isPrime.answers = {};
3}
只有第一次函數調用才會創建這個初始空對象,之后這個緩存就已經存在了。然后我們會檢查參數中傳的值是否已經存儲到緩存中:
1if (isPrime.answers[value] !== undefined) {
2 return isPrime.answers[value];
3}
這個緩存會針對參數中的值value來存儲該值是否為素數(true或false)。如果我們在緩存中找到該值,函數會直接返回。
1return isPrime.answers[value] = prime;
這個緩存是函數自身的一個屬性,所以只要該函數還存在,緩存也就存在。
最后的測試結果可以看到記憶函數生效了。
1assert(isPrime(5), "5 is prime!");
2assert(isPrime.answers[5], "The answer was cached!");
這個方法具有兩個優點。
由于函數調用時會尋找之前調用所得到的值,所以用戶最終會樂于看到所獲得的性能收益。
它幾乎是無縫地發生在后臺,最終用戶和頁面作者都不需要執行任何特殊請求,也不需要做任何額外初始化,就能順利進行工作。
當然這種方法并不是像玫瑰和提琴一樣完美,還是要權衡利弊。
任何類型的緩存都必然會為性能犧牲內存。
純粹主義者會認為緩存邏輯不應該和業務邏輯混合,函數或方法只需要把一件事做好。但不必擔心,在第8章你會了解到如何解決這類問題。
對于這類問題很難做負載測試或估計算法復雜度,因為結果依賴于函數之前的輸入。
現在你看到了函數作為第一類公民的一些實例,接下來看看不同的函數定義的方式。
1.3 函數定義
JavaScript函數通常由函數字面量(function literal)來創建函數值,就像數字字面量創建一個數字值一樣。要記住這一點,作為第一類對象,函數是可以用在編程語言中的值,就像例句字符串或數字的值。無論你是否意識到了這一點,你一直都是這樣做的。
JavaScript提供了幾種定義函數的方式,可以分為4類。
函數定義(function declarations)和函數表達式(function expressions)——最常用,在定義函數上卻有微妙不同的的兩種方式。人們通常不會獨立地看待它們,但正如你將看到的,意識到兩者的不同能幫我們理解函數何時能夠被調用。
1function myFun(){ return 1;}
箭頭函數(通常被叫做lambda函數)——ES6新增的JavaScript標準,能讓我們以盡量簡潔的語法定義函數。
1myArg => myArg*2
函數構造函數—— 一種不常使用的函數定義方式,能讓我們以字符串形式動態構造一個函數,這樣得到的函數是動態生成的。這個例子動態地創建了一個函數,其參數為a和b,返回值為兩個數的和。
1new Function('a', 'b', 'return a + b')
生成器函數——ES6新增功能,能讓我們創建不同于普通函數的函數,在應用程序執行過程中,這種函數能夠退出再重新進入,在這些再進入之間保留函數內變量的值。我們可以定義生成器版本的函數聲明、函數表達式、函數構造函數。
1function* myGen(){ yield 1; }
理解這幾種方式的不同很重要,因為函數創建的方式很大程度地影響了函數可被調用的時間、函數的行為以及函數可以在哪個對象上被調用。
這一節中,我們將會探索函數定義、函數表達式和箭頭函數。你將學到它們的語法和它們的工作方式,我們也將會在本文中多次回顧它們的細節。另一方面,生成器函數則有一點獨特,它不同于普通函數。在第6章我們會再來學習它們的細節。
剩下的JavaScript特性——函數構造函數我們將全部跳過。盡管它具有某些有趣的應用場景,尤其是在動態創建和執行代碼時,但我們依然認為它是JavaScript語言的邊緣功能。如果你想知道更多關于函數構造函數的信息,請訪問http://mng.bz/ZN8e。
讓我們先用最簡單、最傳統的方式定義函數吧:函數聲明和函數表達式。
1.3.1 函數聲明和函數表達式
JavaScript中定義函數最常用的方式是函數聲明和函數表達式。這兩種技術非常相似,有時甚至難以區分,但在后續章節中你將看到,它們之間還是存在著微妙的差別。
函數聲明
JavaScript定義函數的最基本方式是函數聲明(見圖1.4)。正如你所見,每個函數聲明以強制性的function開頭,其后緊接著強制性的函數名,以及括號和括號內一列以逗號分隔的可選參數名。函數體是一列可以為空的表達式,這些表達式必須包含在花括號內。除了這種形式以外,每個函數聲明還必須包含一個條件:作為一個單獨的JavaScript語句,函數聲明必須獨立(但也能夠被包含在其他函數或代碼塊中,在下一小節中你將會準確理解其含義)。
圖1.4 函數聲明是獨立的,是獨立的JavaScript代碼塊(它可以被包含在其他函數中)
清單1.4展示了兩條函數聲明例子。
清單1.4 函數聲明示例
1function samurai() {
2 return "samurai here"; ?--- 在全局代碼中定義samurai函數
3}
4function ninja() { ?--- 在全局代碼中定義ninja函數
5 function hiddenNinja() {
6 return "ninja here";
7 } ?--- 在ninja函數內定義hiddenNinja函數
8 return hiddenNinja();
9}
如果你對函數式語言沒有太多了解,仔細看一看,你可能會發現你并不習慣這種使用方式: 一個函數被定義在另一個函數之中!
1function ninja() {
2 function hiddenNinja() {
3 return "ninja here";
4 }
5 return hiddenNinja();
6}
在JavaScript中,這是一種非常通用的使用方式,這里用它作為例子是為了再次強調JavaScript中函數的重要性。
{注意 }
讓函數包含在另一個函數中可能會因為忽略作用域的標識符解析而引發一些有趣的問題,但現在可以先留下這個問題,第5章會重新回顧這個問題的細節。
函數表達式
正如我們多次所提到的,JavaScript中的函數是第一類對象,除此以外也就意味著它們可以通過字面量創建,可以賦值給變量和屬性,可以作為傳遞給其他函數的參數或函數的返回值。正因為函數有如此的基礎結構,所以JavaScript能讓我們把函數和其他表達式同等看待。例如,如下例子中我們可以使用數字字面量:
1var a = 3;
2myFunction(4);
同樣,在相同位置可以用函數字面量:
1var a = function() {};
2myFunction(function(){});
這種總是其他表達式的一部分的函數(作為賦值表達式的右值,或者作為其他函數的參數)叫作函數表達式。函數表達式非常重要,在于它能準確地在我們需要使用的地方定義函數,這個過程能讓代碼易于理解。清單1.5展示了函數聲明和函數表達式的不同之處。
清單1.5 函數聲明和函數表達式
1<pre class="代碼無行號"><code>function myFunctionDeclaration(){ ?--- 獨立的函數聲明
2 function innerFunction() {} ?--- 內部函數聲明
3}
4var myFunc = function(){}; ?--- 函數表達式作為變量聲明賦值語句中的一部分
5myFunc(function(){ ?--- 函數表達式作為一次函數調用中的參數
6 return function(){}; ?--- 函數表達式作為函數返回值
7});
8(function <strong>namedFunctionExpression</strong> () {
9})(); ?--- 作為函數調用的一部分,命名函數表達式會被立即調用
10+function(){}();
11-function(){}();
12!function(){}();
13~function(){}(); ?--- 函數有達式可以作為一元操作符的參數立即調用</code></pre>
示例代碼的開頭是標準函數聲明,其包含一個內部函數聲明:
1function myFunctionDeclaration(){
2 function innerFunction() {}
3}
從這個示例中你能夠看到,函數聲明是如何作為JavaScript代碼中的獨立表達式的,但它也能夠包含在其他函數體內。與之比較的是函數表達式,它通常作為其他語句的一部分。它們被放在表達式級別,作為變量聲明(或者賦值)的右值:
1var myFunc = function(){};
或者作為另一個函數調用的參數或返回值。
1myFunc(function() {
2 return function(){};
3});
函數聲明和函數表達式除了在代碼中的位置不同以外,還有一個更重要的不同點是:對于函數聲明來說,函數名是強制性的,而對于函數表達式來說,函數名則完全是可選的。
函數聲明必須具有函數名是因為它們是獨立語句。一個函數的基本要求是它應該能夠被調用,所以它必須具有一種被引用方式,于是唯一的方式就是通過它的名字。
從另一方面來看,函數表達式也是其他JavaScript表達式的一部分,所以我們也就具有了調用它們的替代方案。例如,如果一個函數表達式被賦值給了一個變量,我們可以用該變量來調用函數。
1var doNothing = function(){};
2doNothing();
或者,如果它是另外一個函數的參數,我們可以在該函數中通過相應的參數名來調用它。
1function doSomething(action) {
2 action();
3}
立即函數
函數表達式可以放在初看起來有些奇怪的位置上,例如通常認為是函數標識符的位置。接下來仔細看看這個構造(如圖1.5所示)。
圖1.5 標準函數的調用和函數表達式的立即調用的對比
當想進行函數調用時,我們需要使用能夠求值得到函數的表達式,其后跟著一對函數調用括號,括號內包含參數。在最基本的函數調用中,我們把求值得到函數的標識符作為左值(如圖1.5所示)。不過用于被括號調用的表達式不必只是一個簡單的標識符,它可以是任何能夠求值得到函數的表達式。例如,指定一個求值得到函數的表達式的最簡單方式是使用函數表達式。如圖1.5中右圖所示,我們首先創建了一個函數,然后立即調用這個新創建的函數。這種函數叫作立即調用函數表達式(IIFE),或者簡寫為立即函數。這一特性能夠模擬JavaScript中的模塊化,故可以說它是JavaScript開發中的重要理念。第11章中會集中討論IIFE的應用。
{加括號的函數表達式!}
還有一件可能困擾你的是上面例子中我們立即調用的函數表達式方式:函數表達式被包裹在一對括號內。為什么這樣做呢?其原因是純語法層面的。JavaScript解析器必須能夠輕易區分函數聲明和函數表達式之間的區別。如果去掉包裹函數表達式的括號,把立即調用作為一個獨立語句function() {}(3),JavaScript開始解析時便會結束,因為這個獨立語句以function開頭,那么解析器就會認為它在處理一個函數聲明。每個函數聲明必須有一個名字(然而這里并沒有指定名字),所以程序執行到這里會報錯。為了避免錯誤,函數表達式要放在括號內,為JavaScript解析器指明它正在處理一個函數表達式而不是語句。
還有一種相對簡單的替代方案(function(){}(3))也能達到相同目標(然而這種方案有些奇怪,故不常使用)。把立即函數的定義和調用都放在括號內,同樣可以為JavaScript解析器指明它正在處理函數表達式。
表1.5中最后4個表達式都是立即調用函數表達式主題的4個不同版本,在JavaScript庫中會經常見到這幾種形式:
1+function(){}();
2-function(){}();
3!function(){}();
4~function(){}();
不同于用加括號的方式區分函數表達式和函數聲明,這里我們使用一元操作符+、-、!和~。這種做法也是用于向JavaScript引擎指明它處理的是表達式,而不是語句。從計算機的角度來講,注意應用一元操作符得到的結果沒有存儲到任何地方并不重要,只有調用IIFE才重要。現在我們已經學會了JavaScript中兩種基本的函數定義方式(函數聲明和函數表達式)的細節。接下來開始探索JavaScript標準中的新增特性:箭頭函數。
1.3.2 箭頭函數
注意:
箭頭函數是JavaScript標準中的ES6新增項(瀏覽器兼容性可參考http://mng.bz/8bnH)。
由于JavaScript中會使用大量函數,增加簡化創建函數方式的語法十分有意義,也能讓我們的開發者生活更愉快。在很多方式中,箭頭函數是函數表達式的簡化版。一起來回顧一下本文開始的排序例子。
1var values = [0, 3, 2, 5, 7, 4, 8, 1];
2values.sort(function(value1,value2){
3 return value1 – value2;
4});
這個例子中,數組對象的排序方法的參數傳入了一個回調函數表達式,JavaScript引擎會調用這個回調函數以降序排序數組。現在來看看如何用箭頭函數來做完全相同的工作:
1var values = [0, 3, 2, 5, 7, 4, 8, 1];
2
3values.sort((
4
5value1,value2) => value1 – value2
6
7);
看到這是多么簡潔了吧?
這種寫法不會產生任何因為書寫function關鍵字、大括號或者return語句導致的混亂。箭頭函數語句有著比函數表達式更為簡單的方式:函數傳入兩個參數并返回其差值。注意這個新操作符——胖箭頭符號=>(等號后面跟著大于號)是定義箭頭函數的核心。
現在來解析箭頭函數的語法,首先看看它的最簡形式:
1param => expression
這個箭頭函數接收一個參數并返回表達式的值,如下面的清單1.6就使用了這種語法。
清單1.6 比較箭頭函數和函數表達式
1var greet = name => "Greetings " + name; ?--- 定義箭頭函數
2
3assert(greet("Oishi") === "Greetings Oishi", "Oishi is properly greeted")
4;
5
6var anotherGreet = function(nam
7e){
8 return "Greetings " + n
9ame;
10}; ?--- 定義
11函數表達式
12assert(anotherGreet("Oishi") === "Greetings O
13ishi",
14 "Again, Oishi is properly greeted");
稍作欣賞,使用箭頭函數的代碼即簡潔又清楚。這是箭頭函數的最簡語法,但一般情況下,箭頭函數會被定義成兩種方式,如圖1.6所示。
稍作欣賞,使用箭頭函數的代碼即簡潔又清楚。這是箭頭函數的最簡語法,但一般情況下,箭頭函數會被定義成兩種方式,如圖1.6所示。
圖1.6 箭頭函數的語法
如你所見,箭頭函數的定義以一串可選參數名列表開頭,參數名以逗號分隔。如果沒有參數或者多余一個參數時,參數列表就必須包裹在括號內。但如果只有一個參數時,括號就不是必須的。參數列表之后必須跟著一個胖箭頭符號,以此向我們和JavaScript引擎指示當前處理的是箭頭函數。
胖箭頭操作符后面有兩種可選方式。如果要創建一個簡單函數,那么可以把表達式放在這里(可以是數學運算、其他的函數調用等),則該函數的返回值即為此表達式的返回值。例如,第一個箭頭函數的示例如下:
1var greet = name => "Greetings " + name;
這個箭頭函數的返回值是字符串“Greetings”和參數name的結合。在其他案例中,當箭頭函數沒那么簡單從而需要更多代碼時,箭頭操作符后則可以跟一個代碼塊,例如:
1var greet = name => {
2 var helloString = 'Greetings ';
3 return helloString + name;
4};
這段代碼中箭頭函數的返回值和普通函數一樣。如果沒有return語句,返回值是undefined;反之,返回值就是return表達式的值。
在本文中我們會多次回顧箭頭函數。除此之外,我們還會展示箭頭函數的一些額外功能,它能幫助我們規避一些在很多標準函數中可能遇到的難以捉摸的缺陷。箭頭函數和很多其他函數一樣,可以通過接收參數來執行任務。接下來看看當向函數內傳入參數后,該參數值發生了什么。
本文摘自《JavaScript忍者秘籍(第2版)》
《JavaScript忍者秘籍 第2版》
[美] John,Resig(萊西格),Bear,Bibeault(貝比奧特),Josip ... 著
點擊封面購買紙書
JavaScript 正以驚人的速度成為各種應用程序的通用語言,包括 Web、桌面、云和移動設備上的應用程序。當成為 JavaScript 專業開發者時,你將擁有可應用于所有這些領域的、強大的技能集。
《JavaScript 忍者秘籍(第2版)》使用實際的案例清晰地詮釋每一個核心概念和技術。本書向讀者介紹了如何掌握 JavaScript 核心的概念,諸如函數、閉包、對象、原型和 promise,同時還介紹了 JavaScript API, 包括 DOM、事件和計時器。你將學會測試、跨瀏覽器開發,所有這些都是高級JavaScript開發者應該掌握的技能。
小福利
關注【異步社區】服務號,轉發本文至朋友圈或 50 人以上微信群,截圖發送至異步社區服務號后臺,并在文章底下留言,分享你的JavaScript開發經驗或者本書的試讀體驗,我們將選出3名讀者贈送《JavaScript 忍者秘籍(第2版)》1本,趕快積極參與吧!
活動截止時間:2018 年3月15日
在“異步社區”后臺回復“關注”,即可免費獲得2000門在線視頻課程;推薦朋友關注根據提示獲取贈書鏈接,免費得異步圖書一本。趕緊來參加哦!
掃一掃上方二維碼,回復“關注”參與活動!
閱讀原文
TML: HyperText Markup Language 超文本標記語言
HTML代碼不區分大小寫, 包括HTML標記、屬性、屬性值都不區分大小寫;
任何空格或回車鍵在代碼中都無效,插入空格或回車有專用的標記,分別是 、<br>
HTML標記中不要有空格,否則瀏覽器可能無法識別。
如何添加注釋(comment:評論;注釋)
<!-- -->
<comment></comment>
<!-- --> 不能留有空格
字符集
<meta http-equiv="Content-Type" content="text/html;charset=#"/>
<base target="_blank">
可以將a鏈接的默認屬性設置為_blank屬性
單個標簽要有最好有結束符(可以沒有結束符)
<br/> <img src="" width="" />
便于兼容XHTML(XHTML必須要有結束符)
HTML標簽的屬性值可以有引號,可以沒有引號,為了提高代碼的可讀性,推薦使用引號(單引號和雙引號),盡管屬性值是整數,也推薦加上引號。
<marquee behavior="slide"></marquee>
便于兼容XHTML(XHTML必須要有引號)
<marquee behavior=slide></marquee>
經過測試,以上程序都可以正確運行
HTML標簽涉及到的顏色值格式:
color_name 規定顏色值為顏色名稱的文本顏色(比如 "red")。
hex_number 規定顏色值為十六進制值的文本顏色(比如 "#ff0000")。
rgb_number 規定顏色值為 rgb 代碼的文本顏色(比如 "rgb(255,0,0)")。
transparent 透明色 color:transparent
rgba(紅0-255,綠0-255,藍0-255,透明度0-1)
opacity屬性: 就是葫蘆娃兄弟老六(技能包隱身)
css:
div{opacity:0.1} /*取值為0-1*/
英文(顏色值)不區分大小寫
HTML中顏色值:采用十六進制兼容性最好(十六進制顯示顏色效果最佳)
CSS中顏色值:不存在兼容性
紅色 #FF0000
綠色 #00FF00
藍色 #0000FF
黑色: #000000
灰色 #CCCCCC
白色 #FFFFFF
青色 #00FFFF
洋紅 #FF00FF
黃色 #FFFF00
請問后綴 html 和 htm 有什么區別?
答: 1. 如果一個網站有 index.html和index.htm,默認情況下,優先訪問.html
2. htm后綴是為了兼容以前的DOS系統8.3的命名規范
XHTML與HTML之間的關系?
XHTML是EXtensible HyperText Markup Language的英文縮寫,即可擴展的超文本標記語言.
XHTML語言是一種標記語言,它不需要編譯,可以直接由瀏覽器執行.
XHTML是用來代替HTML的, 是2000年w3c公布發行的.
XHTML是一種增強了的HTML,它的可擴展性和靈活性將適應未來網絡應用更多的需求.
XHTML是基于XML的應用.
XHTML更簡潔更嚴謹.
XHTML也可以說就是HTML一個升級版本.(w3c描述它為'HTML 4.01')
XHTML是大小寫敏感的,XHTML與HTML是不一樣的;HTML不區分大小寫,標準的XHTML標簽應該使用小寫.
XHTML屬性值必須使用引號,而HTML屬性值可用引號,可不要引號
XHTML屬性不能簡寫:如checked必須寫成checked="checked"
單標記<br>, XHTML必須有結束符<br/>,而HTML可以使用<br>,也可以使用<br/>
除此之外XHTML和HTML基本相同.
網頁寬度設置多少為最佳?
960px
target屬性值理解
_self 在當前窗口中打開鏈接文件,是默認值
_blank 開啟一個新的窗口打開鏈接文件
_parent 在父級窗口中打開文件,常用于框架頁面
_top 在頂層窗口中打開文件,常用語框架頁面
字符集:
charset=utf-8
Gb2312 簡單中文字符集, 最常用的中文字符
Gbk 簡繁體字符集, 中文字符集
Big5 繁體字符集, 臺灣等等
Utf-8 世界性語言的字符集
ANSI編碼格式編碼格式的擴展字符集有gb2312和gbk
單位問題:
HTML屬性值數值型的一般不帶單位, CSS必須帶單位;
強制刷新
ctrl+F5
*請認真填寫需求信息,我們會在24小時內與您取得聯系。