張東html
zhangdong@tedu.cnc++
1. ***數組:程序員
什麼是:正則表達式
爲何:算法
什麼時候:小程序
如何: 建立,訪問元素,遍歷數組
1. ***數組:瀏覽器
什麼是: 內存中連續存儲多個數據的一塊存儲空間數據結構
vs 變量: 內存中存儲一個數據的存儲空間閉包
爲何: ***程序=數據結構+算法
算法: 解決問題的步驟
數據結構: 數據在內存中的存儲結構
好的數據結構能夠極大提升程序的執行效率
什麼時候: 從此,只要連續存儲多個相關的數據,都要用數組
如何: 建立,訪問元素
建立: 3個場景:
1. 建立空數組: var 變量=[];
=new Array();
什麼時候: 在建立數組時,暫時不知道數組中的元素內容
2. 建立數組時,初始化數組內容:
var 變量=[值1,值2,...];
=new Array(值1,值2,...);
什麼時候: 在建立數組時,已經知道元素的內容
強調: 每一個元素之間,必須用逗號分隔
3. 建立n個空元素的數組: var 變量=new Array(n);
什麼時候: 在建立數組時,僅知道元素的個數,暫時不知道元素的內容。
訪問數組中的元素:
下標: 數組中惟一標識每一個元素存儲位置的序號
默認從0開始,連續不重複
arr[i]: 訪問arr數組中下標爲i位置的元素值
數組中每一個元素的用法和普通變量徹底同樣。
其實,數組也是一組變量的集合,再起一個統一的變量名
三個不限制:
1. 不限制元素的數據類型
2. 不限制元素的個數
3. 不限制下標越界:
賦值: 若是下標越界,不報錯!而是在指定位置自動添加新元素。——稀疏數組: 下標不連續的數組
取值: 若是下標越界,不報錯!而是返回undefined
length屬性: 記錄理論上數組的元素個數
length永遠是最大下標+1
固定套路:
1. 最後一個元素: arr[arr.length-1]
2. 倒數第n個元素: arr[arr.length-n]
3. 在末尾追加新元素: arr[arr.length]=值
4. 清空數組: arr.length=0;
5. 刪除末尾最後一個元素: arr.length--;
6. 刪除末尾的n個元素: arr.length-=n;
遍歷: 依次訪問數組中每一個元素:
for(var i=0;i<arr.length;i++){
arr[i]//當前元素
}
什麼時候: 從此只要對數組中每一個元素執行相同的操做時
***數組是引用類型的對象:
數據類型: 基礎類型: 值直接存儲在變量本地的數據類型
引用類型: 值沒法保存在變量本地的數據類型
數據實際存儲在變量外的一個獨立存儲空間
同時存儲多個數據的一塊存儲空間-對象
每一個對象都有惟一的內存地址
變量中僅保存對象的地址值!
訪問變量等效於經過地址值找到具體對象去訪問
數組就是引用類型的對象
***按值傳遞:
兩變量間賦值,或將變量做爲參數傳入函數時,其實僅將原變量中值,複製一個副本給對方變量:
對基礎類型: 修改新變量的值,不影響原變量;
對引用類型: 僅複製對象的地址給對方,不建立新對象
經過新變量修改對象,等效於修改原對象
null vs undefined
undefined: 空, 專門用於程序自動給變量初始化空值
null: 空, 專門用於程序員主動釋放對一個對象的引用
垃圾回收: 內存中引擎會自動釋放再也不被任何變量引用的對象
垃圾回收器: 在內存中自動執行的小程序
自動釋放不被任何變量引用的對象
好的習慣: 只要一個對象再也不使用,都要主動將變量置爲null
正課:
1. ***數組:
API:
拼接和選取
修改
翻轉
****排序: 自定義排序算法: 冒泡排序
sort()
1. 拼接和選取:
拼接: 將其它元素或其它數組拼接到當前數組末尾,返回新數組
var newArr=arr1.concat(值1,值2,arr2,......)
強調: 1. 無權修改原對象,只能返回新對象
2. 打散傳入的數組參數——珍貴
選取: 複製原數組中指定位置的元素組成新數組
var subArr=arr1.slice(starti,endi+1);
強調: 1. 無權修改原對象,只能返回新對象
2. 規律: 全部兩個參數都是下標的API,都含頭不含尾
簡寫: 1. 省略第二個參數: arr1.slice(starti)
從starti位置一直選取到結尾
2. 不寫參數: arr1.slice()——複製整個數組中全部元素
3. 若是離結尾近:
arr1.slice(arr1.length-n,arr1.length-m+1)
選擇倒數第n到倒數第m的元素
arr1.slice(-n,-m+1);
2. 修改數組splice: 刪除,插入,替換
刪除: arr.splice(starti,n) 從arr中starti開始,刪除n個元素
強調: 1. 不遵循含頭不含尾
2. 直接修改原數組
3. starti也支持負數參數,表示倒數第n個位置
簡寫: 省略第二個參數: arr.splice(starti) 刪除starti後全部
返回值: var deletes=arr.splice(starti,n)
返回被刪除的元素組成的臨時新數組
插入: arr.splice(starti,0,值1,值2,...)
在arr中starti位置插入: 值1,值2,...
原starti位置及其以後的值,向後順移
強調: 不能打散數組參數
替換: arr.splice(starti,n,值1,值2,...)
在arr中先刪除starti位置後的n個元素
再在starti位置插入新元素
強調: 刪除的元素個數和插入的新元素個數沒必要同樣
數組自動調整length
3. 翻轉數組: arr.reverse();
4. ****排序:
自定義排序算法: 冒泡,快速,插入
冒泡: 原理:
正課:
1. ***數組:
排序:
棧和隊列:
二維數組:
2. ***String
1. 排序:
自定義排序: 冒泡
排序API: arr.sort();
大問題: 默認將全部元素轉爲字符串再按字符串排列
只能對字符串類型的元素正確排序
解決: 自定義比較規則:
比較器函數: 專門比較任意兩值大小的函數:
要求: 兩個參數: a,b
返回值: 若是a>b,就返回正數
若是a<b,就返回負數
若是a=b,就返回0
最簡單的數字比較器函數:
function compare(a,b){return a-b;}
如何使用: 將比較器函數名做爲參數傳入sort函數中
arr.sort(compare) //強調: 不要加()
compare函數做爲參數傳入sort中,被sort反覆調用
降序: 顛倒比較器函數的正負號,可改升序爲降序
最簡單的數字降序比較器函數:
function compare(a,b){return b-a;}
2. 棧和隊列: js中沒有專門的棧和隊列類型
一切棧和隊列都是用普通數組模擬的
棧: 一端封閉,只能從另外一端進出的數組
特色: FILO
什麼時候: 從此只要但願始終使用最新的元素時
如何:
1. 從結尾出入棧:
入: arr.push(值) => arr[arr.length]=值
出: var last=arr.pop()
特色: 每次出入棧,都不影響其餘元素的位置
2. 從開頭出入棧:
入: arr.unshift(值)
出: var first=arr.shift()
特色: 每次出入棧,其它元素位置都會依次順移
隊列: 只能從一端進入,從另外一端出的數組
特色: FIFO
什麼時候: 只要但願按前後順序使用數組中的元素時
1. 結尾入: 入: arr.push(值)
2. 開頭出: var first=arr.shift()
3. 二維數組:
什麼是: 數組中的元素又引用了另外一個子數組
什麼時候: 1. 保存橫行豎列的二維數據結構
2. 對一組數據,再進行細緻分類時
如何:
建立: 2種:
1. 建立數組時,還不知道子數組的內容:
var arr=[];
arr[0]=[值1,值2,...];
arr[1]=[值1,值2,...];
2. 建立數組同時初始化元素:
var arr=[
[值1,值2,...],
[值1,值2,...],
...
]
訪問: arr[r][c]->用法和普通數組元素的用法徹底同樣
強調: 二維數組,行下標r不能越界!——報錯!
遍歷: 外層循環控制行,內層循環控制列
for(var r=0; r<arr.length; r++){//遍歷每一行
for(var c=0;c<arr[r].length;c++){//遍歷第r行中每一列
arr[r][c]//當前元素
}
}
練習: Math.random() 生成0~1之間的一個隨機小數
正課:
1. ***數組:
關聯數組:
API:
轉字符串
拼接和選取
修改
翻轉
1. 關聯數組:
索引數組: 下標爲數字的數組
問題: 每一個元素,只有值,沒有有意義的名稱
解決: 關聯數組: 可自定義下標名稱的數組
如何:
建立: 2步: 1. 先建立空數組: var scores=[]
2. 在空數組中添加新元素,使用自定義的下標名
scores["MATH"]=81;
訪問元素: 用自定義的下標名稱:
scores["MATH"] 用法和普通變量徹底同樣
關聯數組原理:
hash算法: 散列: 接收一個字符串,而後根據字符串計算出一個儘可能不重複的數字。
相同字符串,計算出的數字必定相同
不一樣的字符串,計算出的數字幾乎不一樣的
關聯數組: hash數組
存入元素: 將下標名交給hash算法,計算出一個儘可能不重複的序號,將元素存儲到數組中序號指定的位置
獲取元素: 將下標名交給hash算法,計算出和存入時徹底同樣的序號,直接去數組中序號位置獲取元素值——不用遍歷
爲何: 1. 由於下標有意義,便於維護
2. 查找極快——和總元素個數以及存儲位置無關!
什麼時候: 1. 從此只要快速查找某個元素時,都用hash數組
2. 從此只要存儲有意義的一組元素時,都用hash數組
鄙視: 談談對hash的理解:
hash算法; 存入; 獲取; 優;
特色: length屬性失效,永遠爲0
遍歷: for in循環:
for(var key in hash){//in依次得到每一個元素的下標名稱
key//自動得到當前元素的下標名
hash[key]//當前元素值
}
2. 數組API: 瀏覽器已經實現的,咱們直接用的函數
數組對象: 存儲一組數據,提供操做數據的API
1. 數組轉爲字符串: 2種:
1. var str=String(arr): 將arr中每一個元素轉爲字符串,再用逗號連接成一個字符串。
2. 可自定義鏈接符:
var str=arr.join("自定義鏈接符")
固定套路: 1. 無縫拼接: arr.join("")
2. 判斷數組是空數組: arr.join("")===""
3. 動態生成頁面元素的內容
正課:
1. ***String
什麼是:
***內置對象:
***包裝類型:
字符串API
1. 什麼是: 多個字符組成的只讀字符數組
vs 數組: 下標i
length
slice() concat
不一樣: 數組中凡是直接修改原數組的API,字符串都不能用!
2. 內置對象: ES標準中規定的,瀏覽器廠商已經實現的現成的對象和API
11個: Number String Boolean
Array Date RegExp Math
Error
Function Object
Global(瀏覽器中被替換爲window)
3. 包裝類型對象:
什麼是: 專門封裝基礎類型的值,並提供操做基礎類型值的API的對象
爲何: 基礎類型的值,自己不包含任何API功能
什麼時候: 只要試圖對基礎類型的值調用API時,都會自動建立對應類型的包裝類型對象來封裝基礎類型的值。
調用後: 包裝類型對象,自動釋放!
好比: var n=345.678;
n.toFixed(2)=>345.678.toFixed(2)
=>new Number(345.678).toFixed(2)
4. String的API:
***全部字符串API都無權修改原字符串,只能返回新字符串!
大小寫轉換: 將字符串中全部英文字母轉爲統一的大小寫
什麼時候: 只要不區分大小寫時,都要先轉爲一致的大小寫,再判斷。 好比: 用戶名,郵箱地址,驗證碼
如何: str.toUpperCase() //都轉大寫
str.toLowerCase() //都轉小寫
得到指定位置的字符: str[i]
var char=str.charAt(i);
得到指定字符的unicode號:
var unicode=str.charCodeAt(i); //省略i,默認是0
將unicode號反向轉回文字
var char=String.fromCharCode(unicode);
選取子字符串: str.slice(starti,endi+1)
str.substring(starti,endi+1) 不支持負數參數
str.substr(starti,n): 選取starti開始的n個元素
正課:
1. ***String API
查找關鍵詞
替換
切割字符串
2. *****正則表達式
1. 查找關鍵詞: 4種
1. 查找一個固定的關鍵詞出現的位置:
var i=str.indexOf("關鍵詞",fromi)
在str中,從fromi位置開始查找"關鍵詞"的位置
若是找到,返回關鍵詞所在位置的下標
找不到,返回-1
簡寫: 省略fromi,默認從0開始
專門找最後一個關鍵詞的位置:
var i=str.lastIndexOf("關鍵詞")
在str中,找最後一個關鍵詞出現的位置
問題: 只能找第一個關鍵詞
解決: 正則表達式:
2. 使用正則表達式查找指定的一類關鍵詞的位置:
按模式匹配:
var i=str.search(/正則表達式/);
在str中查找第一個符合正則表達式要求的關鍵詞的位置
返回值: 找到的關鍵詞的下標, 若是找不到返回-1
什麼時候: 僅判斷是否包含敏感詞時,就用search
若是返回不是-1,說明包含,不然說明沒找到
忽略大小寫: /正則/i
問題: 1. 只能得到第一個的位置,不能得到全部敏感詞
2. 只能返回位置,不能返回內容
3. 使用正則表達式查找指定的一類關鍵詞的內容:
var arr=str.match(/正則/ig);
默認只找第一個,找全部,必須加g
返回值: 全部敏感詞組成的數組
沒找到返回null!
強調: 若是一個API有可能返回null,就必須先判斷不是null,再使用!
arr.length 表示找到的關鍵詞個數
問題: 僅返回全部關鍵詞的內容,沒法返回每一個關鍵詞位置
4. 即找全部關鍵詞內容,又找每一個關鍵詞的位置?
reg.exec();
2. 替換: 將字符串中全部敏感詞替換爲新內容
基本替換:
str=str.replace(/正則/ig,「替換值」);
4. *****正則表達式:
什麼是: 專門定義一類字符串統一規則的表達式
什麼時候: 1. 按照指定規則模糊查找一類關鍵詞時
2. 表單中驗證輸入項的格式
如何: 語法:
1. 最簡單的正則其實就是關鍵詞原文
2. 字符集: 規定字符串中一位字符可用的備選字符列表
什麼時候: 只要某一位字符,有多個備選字時
如何: [備選字符列表]
強調: 一個字符集只能匹配一位字符
簡寫: 若是備選字符列表是連續的,就可用-省略中間字符
一位字母: [a-zA-Z]
一位數字: [0-9]
一位漢字: [\u4e00-\u9fa5]
特殊: 除了: [^排除的字符列表]
強調: ^必須寫在[開頭]
3. 預約義字符集: 4個:
\w 一位字母數字或_ =>[a-zA-Z0-9_]
\d 一位數字 => [0-9]
\s 一位空字符: 空格,Tab,...
. 一位任意字符
強調: 一個預約義字符集僅匹配一位字符
只有規則和預約義字符徹底一致時,才能使用
若是不一致, 依然須要手寫普通字符集
字符集僅控制每一個字符的內容
4. 量詞: 專門固定字符出現的次數
有明確數量邊界:
字符集{min,max} 規定字符集必須最少出現min次
最多max次
字符集{min,} 最少min次, 多了不限
字符集{n} 必須n次
沒有明確數量邊界:
字符集? 無關緊要,最多一次
字符集* 無關緊要,多了不限
字符集+ {1,}
強調: 僅修改相鄰的前一個字符集
5. 選擇和分組:
分組: 將多個字符集分紅一組:
什麼時候: 若是但願一個量詞同時修飾多個字符集時
好比: 我(了個?)?去: 我去 我了去 我了個去 我個去X
regexper.com
選擇: 其實就是"或" 規則1|規則2
只要匹配任意一個規則便可
(微|w(ei)?)\s*(信|x(in)?)
手機號:
(\+86|0086)? +86或0086 無關緊要,最多一次
\s* 空字符 無關緊要,多了不限
1
[34578] 34578中挑一個
\d{9} 9位數字
(\+86|0086)?\s*1[34578]\d{9}
郵箱:
字母/數字或_ 一次以上
@
字母或數字 2位以上
(.和 字母或數字 2到3位) 1到2次
\w+@[a-zA-Z0-9]{2,}(.[a-zA-Z0-9]{2,3}){1,2}
正課:
1. 正則:
指定匹配位置
2. ***String API:
替換: 衍生: 刪除和格式化
切割
3. ***RegExp對象
1. 正則:
指定匹配位置: 三個位置:
字符串的開頭 ^
字符串的結尾 $
好比: 開頭的空字符: ^\s+
結尾的空字符: \s+$
開頭或結尾的空字符^\s+|\s+$
固定套路: 只要但願字符串和正則從頭至尾徹底匹配
好比同時前加^後加$
只要用正則表達式執行驗證時,必須前加^後加$
單詞邊界 \b 包含: ^ $ 空格 標點
好比: 單詞首字母: \b[a-z]
單詞尾字母: [a-z]\b
單獨的一個單詞no: \bno\b
2. ***StringAPI
替換: 簡單替換: str=str.replace(/正則/ig, "替換值");
問題: 不能根據不一樣的關鍵詞,選擇不一樣的值替換
解決: 高級替換:
str=str.replace(/正則/ig,function(kw){
//kw會自動得到本次找到的關鍵詞內容
return //根據不一樣kw,返回不一樣的替換值
})
什麼時候: 只要根據不一樣的關鍵詞,替換不一樣內容時
衍生:
刪除: 將關鍵詞替換爲""
格式化: 將原字符串從新拼接爲新的格式
好比: "19831226" => "1983年12月26日"
2步: 1. 正則將原字符串分組
/(\d{4})(\d{2})(\d{2})/
// 1 2 3
2. 使用簡單替換: str.replace(/正則/,"...$n...")
$n可自動得到第n個分組的子內容
n從1開始
切割: 將原字符串,按指定字符,分隔爲多個子字符串
如何: var substrs=str.split(/正則/)
返回切割後的多個子字符串組成的數組
結果中,再也不包含分隔符
固定套路: 將字符串打散成字符數組: var chars=str.split("")
3. ***RegExp:
什麼是: 封裝一條正則表達式, 提供了使用正則表達式進行查找和驗證的API
什麼時候: 1. 查詢關鍵詞的第四種狀況: 即查內容,又查位置
2. 利用正則表達式執行驗證
如何:
建立: 2種:
1. 若是正則表達式是固定不變的: var reg=/正則/ig;
強調: /正則/中正則中的/都要轉義爲\/
2. 若是正則表達式是動態生成的:
var reg=new RegExp("正則"[,"ig"]);
強調: "正則"中的" \ 都要轉義爲\" \\
好比: "\d{6}" => "\\d{6}"
正課:
1. ***RegExp:
2. Math
3. ***Date
1. ***RegExp
API:
驗證: 檢查字符串是否徹底符合正則表達式的要求!
如何: var bool=reg.test(待檢測字符串)
強調: 只要驗證,reg中必須前加^後加$
查找關鍵詞: 第四種狀況: 即找內容,又找位置
如何: var arr=reg.exec(待查找的完整字符串)
在"待查找的完整字符串"中,依次查找每一個符合reg要求得關鍵詞。
返回值: 本次找到的一個關鍵詞及其位置
arr[0]: 關鍵詞內容
若是正則中有分組
arr[n]: 自動保存第n個分組匹配的子內容
arr["index"]: 當前關鍵詞位置 -> 可簡寫爲arr.index
若是沒找到,返回null
每次查找後,都將reg.lastIndex屬性(下次開始位置)修改成當前index+關鍵詞長度,至關跳過當前關鍵詞繼續向後找
固定套路: 找全部關鍵詞:
while((arr=reg.exec(str))!=null){
arr[0] 關鍵詞內容
arr[n] 自動得到第n個分組的子內容
arr.index 當前關鍵詞位置
}
若是隻須要分組的子字符串,不須要完整關鍵詞:
可省略arr,用RegExp.$n
while(reg.exec(str)!=null){
RegExp.$n 自動得到第n個分組的子內容
}
練習:
貪婪模式: 正則表達式默認匹配最長的符合條件的子字符串
默認使用貪婪模式
.* .+
懶惰模式: 僅匹配最短的符合條件的子字符串
貪婪->懶惰: .*? .+?
2. Math:
什麼是: 專門封裝數學計算所用常量,並提供數學計算所用API
什麼時候: 只要數學計算時
特色: 不能new!
API:
1. 取整:
Math.ceil(num) 上取整: 只要超過,就取下一個整數
Math.floor(num) 下取整: 省略小數部分
Math.round(num) 四捨五入取整:
vs toFixed(d):
1. 小數位數: Math.round()只能取整,不能規定小數位數
toFixed(d)可取整,也可規定小數位數
2. 返回值: Math.round()返回number
toFixed(d)返回string
自定義round函數:
2. 乘方和開平方:
乘方: Math.pow(底數, 冪)
開平方: Math.sqrt(n)
3. 最大值和最小值:
Math.max(值1,值2,...);
Math.min(值1,值2,...);
問題: 不支持數組
解決: Math.max.apply(null,arr)
4. 隨機數:
Math.random() 0<=r<1 隨機小數
從min~max之間取隨機整數:
Math.floor(Math.random()*(max-min+1)+min)
從0~n之間去隨機:
Math.floor(Math.random()*(n+1));
3. ***Date
什麼是: 封裝一個時間,提供操做時間的API
什麼時候: 只要存儲時間,都要用Date對象
如何:
建立: 4種:
1. 建立日期對象,並自動得到當前客戶端系統時間
var now=new Date();
2. 建立日期對象,並封裝自定義時間:
var date=new Date("yyyy/MM/dd hh:mm:ss");
var date=new Date(yyyy,MM-1,dd,hh,mm,ss)
3. 複製一個日期對象:
問題: 日期對象的計算都是直接修改原日期對象
計算後沒法保留計算前的舊時間
解決: 從此若是須要同時保留開始和結束時間時
都要先將開始時間複製一個副本,再用副本計算
var date1=new Date(...);
var date2=new Date(date1);
4. 用毫秒數建立日期對象:
Date對象的原理:
Date對象中保存了一個巨大的毫秒數
起始時間爲: 1970年1月1日0點至今的毫秒數
var date=new Date(ms);
兩個日期對象可相減: 獲得毫秒差
正課:
1. ***日期API
2. ***Error
1. ***日期API
單位: FullYear Month Date Day
Hours Minutes Seconds Milliseconds
API: 1. 每一個單位都有一個對兒get/set方法
好比: var year=date.getFullYear()//獲取單位的值
date.setFullYear(year)//設置單位的值
特殊: Day沒有set方法
2. 命名: 年月日星期沒有s結尾
時分秒毫秒都有s結尾
3. 取值範圍: 只有Date從1~31
不考慮FullYear, 其他都是從0開始,到進制-1結束
Month: 0~11 總比現實中小1, 須要修正
Date: 1~31 不用修正
Day: 0~6 不用修正
Hours: 0~23 不用修正
Minutes/Seconds: 0~59 不用修正
日期計算:
1. 計算兩個日期的時間差: 兩日期相減,得毫秒數,再換算
2. 對任意單位作加減: 3步:
1. 取份量: var d=date.getDate();
2. 作加減: d+=60
3. 放回去: date.setDate(d);
強調: 全部set方法可自動調整時間進制
其實可簡寫爲: date.setDate(date.getDate()+60)
轉字符串:
date.toString() -> 當地時間的完整時間格式
date.toLocaleString() ->當地時間的簡化版格式
date.toLocaleDateString() -> 當地時間的日期部分
date.toLocaleTimeString() -> 當地時間的時間部分
date.toGMTString() -> 標準時區的標準時間
做業: 自定義format函數: 2_format.html
2. ***Error
什麼是錯誤(bug): 程序執行過程當中遇到的異常中斷。
一旦發生錯誤,程序馬上退出。
什麼是錯誤處理: 即便程序發生錯誤,也能保證程序不異常中斷的一種機制。
如何:
try{
可能發生錯誤的代碼
}catch(err){//僅在發生錯誤時,才執行
錯誤處理代碼: 1. 提示用戶錯誤信息(String(err))
2. 記錄系統日誌
}finally{
不管是否發生錯誤,都必須執行的代碼。
好比: 釋放資源!
}
err: Error對象: 在錯誤發生時,自動建立的,保存錯誤信息的對象。
錯誤類型6種:
SyntaxError 語法錯誤
ReferenceError 要使用的變量沒找到
TypeError 錯誤的調用了對象的方法
RangeError 參數範圍越界 好比: toFixed(d) 0~20
EvalError URIError
正課:
1. ***錯誤處理
2. ***Function
*****閉包
1. ***錯誤處理
只要能夠提早預料的錯誤,都要用if...else...來代替try catch
只有沒法提早預料的錯誤,採用try catch
主動拋出錯誤:
爲何: 拋出錯誤一般是爲了提醒使用者錯誤的使用的程序
如何: throw new Error("錯誤消息")
2. ***Function:
什麼是: js中一切函數都是對象
函數對象是專門封裝函數定義的對象。
建立: 3種:
1. 聲明: function 函數名(參數列表){函數體; return 返回值;}
什麼時候: 只要一段代碼被反覆使用,都要先定義在一個專門的函數中,再反覆調用函數便可——複用
什麼時候使用參數: 只要函數步驟中必須某些數據才能正常執行時,就要定義參數。
什麼時候使用返回值: 若是函數的調用者須要函數的執行結果時,函數就必須返回值。
可被聲明提早:
2. 函數直接量:
var 函數名=function(參數列表){函數體; return 返回值;};
不會被聲明提早。
****聲明提早(hoist): 在開始執行程序前,將全部var聲明的變量和function聲明的函數提早到*當前做用域*的頂部,集中建立。
賦值留在原地!
什麼時候: 只要不但願被聲明提早時。
揭示了: 函數名僅是一個普通的變量
函數定義實際上是一個對象
函數名中僅保存了函數對象的地址——引用
3. 用new:
var fun=
new Function("參數1","參數2",...,"函數體; return 返回值")
好比: function compare(a,b){return a-b;}
var compare=function(a,b){return a-b;}
var compare=new Function("a","b","return a-b;");
***重載(overload):
什麼是: 相同函數名,不一樣參數列表的多個函數,在調用時,可根據傳入參數的不一樣,自動選擇對應的函數調用!
爲何: 減輕調用者的負擔,一個函數名,可執行多種操做
什麼時候: 一項任務,根據不一樣的參數,執行不一樣的操做流程時
如何: js語法不支持重載效果
變通: 全部函數對象內,都自動內建了一個arguments對象
arguments對象:
專門保存傳入函數的全部參數值的類數組對象
類數組對象: (object like array)
vs 數組: 相同: 下標, length, for遍歷
不一樣: 類數組對象是Object,不是Array,沒法使用Array的API
數組是Array類型,可使用數組類型全部的API
匿名函數:
什麼是: 函數建立時,不被任何變量引用的函數
爲何: 節約內存
什麼時候: 若是一個函數只用一次,用完但願自動釋放時
1. 回調callback: 將函數做爲參數傳遞給另外一個函數去調用
好比: arr.sort(function (a,b){return a-b});
str.replace(/reg/g,function(kw,$1,...){return ...})
2. 自調: 建立函數後馬上調用本身!
什麼時候: 若是一個函數只執行一次,不會再重用時
爲何: 創建臨時做用域!避免全局污染!
如何:
(function(參數列表){函數體; return 返回值})();
正課:
1. *****做用域和做用域鏈
2. *****閉包
1. *****做用域和做用域鏈
做用域scope:
什麼是: 一個變量的使用範圍——使用
本質上做用域是一個對象——存儲
做用域中的變量都是對象的成員
程序/函數的執行過程:
1. 開始執行程序前:
建立ECS(執行環境棧):
依次保存每一個調用的函數的執行環境
在ECS中壓入第一個全局執行環境(全局EC)
建立window對象,全局EC引用window對象
window就是全局做用域對象
2. 開始執行程序:
全部全局變量都保存在全局做用域對象window中
3. 定義函數時:
在全局添加函數名變量
建立函數對象封裝函數定義
函數名變量引用函數對象
函數對象中有一個scope屬性,引用回建立函數時的做用域對象。一般都是window。
4. 調用函數時:
在ECS中壓入一個新的EC
爲本次函數調用建立專門的活動對象(AO)
在AO中建立全部函數定義中規定的局部變量
其實AO就是函數做用域對象
全部局部變量都是AO的成員
新的EC引用活動對象AO
AO的parent指向window
變量的使用順序:
先用AO(函數做用域)中的局部變量
若是AO中沒有,纔去window(全局做用域)中找
5. 函數調用後:
本次函數調用的EC出棧
致使函數做用域對象AO釋放
致使局部變量一同釋放
做用域鏈(scope chain): 由多個做用域對象連續引用造成的鏈式結構。
順序: 先函數做用域對象AO->全局做用域對象window
全部的變量都保存在做用域鏈上的對象中
局部變量都保存在函數做用域對象AO中
全局變量都保存在全局做用域對象window中
控制了: 變量的使用順序
先用AO(函數做用域)中的局部變量
若是AO中沒有,纔去window(全局做用域)中找
閉包:
什麼是: 即重用變量,又防止變量被污染的一種機制
爲何: 全局變量: 優: 可重用 缺: 易被全局污染
局部變量: 優: 不會被污染 缺: 不可重用
什麼時候: 即重用變量,又防止變量被污染
如何: 3步:
1. 用外層函數包裹住受保護的變量和操做變量的內層函數
2. 外層函數將內層函數返回到外部,被外部的變量保存
3. 經過外部變量調用內層函數,訪問受保護的變量
缺: 1. 佔用更多內存: 外層函數的AO
2. 容易形成內存泄漏
三特色: 1. 函數嵌套:
2. 外層函數包含一個受保護的局部變量
3. 外層函數將內層函數對象返回
回顧:
1. *****閉包:
鄙視: 快速繪製閉包圖
1. 受保護的變量,並肯定外層函數調用後,變量的值
2. 找全部操做受保護變量的內層函數
正課:
1. *****面向對象OOP:
什麼是: 程序中都是先用對象來定義數據和功能,再按照邏輯的須要,訪問對象中的數據和功能。
爲何: 和現實中人的想法很是接近。
什麼是對象: 內存中同時存儲多個數據和功能的存儲空間
描述現實中一個具體事物的屬性和功能的程序結構
事物的屬性,會成爲對象中的屬性
事物的功能,會成爲對象中的方法
什麼時候: 從此開始寫程序前,都要先用對象,描述好要操做的事物的屬性和功能,再按需使用對象的功能,訪問對象的屬性
如何: 面向對象三大特色: 封裝,繼承,多態
封裝: 將一個具體事物的屬性和功能集中定義在一個對象中
建立自定義對象: ——封裝 3種:
1. 使用對象直接量:
var obj={
屬性名: 屬性值,
... : ... ,
方法名: function(){... this.屬性名 ...},
... : ... ,
}
強調: 對象本身的方法,要訪問本身的屬性,必須用this.屬性名.
this->正在調用函數的當前對象本身
2. 使用new: 2步:
var obj=new Object(); //建立一個空對象
//向空對象中添加屬性和方法
obj.屬性名=屬性值;
obj.方法名=function(){...this.屬性名...};
對象的本質: js中一切對象的底層都是關聯數組
每一個屬性/方法都是關聯數組中的元素
屬性名/方法名是key,屬性值/函數對象是value
問題: 一次只能建立一個對象
3. 解決: 用構造函數:
什麼是構造函數: 專門描述一類對象統一結構的函數
什麼時候: 從此只要反覆建立多個相同結構的對象時,都要先定義構造函數
爲何: 複用對象的結構代碼
如何: 2步:
1. 定義構造函數
function 類型名(屬性參數列表){
this.屬性名=屬性參數值;
...=...;
this.方法名=function(){ ... this.屬性名 ... }
}
2. 用new調用構造函數,建立並裝修新對象
var obj=new 類型名(屬性值列表);
建立一個指定「類型」的對象
用new調用指定"類型"的構造函數來建立對象
new: 4件事:
1. 建立新的空對象
2. 讓新對象繼承構造函數的原型對象
3. 用新對象去調用構造函數
向新對象中添加構造函數規定的屬性
將屬性參數的值,保存到新對象的新屬性中
向新對象中添加構造函數規定的方法
4. 將新對象的地址保存在變量
按需訪問對象的屬性,調用對象的方法:
訪問對象的屬性: obj.屬性名 用法和普通的變量徹底同樣
屬性就是保存在對象中的一個變量
調用對象的方法: obj.方法名() 用法和普通的函數徹底同樣
強調: 方法中的this,默認指.前的對象
構造函數的問題: 只能複用代碼,不能節約內存
繼承: 父對象的成員,子對象不用重複建立,也可直接使用
爲何: 即節約內存,又代碼重用
什麼時候: 只要一類子對象,須要相同的屬性或功能時,都要將相同的屬性和功能僅在父對象中定義一次便可
如何:
原型對象: 集中存儲同一類型的子對象所需的全部共有屬性和方法的父對象
正課:
1. *****OOP
內置對象的原型對象
共有屬性和自有屬性
原型鏈
原型相關API
*****自定義繼承
1. 內置對象的原型對象:
全部內置對象都是一個構造函數(除Math外)
每類內置對象都有本身的原型對象(prototype)
全部內置對象的API都保存在類型.prototype對象中
什麼時候: 解決瀏覽器兼容問題: 2步:
若是類型.prototype.方法===undefined
類型.prototype.方法=function(...){
this->自動得到未來調用該方法的當前對象
}
2. 共有屬性和自有屬性:
自有屬性: 直接保存在對象本地的屬性
共有屬性: 保存在父級原型對象中的屬性
訪問共有/自有屬性:
讀取屬性值: 便可用子對象讀取,也可用原型對象讀取
修改屬性值:
自有屬性: 子對象.屬性名=值
共有屬性: 原型對象.屬性名=值
如何判斷自有仍是共有:
自有: var bool=obj.hasOwnProperty("屬性名")
判斷obj中是否包含自有屬性"屬性名"
共有: 不是自有! 且 子對象可訪問到!
3. ***原型鏈(prototype chain):
什麼是原型鏈: 多級父對象連續繼承,造成的鏈式結構
保存了: 對象的屬性和方法
控制了: 對象的成員的使用順序
優先使用自有成員
本身沒有才延原型鏈向父級查找,只要找到就再也不向上
若是整個原型鏈上都沒有,才返回undefind
vs 做用域鏈(scope chain)
保存了: 全局/局部的變量
控制了: 變量的使用順序
優先在當前函數調用的函數做用域對象(AO)中查找
若是函數做用域對象(AO)中沒有,才延做用域鏈向全局方向查找。只要找到,就再也不繼續
若是整個做用域鏈上都沒有,才報錯
鄙視題: 判斷一個對象是否是數組類型,有幾種方式
0. typeof只能區分基礎類型和function
沒法進一步區分對象的類型
1. 判斷原型對象:
若是obj的原型是Array.prototype說明是數組
obj.__proto__==Array.prototype
問題: __proto__是內部屬性,原本瀏覽器是禁止使用的
解決: Object.getPrototypeOf(obj)
得到obj的原型對象
正課:
1. *****OOP
原型鏈
*****自定義繼承
1. 原型鏈:
判斷一個對象是否是數組類型,有幾種方法: 4種
0. typeof X
1. 判斷原型對象:
obj.__proto__==Array.prototype
問題: __proto__是內部屬性,可能不容許使用
Object.getPrototypeOf(obj)==Array.prototype
問題: 只能判斷直接父對象是Array.prototype的狀況
沒法判斷間接繼承Array.prototype的狀況
解決: var bool=father.isPrototypeOf(child)
判斷father是不是child的父級對象
不但檢查直接父對象,且檢查整個原型鏈!
2. 判斷構造函數:
每一個原型對象都有一個constructor屬性指回構造函數
obj.constructor==Array
還能夠用: obj instanceof Array
instance: 實例-用一個構造函數建立出的一個具體對象
好比: var lilei=new Student(...);
稱lilei是Student類型的一個實例
實例化一個Student類型的對象lilei
檢查整個原型鏈
要求不夠嚴格: 只要有繼承關係,就認爲是數組
要求嚴格: 只有用數組類型建立的對象,纔是真正的數組。
3. 檢查對象的class屬性
什麼是class: 對象的內部屬性,專門保存建立對象時使用的類型名。
只有一個辦法得到class屬性值:
調用Object.prototype.toString();->"[object Class]"
問題: 全部內置對象都重寫了Object中的toString
重寫(override): 若是子對象以爲,父對象的成員很差用,可在本地定義同名的自有成員,覆蓋父對象中的。
——多態
解決: 用call強行借用
call: 強行借用一個本沒法調用的方法
什麼時候: 想調用一個本來沒法調用的方法
如何: 想借用的函數.call(要替換的對象)
好比: Object.prototype.toString.call(obj)
至關於: obj.toString()
返回: "[object Class]"
4. Array.isArray(obj);
問題: ES5 IE9+
解決: 自定義isArray方法
鄙視題: 對象實例方法 vs 構造函數方法
對象實例方法: 必須用一個對象實例才能調用的方法
僅當前類型的對象可用!
對象實例方法一概保存在該類型的原型對象中,全部子對象共用。
什麼時候: 一個方法只但願當前類型的子對象才能使用時
構造函數方法: 不須要任何對象實例,用構造函數便可直接調用的方法。
構造函數方法一概保存在構造函數對象上
什麼時候: 一個方法的執行和對象的類型無關時
2. *****自定義繼承關係:
1. 修改單個對象的繼承關係:
obj.__proto__=father;
問題: 內部屬性: Object.setPrototypeOf(child,father);
設置child繼承father
2. 批量修改多個對象的繼承關係:
直接修改構造函數的prototype引用新的父對象
obj.prototype=father
強調: 必須在建立第一個子對象以前就換
3. 兩種類型間的繼承: 最像Java的繼承
什麼時候: 只要兩種類型間包含相同的屬性結構定義或相同的方法。
如何: 3步:
1. 抽象出一個父類型
共同的屬性定義,集中到父類型的構造函數中
共同的方法,集中到父類型的原型對象中
2. 在子類型構造函數中借用父類型構造函數
不能直接調用: this->window
應該用call,將this替換爲當前正在建立的新對象
父類型構造.call(this,參數...)
3. 讓子類型原型對象繼承父類型原型對象
正課:
1. *****ES5
對對象的保護:
對單個屬性的保護:
數據屬性:
訪問器屬性:
對對象的保護:
問題: 屬性可隨時直接用=賦值任何值
屬性可隨時被訪問
可隨時添加和刪除屬性
——不嚴格!
解決: 對對象提供保護:
如何:
1. 對對象的屬性提供保護
將對象的屬性分兩大類:
1. 命名屬性: 可隨時經過.屬性名方式訪問的屬性
又分爲2類:
1. 數據屬性: 實際存儲屬性值的屬性
如何保護: 每一個屬性都包含四大特性:
{
value: 實際存儲屬性值,
writable: true/false, //是否可修改
enumerable: true/false,//是否可for in遍歷
//依然可用.訪問到
configurable: true/false,
//1. 是否可修改前兩個特性
//2. 是否可刪除當前屬性
//一旦改成false,不可逆!
}
特殊: 若是要定義的屬性不存在:
defineProperty會自動添加:
自動添加後,屬性的特性值都爲false
問題: 只能提供基本(只讀,遍歷,刪除)保護
沒法按照自定義規則保護屬性
解決:
2. 訪問器屬性: 不實際存儲屬性值
專門對其它屬性提供驗證保護
什麼時候: 只要按照自定義規則保護屬性
如何: 也有四大特性:
{
get:function(){return 受保護的屬性值},
set:function(val){
驗證要賦的新值val
驗證經過纔將val保存到受保護的屬性中
},
enumerable:true/false,
configurable:true/false,
}
當經過訪問器屬性獲取受保護的屬性值時
自動調用get方法
當經過訪問器屬性爲受保護的屬性賦值時
自動調用set方法
參數val,自動得到要賦的新值
大問題: 受保護的屬性值應該保存在哪兒?
才能作到比人不能直接用,只能經過訪問器屬性訪問
解決: 閉包!
2. 內部屬性: 沒法經過.屬性名方式訪問到的屬性
class Object.prototype.toString.call(obj)
__proto__ Object.getPrototypeOf(obj)
Object.setPrototypeOf(child,father)
2. 對整個對象提供保護
正課:
1. *****ES5
對對象的保護:
對屬性的保護
防篡改
Object.create();
數組API:
*****bind()
1. 對對象的保護:
對屬性:
命名屬性
數據屬性:
訪問器屬性:
大問題: 受保護的屬性值應該保存在?
解決: 閉包
內部屬性
防篡改: 禁止修改對象的屬性結構
3個級別:
1. 防擴展: 禁止向對象中添加新屬性
Object.preventExtensions(obj)
2. 密封: 即防擴展,又禁止刪除舊屬性
Object.seal(obj)
實際上是將全部屬性的configurable設置爲false
3. 凍結: 即密封,又禁止修改全部屬性值!
什麼時候: 若是一個對象中保存了大量不變的屬性值時
Object.freeze(obj);
實際上是將全部屬性的writable設置爲false!
2. Object.create():
var newObj=Object.create(father,{擴展的新屬性})
建立一個新對象newObj,繼承father,併爲newObj擴展新的自有屬性
什麼時候: 只要繼承一個現有對象,建立一個新的子對象時
至關於: var newObj={};
newObj.__proto__=father;
Object.defineProperties(newObj,{
擴展的新屬性
})
3. 數組API:
1. 判斷: 數組中的元素是否符合要求
1. 全部元素是否都符合要求
var bool=
arr.every(function(val,i,arr){ return 判斷條件 }) 2. 是否包含符合要求的元素
var bool=
arr.some(function(val,i,arr){ return 判斷條件 })
2. 遍歷API: 依次對數組中每一個元素執行相同的操做
1. 對原數組中每一個元素執行相同的操做,結果保存回原數組
arr.forEach(function(val,i,arr){ arr[i]=新值; });
2. 取出原數組中每一個元素的值,執行相同的操做後,保存到一個新數組中
var newArr=arr.map(function(val,i,arr){
return 操做後的元素值
});
3. 過濾和彙總:
過濾: 選擇原數組中符合條件的元素,組成新數組
var subArr=arr.filter(function(val,i,arr){
return 判斷條件;
});
彙總: 將原數組中每一個元素統計出一個彙總結果
var r=arr.reduce(function(prev,val,i,arr){
return prev+val;
},0);
其中: 0: 表示初始值
prev: 截止到目前的階段彙總值
回調函數的返回值,自動做爲下次的prev值