张东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值