JavaScript (1)
2017-01-24 15:10:22 0 举报
AI智能生成
JavaScript是一种高级的、解释型的编程语言,主要用于网页和网络应用程序的开发。它是Web的三大核心技术之一,与HTML和CSS一起,构建了互联网的基础。JavaScript的主要特点是它是一种基于原型、多范式的动态脚本语言,支持面向对象、命令式和声明式(如函数式编程)风格。JavaScript具有灵活的特性,可以在浏览器中直接运行,无需编译。此外,它还具有事件驱动的特性,可以响应用户的交互行为。JavaScript的应用非常广泛,包括表单验证、动态效果、数据存储、异步通信等。随着Node.js的出现,JavaScript也被用于服务器端开发。
作者其他创作
大纲/内容
内置对象
ES标准中规定的,浏览器厂商已经实现的对象
11个
String Number Boolean
包装类型
专门封装原始类型的值,并提供操作原始类型值的API
为什么
原始类型的值本身不具有任何功能
何时
只要试图对原始类型的值调用函数时,引擎会自动创建对应类型的包装类型对象
封装原始类型的值
调用包装类型中的方法操作原始类型的值
Array Date Math RegExp
Error
Function Object
Global
在浏览器中被window代替
String
什么是
多个字符组成的只读字符数组
vs 数组
相同
下标
.length
slice()
不同
两者类型不同
API不通用
API
强调
所有String API都无权修改原字符串,只能返回新字符串
大小写转换
何时
只要不区分大小写时,都要先转换为一致的大小写,再比较
str.toUpperCase()
str.toLowerCase()
获取指定位置字符
var char=str.charAt(i)
str[i]
var unicode=str.charCodeAt(i)
简写
省略i, 默认为0
var char=String.fromCharCode(unicode)
选取子字符串
var subStr=str.slice(starti,endi+1)
简写:
同数组的slice
str.substring(starti,endi+1)
简写
不支持负数参数
str.substr(starti,n)
不考虑含头不含尾
查找关键词
1. 查找一个固定的关键词出现的位置
var i=str.indexOf("关键词",fromi)
返回值
返回找到的关键词所在的位置
如果找不到返回-1
固定套路
查找所有关键词出现的位置
var i=-1;
while((i=str.indexOf("关键词",i+1))!=-1){
i //本次找到的关键词位置
}
简写:
省略fromi,默认为0
优:
可以指定开始位置,可以找所有
缺:
不支持正则,一次只能找一种关键词
专门查找最后一个关键词的位置
var i=str.lastIndexOf("关键词")
2. 判断是否包含符合条件的关键词
var i=str.search(/正则表达式/)
如果返回-1,说明不包含,如果返回不是-1,说明包含
每次只找第一个关键词的位置
忽略大小写
/正则表达式/i
强调
不支持g
优:
支持正则
缺:
不能设置开始查找位置,只能找第一个,不能找所有
只能返回位置,不能返回关键词内容
3. 获取所有关键词的内容
var kwards=str.match(/正则表达式/ig)
返回值
包含所有关键词内容的数组
如果找不到,返回null
强调:
如果一个方法可能返回null
都要先 判断不是null,再使用!
强调:
省略g,只找第1个
加g,才找所有
返回包含所有关键词的数组
如果没找到,返回null
如果一个函数可能返回null,都要先验证,再使用结果
优:
获得所有关键词的内容
缺:
无法返回每个关键词的位置
4. 即获得每个关键词的内容,又获得每个关键词的位置
regexp.exec(str)
替换
简单替换:
将所有关键词都替换为统一的新值
str=str.replace(/正则表达式/ig,“替换值”)
问题: 无法根据不同的关键词,选择不同的值替换
高级替换:
根据每个关键词的不同,动态返回不同的替换值
str=str.replace(/正则表达式/ig,function(kw,$1,$2,...){
//kw: 会自动获得本次找到的完整关键词
//$n: 会自动获得本次找到的关键词中第n个分组的子内容
return 根据不同kw,返回不同替换值
})
衍生
删除
替换为空字符串
格式化
2步
1. 用正则对原始字符串分组
var reg=/(\d{4})(\d{2})(\d{2})/
2. 在replace的替换值中使用$n,重新拼接新的格式
birth.replace(reg,"$1年$2月$3日")
切割
简单切割
var subs=str.split("分隔符")
复杂切割
var subs=str.split(/正则表达式/)
切割后的结果中不包含分隔符
固定套路
将字符串打散为字符数组
var chars=str.split("")
正则表达式
什么是
规定一个字符串中字符出现规律的规则
何时
1. 按规则模糊查找多种关键词时
2. 用规则验证用户输入的格式时
1. 关键词的原文就是最简单的正则表达式
2. 字符集
[备选字符列表]
何时
只要一位字符有多个备选字时
强调:
一个字符集只能匹配一位字符
简写
[0-9]
[a-z]
[A-Z]
[A-Za-z]
[A-Za-z0-9]
[\u4e00-\u9fa5]
除了
[^47]
3. 预定义字符集
\d
[0-9]
\w
[A-Za-z0-9_]
\s
空字符
空格,制表符...
.
通配符
4. 量词
何时
只要规定一个字符集出现次数时
如何
字符集量词
强调:
默认仅修改相邻的前一个字符集
有明确数量
字符集{n,m}
至少n个,最多m个
字符集{n,}
n个以上
字符集{n}
必须n个
没有明确边界
字符集?
{0,1}
字符集*
{0,}
字符集+
{1,}
5. 选择和分组
分组
(多个规则)
何时:
1. 希望一个量词同时修饰多个字符集时
身份证号
\d{15}(\d\d[0-9Xx])?
2. 希望分段获取或处理字符串中部分子内容时
格式化生日
(\d{4})(\d{2})(\d{2})
选择
规则1|规则2
| 优先级最低
何时
在两种规则间任选其一匹配
微信
(微|w(ei)?)\s*(信|x(in)?)
6. 指定匹配位置
^ 字符串开头
比如
开头的空字符
^\s+
$ 字符串结尾
比如
结尾的空字符
\s+$
开头或结尾的空字符
^\s+|\s+$
\b 单词边界
^ $ 空字符 标点
7. 密码强度:
6~8位字母,数字的组合,至少包含一个大写字母和一位数字
^(?![a-z0-9]+$)(?![A-Za-z]+$)[A-Za-z0-9]{6,8}$
RegExp
创建
1. 直接量
var reg=/正则表达式/ig
如果正则表达式是固定不变的
字符冲突
/ -> \/
2. 用new
var reg=new RegExp("正则表达式","ig");
如果正则表达式需要动态生成
字符冲突:
\->\\ \" \'
new RegExp("\\d{6}")
API
查找关键词
var arr=reg.exec(str)
即查找内容又查找位置
在str中查找下一个满足reg要求的关键词
返回值:
arr: [0: "完整关键词", 1: $1, 2: $2,..., index: 本次找到关键词的位置]
reg.lastIndex
下次开始位置
如果没找到,返回null
exec做三件事
1. 将本次找到的关键词,放入数组第0个元素, 将每个分组的子内容放入后续元素
2. 修改数组的index属性,记录本次找到关键词的位置
3. 修改reg.lastIndex属性=index+关键词的长度
固定套路:
找全部
var arr=null;
while((arr=reg.exec(str))!=null){
arr[0] //完整关键词
arr[n] //第n个分组的子内容
arr.index //本次找到关键词的位置
reg.lastIndex //下次开始查找的位置
}
简写
如果只获得某个分组的子内容
while(reg.exec(str)!=null){
RegExp.$n //第n个分组的子内容
}
验证
var bool=reg.test(str)
验证str是否符合reg的规则要求
问题:
test默认,只要部分匹配就返回true
解决:
只要验证,正则都要前加^,后加$
表示从头到尾完全匹配
Math
不能new
所有API都用Math.直接调用
API
1. 取整
上取整
Math.ceil(num)
下取整
Math.floor(num)
只能对纯数字内容下取整
vs parseInt(str)
先去掉字符串后非数字字符,再省略小数部分
问题:
多数情况下,只去单位,还要保留小数
解决:
如果只是去单位,首选parseFloat
四舍五入取整
Math.round(num)
返回值是number
不能指定小数位数
vs num.toFixed(d)
返回值string
可以按任意小数位数四舍五入
自定义round
function round(num,d){
num*=Math.pow(10,d);
num=Math.round(num);
return num/Math.pow(10,d);
}
2. 乘方和开平方
乘方
Math.pow(底数,幂)
开平方
Math.sqrt(num)
for(var i=0;i<Math.sqrt(num);i++){
if(num%i==0){不是质数,return}
}
是质数
3. 最大值和最小值
Math.max/min(值1,值2,..)
问题:
不支持查找一个数组中的最大值/最小值
解决:
Math.max/min.apply(null,arr)
4. 随机数
默认
0<=Math.random()<1
在任意min~max之间生成随机整数
Math.floor(Math.random()*(max-min+1)+min)
在0~max之间生成随机整数
Math.floor(Math.random()*(max+1))
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 date2=new Date(date1)
4. 用毫秒数
var date=new Date(ms)
何时:
数据库中存时间都用ms,数据库中的ms,被查询到客户端,都需要转为日期对象
本质
日期对象中存储的是1970年1月1日0点至今的毫秒数
var ms=date.getTime();
API
单位
FullYear, Month, Date, Day
Hours, Minutes, Seconds Milliseconds
每个单位都有一个对儿getXXX/setXXX方法
date.getXXX()负责获取指定单位的数值
date.setXXX(num)负责设置指定单位的数值
特例:
Day没有setXXX()方法
取值范围:
只有月中的日Date,从1开始到31结束
其余都从0~进制-1结束
只有月份需要修正
0~11
计算机中的月份比现实中的月份至小1
其余都不需要修正
计算
两日期对象可相减
得到ms差
可计算倒计时
对任意单位做加减
date.setXXX(date.getXXX()+n)
强调:
1. 直接修改原日期对象
如果需要同时保存新旧两个时间,就要先将旧时间复制一个副本,再用副本计算
2. setXXX() 可自动调整时间进制
格式化
toString()
中国标准时间(+8区)
toLocaleString()
当前系统本地的时间格式
toLocaleDateString()
当前系统本地的时间格式
仅保留日期部分
有兼容性问题
toLocaleTimeString()
当前系统本地的时间格式
仅保留时间部分
toGMTString()
国际标准时间(0时区)
Error
Error错误对象
什么是
在发生错误时自动创建的封装错误信息的对象
name
错误类型
SyntaxError, ReferenceError, TypeError, RangeError, EvalError, URIError
message
错误提示信息
String(err)
错误类型:错误提示信息
错误处理
什么是
即使程序发生错误,也保证不异常退出的机制
为什么
任何程序只要发生错误,就会立刻中断退出
何时
只要希望程序即使出错,也不会中断退出
如何
try{
可能出错的代码
}catch(err){
只有出错才执行的错误处理代码
比如: 错误提示,记录日志,保存进度/数据
}finally{
无论是否出错都必须执行的代码
释放资源
}
优化
如果可以提前预知错误的原因
建议用if...else...代替try...catch
抛出自定义错误
throw new Error("自定义错误信息")
Function
什么是
函数其实是一个封装一段代码段的对象
函数名其实仅是引用函数对象的一个普通变量
function fun和var fun都是声明/创建一个变量的意思
创建
3种
1. 声明
function 函数名(参数列表){函数体; return 返回值}
会被声明提前
2. 直接量
var 函数名=function(参数列表){函数体; return 返回值}
不会被声明提前
3. 用new
var 函数名=new Function("参数名1","参数名2",...,"函数体; return 返回值")
重载(overload)
什么是
相同函数名,不同参数列表的多个函数
在调用时
根据传入参数的不同,自动选择匹配的函数执行
为什么
减少API的数量,减轻调用者的负担
何时
只要一项任务,需要根据不同的参数,执行不同的操作时
如何
问题
js语法不支持重载
js不允许同时存在多个同名函数
解决
每一个函数内,都有一个arguments对象接住所有传入函数的参数值
根据arguments的元素内容或元素个数,判断执行不同的操作
arguments
函数调用时,自动创建的
自动接收所有传入函数的参数值的
类数组对象
长的像数组的对象
vs 数组
相同
1. 下标
2. length
3. for遍历
不同
类型不同
数组是Array类型
类数组对象是Object类型
类数组对象无法使用数组的API
匿名函数
什么是
函数创建时,没有被任何变量引用
使用后自动释放!
为什么
节约内存
何时
只要一个函数只用一次
1. 回调callback
将一个函数对象作为参数传入另一个函数内,被其他函数调用
比如:
arr.sort(function(a,b){return a-b;})
str.replace(/reg/g, function(kw,$1,$2,...){return 替换值})
2. 自调
定义函数后自己调用自己,调用后,立刻释放
何时
定义一个临时作用域,减少使用全局变量, 避免全局污染
如何
(function(参数列表){函数体; return 返回值})(参数值列表)
+function(参数列表){函数体; return 返回值}(参数值列表)
作用域和作用域链
函数的生命周期
开始执行程序前
创建ECS
在ECS中压人第一个全局EC
创建全局作用域对象window
所有全局变量都是定义在window对象中
全局EC引用window
定义函数时
用函数名声明全局变量
创建函数对象,封装函数定义
函数对象的scope属性,指回函数创建时的作用域
函数名变量引用函数对象
调用函数时
向ECS中压入本次函数调用的EC
创建本次函数调用时使用的函数作用域对象(AO)
在AO中创建所有局部变量
设置AO的parent属性引用函数的scope属性指向的父级作用域对象
函数的EC引用AO
变量的使用顺序
先在AO中找局部变量
找不到才去window中找全局变量
调用后
函数的EC出栈
导致AO释放
导致AO中的局部变量一同被释放
作用域(scope)
变量的可用范围
其实是一个对象
包括
全局作用域对象
window
函数作用域对象
AO
作用域链(scope chain)
由各级作用域逐级引用,形成的链式结构
控制着
变量的使用顺序
函数中,没有用任何对象/this就直接访问的变量,在作用域链中找
闭包(closure)
什么是
即重用变量,又保护变量不被污染的机制
为什么
全局变量
优:
可重用
缺:
易被污染
局部变量
缺:
不可重用
优:
不会被污染
何时:
即重用变量,又保护变量不被污染
如何
三特点(3步)
1. 外层函数
包裹受保护的变量和操作变量的内层函数
2. 外层函数要返回内层函数的对象
3种
1. return function(){...}
2. 直接给全局变量赋值一个内部function
3. 将内部函数保存在一个对象的属性或数组元素中
return [function,function,function]
return { fun:function(){...} }
3. 调用外层函数,获得内层函数的对象,赋值给全局变量
形成闭包
闭包如何形成
外层函数调用后,外层函数的作用域对象(AO),无法释放
缺:
比普通函数占用更多内存
多的是外层函数的作用域对象(AO)始终存在
容易造成内存泄漏
解决:
如何释放闭包
将引用内层函数对象的全局变量置为null
鄙视
2步
1. 找受保护的的变量,确定外层函数调用后,变量的最终值
2. 找操作受保护的变量的内层函数对象
OOP
面向对象三大特点:
封装
创建一个对象,集中存储一个事物的属性和功能
继承
父对象中的成员,子对象无需重复创建,就可直接使用
多态
同一事物,在不同情况下,表现出不同的状态
封装——创建对象
也称为封装
将一个事物的属性和功能集中定义在一个对象中
事物的属性会成为对象的属性
其实就是保存在对象中的普通变量
事物的功能会成为对象的方法
其实就是保存在对象中的普通函数
对象的成员
属性和方法统称为成员
何时
只要使用面向对象,都要先创建对象,再按需调用对象的方法执行操作
如何
3种
创建一个单独的对象
1. 用对象直接量
var obj={
属性名:属性值,
... : ... ,
方法名: function(){
... this.属性名...
}
}
问题:
对象自己的方法,访问自己的属性,如果不加this,仅会在作用域链中找,不会在对象中找
解决:
this.属性名
强调:
不带this.的变量,在作用域链中查找
this.属性在当前对象和当前对象的原型链中找
2. 用new
先创建一个空对象
var obj=new Object();
说明:
new可省略,()可省略,但不能同时省略
为新对象添加新属性
obj.属性名=属性值
obj.方法名=function(){
... this.属性名 ...
}
揭示:
js中一切对象底层都是关联数组
相同
obj.属性名
等效于
obj["属性名"]
for in 遍历每个成员
for(var key in obj){
key //当前属性名
obj[key] //当前属性值
}
访问不存在的属性,不报错,返回undefined
随时添加新属性
不同
类型不同
问题:
前两种方式仅适合创建一个单独的对象
解决: 用构造函数反复创建多个相同结构的对象
3. 用构造函数
什么是
规定一类对象统一结构的函数
何时
反复创建多个相同结构的对象
如何
2步
1. 定义构造函数
function 类型名(属性参数){
this.属性名=属性参数;
/*this.方法名=function(){
... this.属性名 ...
}*///js中强烈不推荐将方法定义在构造函数中
}
2. 用new调用构造函数
var obj=new 类型名(属性值)
new
1. 创建一个新的空对象
2. 设置新对象的__proto__继承构造函数的prototype对象
3. 调用构造函数,将构造函数中的this自动替换为当前新对象
构造函数将规定的属性添加到新对象中,并将传入的参数值保存在新对象的新属性中
4. 返回新对象的地址保存到变量中
优:
代码重用
缺:
无法节约内存
放在构造函数中的方法定义,每new一次,都会创建函数对象副本
解决:
继承
this
什么是
自动引用正在调用当前方法的.前的对象
为什么
不用this的普通变量,只能在作用域链中查找,无法进入对象中
何时
只要希望去当前对象中找属性值时,就用this.属性名
obj.fun()
fun中的this->obj
new Fun()
Fun中的this->正在创建的新对象
fun()和匿名函数自调
this->window
类型.prototype.fun
fun中的this->将来调用fun的.前的子对象
子对象一定是当前类型
如果this不是想要的
fun.call(替换this的对象)
相当于 对象.fun()
访问对象成员
访问属性
对象.属性名
用法和普通变量完全一样
特殊:
如果访问对象的属性时,属性名需要动态拼接
只能用obj["xxx"]
调用方法
对象.方法名()
用法和普通函数完全一样
继承
js中的继承都是继承原型对象
原型对象
什么是
集中保存同一类型的所有子对象共有成员的父对象
何时
只要多个子对象,拥有相同的成员时,都要将相同的成员集中保存在原型对象中一份即可
如何
创建
在定义构造函数同时,已经自动创建了该类型的原型对象
构造函数.prototype指向原型对象
原型对象.constructor指回构造函数
new的第二步
每创建一个新子对象,都会自动设置子对象的__proto__继承构造函数的原型对象
向原型对象中添加共有成员:
构造函数.prototype.成员=值
内置对象的原型对象
其实内置对象类型Array,Date...都是构造函数
每种类型都有自己的原型对象
Array.prototype, String.prototype, ..
内置对象的原型对象中保存了所有该类型的子对象共用的API
固定套路:
解决旧浏览器无法使用新API的问题
2步:
判断
if(类型名.prototype.方法===undefined)
添加
类型名.prototype.方法=funciton(参数){
... this //获得将来调用方法的.前的对象
}
自有属性和共有属性
自有属性:
直接保存在对象本地的属性
共有属性:
保存在原型对象中,所有子对象共有的属性
判断
自有
var bool=obj.hasOwnProperty("属性名")
共有
!obj.hasOwnProperty("属性名")
&&
obj.属性名!==undefined
属性名 in obj
判断属性名是否在obj的原型链中
否则,就是没有
操作
读取
子对象和原型对象都行
修改
自有属性
必须用 子对象.属性名=值
共有属性
必须用 原型对象.属性名=值
原型链(prototype chain)
由各级父对象逐级继承形成的链式结构
保存了
所有对象的属性
控制着
对象成员的使用顺序
先用自有属性
自己没有,才去原型链上找
原型链上没有,返回undefined
vs 作用域链
保存了
局部和全局变量
控制着变量的使用顺序
先用局部变量
局部没有,才去作用域链上找
找不到,报错
判断对象的类型
0. typeof
只能区分基础类型和function
不能进一步区分对象的类型
1. var bool=类型.prototype.isPrototypeOf(child)
不仅检查直接父对象,而且检查整个原型链
2. var bool= child instanceof 构造函数
不仅检查直接父对象,而且检查整个原型链
问题:
检查整个原型链
解决: 3. 检查内置class属性
Object.prototype.toString.call(obj)==“[object 类型名]”
obj.toString()
更严格
class属性直接保存在对象本地
只在创建对象时确定类型
对象创建后,不随继承关系的改变而改变
如果检查数组类型: 4. var bool=Array.isArray(obj)
ES5
IE9+
原理和Object.prototype.toString.call一样
鄙视:
方法定义在原型对象中,还是定义在构造函数对象上
答
如果方法仅限当前类型的子对象可用,其他类型的对象不可用,就定义在原型对象中
必须当前类型的子对象才能调用
如果方法不确定将来调用它的对象类型,就定义在构造函数对象上
不需要任何对象实例,即可用构造函数名直接调用
多态
重写(override)
如果子对象觉得父对象的成员不好用,可在本地定义同名自有成员,来覆盖父对象中的成员
自定义继承
1. 仅修改两个对象间的继承关系:
获得子对象的父对象
var father=Object.getPrototypeOf(child)
设置子对象继承指定父对象
Object.setPrototypeOf(child,father)
2. 修改构造函数原型对象,来修改所有子对象的父对象
构造函数.prototype=father
时机:
必须在开始创建第一个子对象之间
3. 基于现有父对象,创建子对象,并扩展自有属性: Object.create()
创建新对象,继承父对象,扩展子对象自有属性
var child=Object.create(father,{
属性名:{四大特性},
... : ...
})
鄙视: 模拟实现Object.create()
Object.create=function(father,props){
//var child=new Object();
//Object.setPrototypeOf(child,father);
var Fun=function(){};
Fun.prototype=father;
var child=new Fun();
//Object.defineProperties(child,props);
if(props!==undefined){
for(var key in props){
child[key]=props[key].value;
}
}
return child;
}
4. 两种类型间的继承
何时:
如果发现多个类型拥有部分相同的属性结构和方法定义
如何
3步:
1. 定义抽象父类型
相同的属性结构定义在父类型的构造函数中
相同的方法定义在父类型的原型对象中
2. 让子类型继承父类型
1. 在子类型构造函数中借用父类型构造
extends
让父类型构造函数帮助添加相同部分的属性定义
子类型构造函数仅负责添加独有的属性定义即可
如何
父类型构造.call(this, 参数1,参数2,...)
简写:
父类型构造.apply(this, arguments)
鄙视:
call vs apply
相同:
都是强行借用一个本来无法调用的函数,并临时替换函数中this为指定对象
不同:
call
传入借用函数的参数,必须单独传入,逗号分隔
apply
传入借用函数的参数,放在一个数组中整体传入
可自动打散数组类型参数
2. 让子类型原型对象继承父类型原型对象
inherits
Object.setPrototypeOf(
子类型构造.prototype, 父类型构造.prototype
)
什么是
前端三大语言
HTML
专门编写网页内容内容的语言
CSS
专门编写网页样式的语言
JS
专门编写交互行为的语言
交互(IPO)
1. 用户输入
2. 程序接受输入,处理数据
3. 输出处理结果
JS特点
运行在客户端浏览器
浏览器包含两个小软件
内容排版引擎
解析HTML和CSS的程序
脚本解释引擎
解析js并运行js程序的程序
其实是运行在js脚本解释引擎中
解释执行
弱类型
基于对象
原生js
不依赖于第三方文件,仅依靠浏览器就可直接执行的代码
包含3大部分
ECMAScript
规定了js语言核心语法
3 5 6(2015)
DOM
专门操作网页内容的API
BOM
专门操作浏览器窗口的API
变量
什么是
内存中存储*一个*数据的存储空间,再起一个名字
为什么
没有名字的数据直接量,不可重用
何时
只要一个数据可能被反复使用时,都要保存在变量中
如何
声明
什么是
在内存中创建一块存储空间,再起一个名字
何时
任何变量在使用前,必须先声明
如何
var 变量名;
简写
var 变量1, 变量2, ...;
默认值
仅声明,未赋值的变量,默认值都是undefined
赋值
只有=才能赋值
变量名=值
将等号右边的值保存到等号左边的变量中
特殊
如果给未声明的变量强行赋值
普通模式
不会报错!
会自动在全局创建该变量
全局污染
严格模式
报错!
变量未定义
总结
禁止使用!
简写
声明同时初始化变量的值
var 变量名=值
特殊
同时声明并初始化多个变量
var 变量1=值1, 变量2=值2,...;
鄙视
var a , b=2;
a: undefined
b: 2
取值
任何情况下使用变量名等效于直接使用变量中的值
任何情况下从未声明的变量中取值都会报错!
ReferenceError
数据类型
什么是
数据在内存中的存储格式
包括
基础类型
值直接保存在变量本地的数据类型
number string boolean null undefined
引用类型
值无法保存在变量本地的数据类型
存储在window之外的一块独立存储空间
独立存储空间中可同时存储多个数据
变量名仅保存存储空间的地址
number
什么是
凡是不加引号的数字直接量称为数字类型
何时
如果一个数值可能需要比较大小和进行数学计算时
存储
整数占4字节
浮点数占8字节
string
什么是
凡是加引号的一串字符直接量称为字符串类型
何时
记录一串文字
存储
unicode
对全球主要语言的每个字编一个号
为什么
计算机不认识字符,只认识数字
每个字母/数字字符,占1字节
每个汉字,占2字节
boolean
什么是
只有两个值
true/false
何时
只要作为判断条件的结论时
null
空
专门表示一个空地址
专门给程序员用于手动清空一个变量之用
undefined
空
专门表示一个变量声明后还未赋值
专门由程序自动为一个变量初始化之用
数据类型转换
弱类型语言
1. 声明变量时无需提前规定变量中存储数据的类型
2. 一个变量先后可保存不同类型的数据
3. js会根据自身的需要,隐式转化数据的类型
什么是
将数据从一种类型转化为另一种类型
何时
只要数据类型不是想要的
2大类
强制转换
程序员主动调用转化函数实现的类型转换
3种情况
转为number
2种
1. 将非字符串类型转为number
Number(x)
Number(true)->1 Number(false)->0
Number(null)->0
Number(undefined)->NaN
其实也可以转字符串
只能转纯数字组成的字符串
2. 将字符串转为number
parseFloat(str)
将str转为number类型的浮点数(小数)
原理
从开头开始,依次读取每个字符
只要碰到数字和小数点就保留
直到碰到第一个不是数字和小数点的字符就不再继续读取
总结:
可保留小数
仅认识第一个小数点
去掉结尾的非数字字符
无法去掉开头的非数字字符
何时
只要希望去掉字符串结尾的单位时
parseInt(str)
将str转为number类型的整数
原理
和parseFloat完全一样
只是不认识小数点
总结
去掉小数
去掉结尾的非数字字符
何时
只要希望去掉结尾的单位,且同时舍弃小数部分时
特殊:
parseFloat(null/true/false)
NaN
先将null/true/false隐式转化为字符串,再按照原理转为数字
总结:
如果将字符串转数字
首选parseFloat
除非确定要舍弃小数部分采用parseInt
如果将非字符串转数字
首选Number
NaN
Not a Number
js中number类型的一个特殊值
代表一切不是数字的值
只要将其它类型转为数字时,无法正常转换,都转为NaN
转为string
x.toString()
问题:
x不能是null和undefined
解决
String(x)
万能
转为boolean
Boolean(x)
规则
只有五个值被转为false
0, null, NaN, undefined, ""
其余任何值都转为true
隐式转换
无需程序员干预,程序自动完成的类型转换
运算符和表达式
概念
程序
人的想法在计算机中的执行
运算符
程序中模拟人的想法的特殊符号
表达式
由数据,变量和运算符组成的一条程序的语句
执行过程
从左向右,依次执行
每读到一个变量就将变量换成变量中的值
包括
算数计算
+ - * / %
隐式转换
默认
一切转为number,再算数计算
特殊
参与+运算的数据中至少有一个是字符串
则全部转为字符串
+计算改为字符串拼接
关系运算
做比较,做判断
> < >= <= != ==
返回值
只能是true/false
隐式转换
默认
一切都转为number,再做比较
特殊
string类型做比较
如果参与比较的两个数据都是字符串,则不再转为number
而是按位PK每个字符的unicode号
只要一位字符能比较出结果,就不再继续
null和undefined
问题
用==无法区分null和undefined
普通关系运算会将undefined先隐式转为null再做比较
解决
===
先要类型相同,然后再值相等
不带隐式转换的==比较
强烈建议使用===代替==
!==
不带隐式转换的!=比较
强烈建议使用!==代替!=
NaN
问题
无法判断是不是NaN
NaN不等于,不大于,不小于一切
因为NaN表示所有不是数字的内容
是一个范围,不表示一个具体值
做比较无任何意义
解决
isNaN(num)
专门判断num是不是NaN
经常反用
!isNaN(num)
专门用来判断num是不是有效的数字
两个对象做==比较
不再做任何转换,而是比较两个对象的*地址*是否相同
判断两个对象是否同一个
逻辑运算
将多个关系运算,组合起来,得出最终的结论
返回true/false
3种
1. 逻辑与
而且
条件1&&条件2
必须同时满足条件1和条件2才返回true
只要一个条件不满足,都返回false
2. 逻辑或
或
条件1或条件2
只要满足条件1或条件2中任意一个条件就返回true
只有所有条件都为false时,才为false
3. 逻辑非
不
!条件
颠倒条件的判断结果
隐式转换
默认
每个条件都转为bool类型,再联合判断
短路逻辑
在逻辑运算中,如果前一个条件已经可以得出最终的结论,则后续条件不再执行
&&
如果前一个条件为false,则后续条件不再执行,直接返回false
||
如果前一个条件为true,则后续条件不再执行,直接返回true
利用短路
1. 实现简单分支
一个条件一件事,满足就做,不满足就不做
条件&&(操作)
2. 定义默认值效果
变量1||变量2
优先使用变量1的值
如果变量1的值转为bool后为false,则使用变量2的值作为备用
赋值运算
赋值运算也有返回值
返回保存到等号左边的变量中的新值
扩展赋值运算
一种简写
包括
a+=1
累加
a=a+1
如果只是+1
更简化
递增
a++
a-=1
a=a-1
如果只是-1
更简化
递减
a--
a*=1
a/=1
a%=1
何时
只要取出变量值,计算后,再保存回原变量时
++a vs a++
相同
变量a中的值,一定都会被+1
不同
返回值
++a,返回+1后的新值
a++,返回+1前的旧值
位运算
左移和右移
左移
m<<n
将m的二进制数左移n位
相当于m* 2的n次方
右移
m>>n
将m的二进制数右移n为
相当于m/ 2的n次方
下取整
m>>>0
m|0
m^0
不声明第三个变量,交换两数位置
a^=b; b=a^b; a^=b;
分支结构
让程序根据不同的条件,执行不同的任务
包括
1. 一个条件一件事,满足就做,不满足就不做
如果代码简单
条件&&(操作1,操作2,...)
如果代码复杂
if(条件){
操作
}
2. 一个条件2件事,二选一执行
如果只是根据不同的条件返回不同的值
条件?值1:值2;
如果代码简单
三目/三元/条件
条件?操作1:操作2;
如果代码复杂
if(条件){
操作1
}else{
操作2
}
3. 多个条件多件事,多选一执行
如果只是根据不同条件返回不同的值时
条件1?值1:
条件2?值2:
... ? ... :
默认值
默认值不能省略!
如果代码简单
条件1?操作1:
条件2?操作2:
... ? ... :
默认操作
最后必须有一个默认操作
如果代码复杂
if(条件1){
操作1
}else if(条件2){
操作2
}else if(...){
...
}else{
默认操作
}
最后一个else不是必须
特殊
如果所有条件都是等于比较时
switch(表达式){
case 值1:
操作1;
case 值2:
操作2;
case ... :
... ;
default:
默认操作
}
原理:
先计算表达式的值
再用表达式的值和每个case做===比较
只要表达式的值和某个case的值全等,就进入执行该case下的操作
强调:
表达式的值和case的值做全等比较,意味着首先类型必须相同
每个case其实仅是一个入口而已
问题
默认
一但进入一个case开始执行,则会连续触发之后所有case和default的操作
解决
在每个case之间加break
break退出当前结构
循环结构
让程序反复执行一段相同代码
循环三要素
循环条件
控制循环可以继续反复执行的条件
每执行完一次重复的操作,都要重新判断一次循环条件是否满足
一旦循环条件不再满足,则退出循环,不再反复执行
循环变量
循环条件中用作比较和判断的变量
都要考虑
从几开始,到几结束,每次增几
通常都会向着不满足循环条件的趋势不断变化
循环体
循环反复执行的代码段
三种
while
声明循环变量
while(循环条件){
循环体;
修改循环变量;
}
何时
循环变量的变化没有规律时
do while
声明循环变量
do{
循环体;
修改循环变量;
}while(循环条件);
vs while
如果第一次循环条件都满足
二者效果完全相同
如果第一次循环条件不满足
while是一次都不执行
do while至少可以执行一次
for
for(声明循环变量; 循环条件; 修改循环变量){
循环体;
}
何时
循环变量的变化是有规律的
简写
1. 声明循环变量部分,可同时声明并初始化多个变量,用逗号分隔
2. 修改循环变量部分,可同时执行多个短小的操作,用逗号分隔
不能修改原程序的执行顺序
结尾的分号不能省
了解
其实分支结构和循环结构中,如果if/else/else if/for/while之后只有一句话,可省略{}
禁止使用!
死循环
循环条件永远为true的不能自己退出的循环
while(true)
for(;;)
break和continue
break
退出当前结构,不再循环
continue
仅跳过本轮循环,依然继续执行下一轮
退出循环
2种办法
1. 用循环条件控制退出
优雅
难度高
2. 用死循环+break方式退出
野蛮
简单
函数
什么是
封装一项任务步骤清单的代码段,再起一个名字
函数也是引用类型的对象
函数名其实只是保存函数对象地址的变量
为什么
普通代码不可重用
何时
只要一项任务可能被反复使用,都要定义在函数中,再反复调用函数
如何
声明函数
function 函数名(参数列表){
函数体;
return 返回值;
}
参数
函数执行过程中必须的数据
为什么
有些功能必须某些外来数据才能正常执行
何时
只要函数本身需要某些数据才能正常执行时
如何
声明
函数名后的圆括号中, 用逗号分隔每个参数名
不用加var
访问
在函数内,参数的用法和普通变量完全一样
返回值
函数执行的结果
何时
只要调用者需要返回结果时
如何
函数结尾 return 值
向外部抛出函数处理结果的主要手段
调用
让引擎找到函数,按照函数的步骤清单执行程序
如何
var 返回值=函数名(参数值列表)
如果函数定义时,定义了参数列表,调用时必须传入参数值
传入的参数值的顺序和个数必须和函数定义中的参数列表一致
如果函数定义时,定义了返回值,调用时可以用变量接收函数的返回值
作用域(scope)
什么是
一个变量的可用范围
包含:
2种
全局作用域
window
全局变量
在函数外声明的不属于任何函数的变量
特点
随处可用
可反复使用
函数作用域
函数内部
局部变量
包含
在函数内用var声明的变量
参数变量也是局部变量
特点
仅函数内可用
不可重用
使用顺序
优先在函数作用域中找局部变量使用
如果局部没有,才去全局找
如果全局没有,才报错
按值传递byValue
在两变量间赋值时,或将变量作为参数传入函数时,仅将原变量中的值复制一个副本给对方
影响
如果传递的是基础类型的值,在函数中修改新变量,不会影响原变量
如果传递的是引用类型的对象,在函数中用新变量修改对象,等效于直接修改原对象
声明提前
在程序正式执行前,先将所有var声明的变量和function声明的函数提前到当前作用域的顶部,集中创建
赋值留在原地
数组
什么是
内存中连续存储多个数据的存储空间,再起一个名字
为什么
连续存储的一组数据,可极大提高程序的执行效率
何时
只要存储多个数据都要用数组
如何
创建
1. 创建空数组
数组直接量
var arr=[]
用new
var arr=new Array();
2. 创建数组同时初始化数组元素
数组直接量
var arr=[值1, 值2,...]
用new
var arr=new Array(值1,值2,...);
3. 创建n个空元素的数组
用new
var arr=new Array(n)
访问
arr[i]
用法和单个变量完全一样
三个不限制
不限制元素的数据类型
不限制下标越界
取值
不报错,返回undefined
赋值
不报错,自动在指定位置创建新元素
稀疏数组
下标不连续的数组
自动将length调整到最大下标+1
不限制元素个数
可随时在任意位置添加新元素
添加新元素后,都会自动改变length
关联数组
什么是
可自定义下标名称的数组
vs 索引数组
下标都是数字的数组
为什么
索引数组的数字下标没有意义,只能通过遍历查找指定的元素内容
查找速度受数组元素个数和元素位置的影响
何时
希望通过下标名称快速查找某个元素时
无需遍历
不受元素个数和元素存储位置的影响
如何
创建
2步
创建空数组
var hash=[]
向数组中添加新元素
hash["下标名(key)"]=值(value)
访问
hash["下标名(key)"]
用法同访问索引数组中的元素
特点
.length是属性始终为0
无法使用索引数组的API
遍历
依次取出数组中每个元素的值,执行相同的操作
何时
只要对数组中每个元素执行相同操作时
如何
for(var i=0;i<arr.length;i++){
arr[i] //当前数组元素
}
关联数组
for(var key in hash){
key //仅获取当前下标名称
hash[key] //获取当前元素值
}
in 依次取出关联数组中每个key
固定套路
仅获取hash中的所有key
var keys=[];
var i=0;
for(keys[i++] in hash);
//结束后: keys中保存了hash的所有key
数组API
.length属性
规定了数组理论上的元素个数
始终等于最大下标+1
自动维护
固定套路
获取最后一个元素
arr[arr.length-1]
获取倒数第n个元素
arr[arr.length-n]
末尾追加一个新元素
arr[arr.length]=值
缩容
删除末尾元素
arr.length--
删除末尾n个元素
arr.length-=n
清空数组
arr.length=0
转字符串
String(arr)
将arr中每个元素转为字符串,用逗号链接
arr.join("连接符")
将arr中每个元素转为字符串,可自定义连接符
固定套路
无缝拼接
arr.join("")
判断空数组
arr.join("")===""
动态生成页面元素
“<ANY>”+arr.join("</ANY><ANY>")+"</ANY>"
拼接和选取
强调: 都无权修改原数组,只能返回新数组,必须用变量接住返回值
拼接
var newArr=arr1.concat(值1,值2,arr2,....)
将值1,值2,arr2中的每个元素,拼接到arr1结尾
强调:
可自动打散数组类型的参数为单个数值,再拼接
选取
var subArr=arr.slice(starti,endi+1);
选取arr中starti位置到endi位置的元素,组成新数组返回
强调:
凡是两个参数都是下标的API
含头不含尾
简写
负数参数
倒数第n个
本质:
自动执行length-n
省略第二个参数
从starti一直选取到结尾
省略全部两个参数
复制整个数组
用途
将类数组对象转化为数组对象
Array.prototype.slice.call(arguments)
相当于
arguments.slice()
固定套路:
获得i位置开始的n个元素
arr.slice(i,i+n)
修改数组
删除元素
arr.splice(starti,n)
删除arr中starti位置开始的n个元素
强调:
直接修改原数组
不用考虑含头不含尾
简写:
省略第二个参数
删除starti位置后所有元素
支持负数参数,表示倒数第n个
其实有返回值
返回被删除的元素组成的临时数组
var deletes=arr.splice(starti,n)
插入元素
arr.splice(starti,0,值1,值2,...)
在arr中starti位置插入新值,原starti位置的值及其之后的值被向后顺移
强调:
不支持打散数组类型参数
如果插入子数组,会变成二维数组
替换
arr.splice(starti,n,值1,值2...)
先删除arr中starti位置的n个元素,再在starti位置插入新元素
强调:
删除的元素个数不一定和插入的元素个数一致
固定套路
广告轮播
移除开头的n个元素到结尾
imgs=imgs.concat(imgs.splice(0,n))
移除结尾的n个元素到开头
imgs=imgs.splice(-n).concat(imgs)
翻转
arr.reverse()
排序
手写排序:
冒泡,快速,插入
冒泡
依次比较相邻两数,如果前数>后数,就交换两数位置
for(var r=1;r<arr.length;r++){
for(var i=0;i<arr.length-r;i++){
if(arr[i]>arr[i>1]){
arr[i]=[arr[i+1],arr[i+1]=arr[i]][0];
}
}
}
arr.sort()
将arr中每个元素转为字符串,再按字符串升序排列
问题:
只能按字符串升序排列
解决:
自定义比较器函数
2步:
1. 定义比较器函数
专门比较两个值大小的函数
2个要求
1. 两个参数a,b
2. 返回值
a>b,返回正数
a<b,返回负数
a==b,返回0
比如
最简单的数字升序比较器
function compare(a,b){return a-b;}
2. 将比较器函数作为对象传入sort方法
arr.sort(compare)
强调:
回调函数
将一个函数,作为对象传入另一个函数内,被另一个函数使用
传入回调函数时,不要加()
因为不是立刻调用!而是交给别人去调用
其实都会简写为:
arr.sort(function(a,b){return a-b;})
降序排列
颠倒比较器函数返回值的正负号
比如:
数字降序比较器
function compare(a,b){return b-a;}
栈和队列
js中没有专门的栈和队列的类型,都是用普通数组模拟的
栈stack
一端封闭只能从另一端进出的数组
FILO
何时
希望始终使用最新进入数组的元素时
如何
结尾出入栈
入
arr.push(值)
arr[arr.length]=值
出
var last=arr.pop()
开头出入栈
入
arr.unshift(值)
出
var first=arr.shift()
队列queue
只能从结尾进入,从开头出的数组
FIFO
何时
只要按照先来后到的顺序使用数组元素时
如何
结尾入
开头出
二维数组
什么是
数组中的元素内容,又是一个子数组
何时使用
1. 保存横行竖列的二维数据
2. 对大的数组中的元素,再进行细分类
如何使用
创建
1. 先创建空数组,再赋值
var arr=[];
arr[i]=[值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++){
arr[r][c] //当前元素
}
}
ES5
对对象的保护
对象的属性
命名属性
可直接用.访问到的属性
分为
数据属性
实际存储属性值的属性
四大特性
{
value: 属性值,
writable: true/false, //控制是否可修改
enumerable: true/false, //控制是否可被for in遍历
configurable: true/false, //1. 控制是否可删除
//2. 控制是否可修改前两个特性
}
强调
configurable一旦改为false,不可逆
访问器属性
不实际存储数据,专门提供对其它数据/变量的保护
四大特性
{
get:function(){return 变量;},
set:function(val){
//如果val符合条件
变量=val
//否则
报错
},
enumerable, configurable
}
试图用访问器属性读取受保护的值时,自动调用get方法
试图用访问器属性修改受保护的值时,自动调用set方法,参数val可自动获得要赋的新值
如果省略set方法,则该访问器属性相当于只读
强调:
被访问器属性保护的值,应该保存在受闭包保护的局部变量中
何时
1. 用自定义规则保护属性时
比如
age属性必须介于18~65之间
2. 为对象添加虚拟属性
fullName
获取时
firstName+lastName
赋值时
将fullName按空格分隔,一半给firstName,另一半给lastName
获取一个属性的四大特性
var attrs=Object.getOwnPropertyDescriptor(obj,"属性名")
attrs: {四大特性}
修改四大特性
只改一个属性的四大特性
Object.defineProperty(obj,"属性名",{
特性名:值,
... : ...
})
同时修改多个属性的四大特性
Object.defineProperties(obj,{
属性名1: {
特性名:值,
... : ...
},
属性名2: {四大特性}
})
强调:
双保险
修改writable或enumerable时,最好同时定义configurable为false,禁止反向修改
要修改的属性不存在,会自动添加该属性
特性的默认值
用.添加的新属性
特性的默认值为true
defineProperty/defineProperties添加的新属性
特性的默认值为false
内部属性
不能用.访问到的属性
__proto__
Object.getPrototypeOf(obj)
Object.setPrototypeOf(child,father)
class
Object.prototype.toString.call(obj)
extensible:true
var bool=Object.isExtensible(obj)
Object.preventExtensions(obj)
防篡改
1. 防扩展
禁止添加新属性
相当于
将对象的extensible:false
判断是否已禁止扩展
Object.isExtensible(obj)
设置防扩展
Object.preventExtensions(obj)
2. 密封
在防扩展同时,禁止删除现有属性
相当于
将每个属性的configurable:false
其他属性在修改特性时,不必反复修改configurable:false
判断是否已密封
Object.isSealed(obj)
密封对象
Object.seal(obj)
最像Java的构造函数
function Emp(id,name,sal,age){
//public
this.id=id;
this.name=name;
this.sal=sal;
//age:18~65
//private
var _age;//受保护的局部变量
Object.defineProperties(this,{
id:{writable:false},
sal:{enumerable:false},
age:{
get:function(){return _age;},
set:function(val){
if(val<18||val>65)
throw new Error(...)
_age=val;
}
}
});
this.age=age;
//防篡改:
Object.seal(this);//密封
}
鄙视:
实现一个js类型,包含public属性和private属性
3. 冻结
在密封同时,禁止修改所有属性的值
相当于
将每个属性的writable:false
判断是否被冻结
Object.isFrozen(obj);
冻结对象
Object.freeze(obj)
何时:
如果一个对象中所有属性值都不允许擅自修改
数组API
判断
判断arr中每个元素是否都符合条件
arr.every(function(val,i,arr){
//val: 当前元素值
//i : 当前位置
//arr : 当前数组
return 判断条件;
})
只判断arr中是否包含符合条件的元素
arr.some(function(val,i,arr){
return 判断条件;
})
遍历
对数组中每个元素执行相同的操作
直接修改原数组
arr.forEach(function(val,i,arr){
arr[i]=新值;
})
IE8
Array.prototype.forEach=function(callback){
for(var i=0;i<this.length;i++){
if(this[i]!==undefined)//防稀疏数组
callback(this[i],i,this);
}
}
不修改原数组,返回新数组
var newArr=arr.Map(function(val,i,arr){
return 修改后的新值
})
IE8
Array.prototype.map=function(callback){
for(var i=0,result=[];i<this.length;i++){
if(this[i]!==undefined)//防稀疏数组
result[i]=callback(this[i],i,this);
}
return result;
}
其实map也可像forEach一样使用
过滤和汇总
过滤
选取arr中符合条件的元素组成新的子数组
var subArr=arr.filter(function(val,i,arr){
return 判断条件
})
汇总
将数组中每个元素值汇总出一个结果
var r=arr.reduce(function(prev,val,i,arr){
return prev+val;
}, 初始值)
bind()
基于一个现有函数,创建一个新函数,并永久绑定this和部分参数
为什么
vs call和apply
call和apply是临时绑定
每次调用必须手动输入要替换this的对象和要传入函数的参数
何时:
今后只要希望一个函数中的this始终指向一个固定的对象时
如何:
var newFun=fun.bind(替换this的对象, 希望永久绑定的参数列表 )
强调:
被bind创建的函数中的this和绑定的变量,任何情况下不能再被call替换
vs call和apply
call和apply
立刻调用函数执行
临时绑定
bind
仅创建新函数,不会立刻执行新函数
永久绑定
0 条评论
下一页