javascript中级

张东

[email protected]

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次

\[email protected][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值

原文地址:https://www.cnblogs.com/sidings/p/8449173.html

时间: 2024-10-20 07:40:56

javascript中级的相关文章

javascript中级--DOM元素的创建、插入、删除

一.创建DOM元素 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script> window.onload = function() { var oBtn = document.getElementById('btn1'); var oUl = docu

javascript中级笔记

DOM : Document Object Model 文档对象模型 文档:html页面 文档对象:页面中元素 文档对象模型:定义 为了能够让程序(js)去操作页面中的元素 DOM会把文档看作是一棵树,同时定义了很多方法来操作这棵数中的每一个元素(节点) DOM节点 getElementById getElementByTagName document document.body 元素.childNodes : 只读 属性 子节点列表集合 标准下:包含了文本和元素类型的节点,也会包含非法嵌套的子

javascript中级--ajax(1)

一.ajax.js function ajax(url, fnSucc, fnFaild) { // 1.创建ajax var oAjax = null; if (window.XMLHttpRequest) { var oAjax = new XMLHttpRequest(); } else { var oAjax = new ActiveXObject("Microsoft.XMLHTTP"); } //2 连接服务器 oAjax.open('GET', url, true); /

FCC上的javascript算法题之中级篇

FCC中的javascript中级算法题解答 中级算法的题目中用到了很多js的知识点,比如迭代,闭包,以及对json数据的使用等等,现在将自己中级算法的解答思路整理出来供大家参考讨论.欢迎大家提出新的思路,写出更简单的解法. 1.给一个包含两个数字的数组.返回这两个数字和它们之间所有数字的和. 说明:最小的数字并非总在最前面 如:sumAll([4, 1]) 应该返回 10. sumAll([5, 10]) 应该返回 45. function sumAll(arr) { var max=Math

JavaScript专家编程——互动出版网

这篇是计算机类的优质预售推荐>>>><JavaScript专家编程> 深入理解JavaScript 构建更好的应用程序 编辑推荐 本书是JavaScript深入学习的权威指南.难得的JavaScript中级到高级的作品,深入探讨语言机制和底层工作原理. 本书帮助读者深入理解JavaScript,构建更好的应用程序. 作者挑选了JavaScript语言重要的内部工作原理,设计成一张"藏宝图",你可以跟着这张藏宝图找到最后的宝藏.通过学习本书,挖掘Jav

Vue.js 是什么

Vue.js(读音 /vju?/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计.Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合.另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用. Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件. 如果你是有经验的前端开发者,想知道 Vue.js 与其它库/框架的区别,查看对比其它框

在这个夏天与Html5不期而遇(三)

一.HTML 1.学习HTML我们主要做的工作是前端,在与后台结合的过程中我们都需要互相有一定的了解. 2.服务器技术:PHP技术,JAVA技术,.NET技术 3.客户端技术: 基础:Html5 css3 javascript 中级:jquery(Java的框架) bootstrap(前端框架) 简单的服务器技术 ajax技术 高级:(协同工作的软件)+手机APP页面+react native 4.WEB的工作原理: B/S模式:浏览器/服务器(这是我们日常使用的网页搜索模式) C/S模式:客户

开始启程, 你的第一行前端代码

Vue.js杀敌须知 人生最大的敌人,就是没有明确的目标 -- 罗曼罗兰 选择自己的人生坐标,是对自己对生命的负责,没有认识目标,会变得慵懒,只能听天由命叹息茫然.学习前端开发亦是同理,我们开始学习之前我们必须要弄清楚 前端开发的职业前景怎么样? Vue.js是什么?为什么选择它? 学完Vue.js我们能够干什么? 如果自身对以上三个问题的答案都不感兴趣,那么劝诫大家不要再自己不喜欢的道路上花费太多的时间. 1.1 前端开发的职业前景怎么样? 最近两年,尤其是过去的2016年,前端开始引爆市场,

一文带你了解2018年最流行的前端技术

2018年即将过半,前端开发这个行业又进一个台阶了.找来一个现代前端技术图谱看看,真是吓尿了--宝宝心里苦啊! 点图片看大图 仔细想想,这要是全学会了还得了,也太不切实际了.还是来看看现在流行的是有哪些东西,跟着潮流走总不会错的. 每一个开发者都有着自己的知识和习惯,根据自己的知识和习惯,很容易地理所当然使用哪些工具.通过分析了解行业内开发者对开发工具的使用,这些结果有助于深入了解前端工具的当前趋势.无论你是刚刚开始学习网络开发,还是一位有经验的开发人员,这些分析数据对你的学习或工作都能带来不少