JavaScript
-
什么是
-
前端三大语言
-
HTML
- 专门编写网页内容的语言
-
CSS
- 专门编写网页样式的语言
-
JS
-
为什么
- 仅用HTML和CSS编写的网页,只能看不能用!
- 有些不需要服务器端数据,就可执行的功能,就不应该反复与服务器交互,频繁让用户等待。
-
发展史
-
专门编写客户端交互行为的语言
-
交互(IPO)
-
- 用户输入
-
- 程序接受输入,处理数据
-
- 输出处理结果
-
-
-
何时
- 今后,凡是HTML和CSS编写的网页,必须用js添加交互行为后,才能让用户使用
- 今后,不需要服务器数据,就可执行的操作,只要在客户端用JavaScript实现即可,不用频繁与服务器交互
-
-
-
如何使用JavaScript
-
在何处编写JavaScript
-
2处
-
-
HTML网页中的script标签内
-
问题:
-
无法重用
-
程序的准则:
- DRY
-
尽量:
- 一次定义,处处使用,一次修改,处处生效
-
-
-
解决
-
-
-
在独立的js文件中编写JavaScript
-
再用script标签引入网页中
-
-
-
-
如何运行
-
脚本解释引擎
- 解析并执行js程序的小软件
-
如何
-
-
浏览器已经自带脚本解释引擎
-
浏览器包含两个小软件
-
内容排版引擎
-
解析HTML和CSS的程序
- 效率高
-
-
脚本解释引擎
-
解析js并运行js程序的小程序
- 效率比排版引擎略低
-
-
-
-
-
独立安装
- Node.js
-
-
-
-
JavaScript语言基本语法
-
- 区分大小写
-
-
字符串必须用引号包裹, 但单双引号都行
-
特殊
- 如果字符串内又包含引号冲突, 只要内外使用不同的引号区分即可
-
-
- 每句话结尾必须用分号结束
-
-
注释
- //单行注释
- /* 多行注释 */
-
-
-
调试JavaScript
- 只要想要的效果没出来,就是出错了
- 只要出错,先开F12控制台(Console)
- Console中显示错误的原因,和出错位置
-
-
JavaScript能做什么
- 客户端表单验证
- 客户端数据计算
- 客户端动画效果和网页游戏
- 总之: js可对网页中的一切内容执行一切想要的操作——只有想不到,没有做不到
-
常用输出
-
向网页中输出内容
- document.write("HTML片段或文字")
- 问题: 影响网页中现有结构和内容
-
弹出警告框
-
alert("提示内容...")
-
问题:
-
- 样式写死,不可定制修改
-
- 模态,阻碍用户操作
-
-
-
向控制台输出:
- console.log("输出内容...")
-
今后,凡是调试程序,查看输出结果,都要在控制台输出
-
-
控制台的使用
-
- 单行代码按回车直接执行
-
-
切换出之前执行过的代码记录,微调
- 按键盘上下按键切换
-
-
-
清屏
- 点控制台左上角圆形叉号
-
-
-
仅换行输入,不执行
- shift+enter
-
-
-
原生js
-
不依赖于第三方文件,仅依靠浏览器就可直接执行的代码
-
包含3大部分
-
ECMAScript
-
规定了js语言核心语法的标准
-
3 5 6(2015)
-
JavaScript
- 网景(Mozilla火狐)参照ES标准实现的JavaScript语言版本
-
JScript
- 微软参照ES标准实现的JavaScript语言版本
-
-
-
DOM
-
专门操作网页内容的API
-
比如:
-
document.write(...)
- 向网页内容中输出一行话
-
-
-
BOM
-
专门操作浏览器窗口的API
-
比如:
-
alert,prompt
- 弹出浏览器的提示框
-
-
-
-
-
-
变量
-
什么是
- 内存中存储一个数据的存储空间,再起一个名字
-
为什么
- 重用一个数据
-
何时
- 只要一个数据可能被反复使用时,都要保存在变量中
-
如何
-
声明
-
什么是
- 在内存中创建一块存储空间,再起一个名字
-
何时
- 任何变量在使用前,必须先声明
-
如何
-
var 变量名;
-
变量名
-
- 字母,数字,下划线和$组成,但不能以数字开头
-
- 不能使用保留字
-
- 见名知义
-
- 驼峰命名
-
-
-
-
默认值
- 仅声明,未赋值的变量,默认值都是undefined
-
简写
- var 变量1, 变量2, ...;
-
-
赋值
-
变量名=值
- 将等号右边的值保存到等号左边的变量中
-
只有=才能赋值
-
=不是追加,而是完全替换!
- 覆盖原值
-
特殊
-
如果给未声明的变量强行赋值
-
普通模式
-
不会报错!
-
会自动在全局创建该变量
- 全局污染
-
-
ES5
-
严格模式
-
什么是
- 比普通js运行模式要求更严格的运行机制
-
要求1:
-
禁止给未声明的变量赋值
-
报错!
- 变量未定义
-
-
-
-
如何启用:
- 在当前代码段的顶部先插入"use strict";
-
-
总结
- 禁止使用!
-
-
-
-
简写
-
问题:
- 变量的默认值undefined是造成最多错误的根源
-
强烈建议
-
声明变量同时初始化变量的值
- var 变量名=值
-
-
特殊
-
同时声明并初始化多个变量
- var 变量1=值1, 变量2=值2, ...;
-
鄙视
-
var a , b=2;
- a: undefined
- b: 2
-
-
-
-
取值
-
任何情况下使用变量名等效于复制出变量中的值使用
-
尝试从未声明的变量中取值都会报错!
- ReferenceError
-
-
-
常量
-
什么是
- 一旦创建,值不能改变的量
-
何时
- 只要程序中的一个值,任何情况下都不能改变
-
如何
- const 常量名=值;
-
强调:
- 常量名必须全大写
- 创建常量时必须立刻赋值
-
强行修改常量的值:
-
在最新的Chrome浏览器中,已经内置了对常量赋值的检查
-
在ES3标准中,普通运行模式下,强行给常量赋值
-
不报错!
-
也不让修改
-
静默失败
-
解决
-
启用严格模式
-
要求:
- 将所有静默失败升级为错误!
-
-
-
-
-
-
-
数据类型
-
什么是
- 数据在内存中的存储格式
-
为什么
- 不同类型的数据,可执行的操作不一样
- 不同的操作,要求专门的存储结构支持
-
包括
-
原始类型
- 值直接保存在变量本地的数据类型
- number string boolean null undefined
-
引用类型
-
值无法保存在变量本地的复杂数据类型
- 如果一个复杂的数据结构,同时保存多个数据,就不能直接保存在变量中
- 存储在window之外的一块独立存储空间
- 独立存储空间中可同时存储多个数据,拥有唯一的地址
- 变量名仅保存存储空间的地址
-
-
-
number
-
什么是
- 专门存储数字(即包含整数,也包含小数)的类型
-
何时
- 如果一个数值可能需要比较大小和进行数学计算时
-
如何
- 程序中只要不加引号的数字,自动就是number类型
-
底层都是二进制存储
- n.toString(2)
-
存储
- 整数占4字节
- 浮点数占8字节
- 存储空间和数值大小无关
-
-
string
-
什么是
- 专门存储一串用于显示的文字
-
何时
-
记录一串文字
- 不用做比较和计算时
-
-
如何
- 凡是加引号的一串字符直接量称为字符串类型
-
存储
-
unicode
-
对全球主要语言的每个字编一个号
-
为什么
- 计算机不认识字符,只认识数字
-
范围:
-
“0”~“9”
- 48~57
-
"A"~"Z"
- 65~90
-
"a"~"z"
- 97~122
-
汉字
- “\u4e0”~"\u9fa5"
-
-
每个字母/数字字符,占1字节
-
每个汉字,占2字节
-
-
其他
-
utf-8
- 字母数字1字节
- 汉字3字节
-
GBK
- 字母数字1字节
- 汉字2字节
-
-
-
-
boolean
-
什么是
- 专门表示判断结果的
-
何时
- 只要作为判断条件的结论时
-
如何
-
只有两个值
-
true/false
- 不加引号
-
-
-
-
undefined
-
空
-
专门由程序自动为一个变量赋初值
- 程序员很少主动使用
-
-
-
null
-
空
- 专门给程序员用于手动清空一个变量之用
-
-
-
数据类型转换
-
弱类型语言
-
- 声明变量时无需提前规定变量中存储数据的类型
-
- 一个变量先后可保存不同类型的数据
-
- js会根据自身的需要,隐式转化数据的类型
-
-
什么是
- 将数据从一种类型转化为另一种类型
-
何时
- 只要数据类型不是想要的
-
2大类
-
隐式转换
-
无需程序员干预,程序自动完成的类型转换
-
何时
- 如果给定的数据类型和js运算要求的数据类型不相符,都会首先自动类型转换为规定的类型,再运行
-
-
强制转换
-
什么是
- 程序员主动调用转化函数实现的类型转换
-
何时
- 只要隐式转换的结果不是想要的
-
3种情况
-
转为number
-
2种
-
-
将纯数字组成的字符串和bool类型转为number
-
Number(x)
-
Number(true)->1 Number(false)->0
-
Number(null)->0 , Number("")->0
-
Number(undefined)->NaN
-
其实也可以转字符串
- 只能转纯数字组成的字符串
-
-
其实,所有隐式转化都是自动调用Number()
-
-
-
将字符串转为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
-
特点:
-
- 参与任何计算,结果依然是NaN
-
- 不大于,不小于,不等于任何值
-
-
-
-
转为string
-
x.toString()
-
问题:
- x不能是null和undefined
-
解决
-
-
String(x)
- 万能
- 其实隐式转换都是自动调用String()
-
-
转为boolean
-
Boolean(x)
-
规则
-
只有五个值被转为false
- 0, null, NaN, undefined, ""
-
其余任何值都转为true
-
-
-
-
-
-
-
-
运算符和表达式
-
概念
-
程序
- 人的想法在计算机中的执行
-
运算符
- 程序中模拟人的想法的特殊符号
-
表达式
-
由数据,变量和运算符组成的一条程序的语句
-
执行过程
-
从左向右,依次执行
- 每读到一个变量就将变量换成变量中的值
-
-
-
-
包括
-
算数计算
-
-
-
- / %
-
-
%
-
被除数/除数,不取商,取除不尽的余数部分
-
何时:
-
- 取余数
-
-
判断能否整除
-
比如
- 判断奇偶数
- 判断闰年
-
-
-
-
-
隐式转换
-
默认
- 一切转为number,再算数计算
-
特殊
-
+运算中只要有一个字符串
-
则全部转为字符串
-
+计算改为字符串拼接
-
问题:
- 凡是从页面上获得的数据都是字符串
-
解决:
- 做算数计算前,必须用parseFloat()强转为数字类型
-
-
-
-
-
关系运算
-
做比较,做判断
-
< >= <= != ==
-
返回值
- 只能是true/false
-
隐式转换
-
默认
- 一切都转为number,再做比较
-
特殊
-
两个string类型做比较
-
不再转为number
-
而是按位PK每个字符的unicode号
- 只要一位字符能比较出结果,就不再继续
-
-
null和undefined
-
问题
-
用==无法区分null和undefined
- 普通关系运算会将undefined先隐式转为null再做比较
-
-
解决
-
===
-
先要类型相同,然后再值相等
-
不带隐式转换的==比较
- 强烈建议使用===代替==
-
-
!==
-
不带隐式转换的!=比较
- 强烈建议使用!==代替!=
-
-
-
-
NaN
-
问题
-
无法判断是不是NaN
-
NaN不等于,不大于,不小于一切
-
因为NaN表示所有不是数字的内容
-
是一个范围,不表示一个具体值
- 做比较无任何意义
-
-
-
-
解决
-
isNaN(num)
-
专门判断num是不是NaN
-
经常反用
-
只要不是NaN,一定是数字
-
!isNaN(num)
- 专门用来判断num是不是有效的数字
-
-
-
-
-
两个对象做==比较
-
不再做任何转换,而是比较两个对象的地址是否相同
-
判断两个变量是否引用同一个对象
-
比如
-
[]==[]
- false
-
-
-
-
-
-
-
逻辑运算
-
将多个关系运算,组合起来,综合得出最终的结论
-
返回true/false
-
3种
-
-
逻辑与&&
-
而且
-
条件1&&条件2
- 必须同时满足条件1和条件2才返回true
- 只要一个条件不满足,都返回false
-
-
-
-
逻辑或
-
或/要么
-
条件1||条件2
- 只要满足条件1或条件2中任意一个条件就返回true
- 除非所有条件都为false时,才为false
-
-
-
-
逻辑非
-
不/没有
-
!条件
- 颠倒条件的判断结果
-
-
-
-
隐式转换
-
默认
- 每个条件都转为bool类型,再联合判断
-
-
短路逻辑
-
在逻辑运算中,如果前一个条件已经可以得出最终的结论,则后续条件不再执行
-
&&
- 如果前一个条件为false,则后续条件不再执行,直接返回false
-
||
- 如果前一个条件为true,则后续条件不再执行,直接返回true
-
利用短路
-
利用&&的短路
-
-
实现简单分支
- 一个条件一件事,满足条件才执行,不满足就不执行
- 条件&&(操作)
-
-
-
利用||的短路
-
-
定义默认值
-
值1||默认值
- 优先使用值1
- 如果值1转为bool后为false,才使用默认值作为备用
-
-
-
-
-
-
位运算
-
左移和右移
-
左移
-
m<<n
-
将m的二进制数左移n位
- 相当于m* 2的n次方
-
-
-
右移
-
m>>n
-
将m的二进制数右移n位
- 相当于m/ 2的n次方
-
-
-
-
下取整
- m>>>0
- m|0
- m^0
-
不声明第三个变量,交换两变量的值
-
方法一:
- a^=b; b^=a; a^=b;
-
方法二:
- a+=b; b=a-b; a-=b;
-
问题: 只能交换数字类型的数据
-
方法三:
- a=b,b=a
-
-
-
赋值运算
-
赋值运算也有返回值
- 返回保存到等号左边的变量中的新值
-
扩展赋值运算
-
一种简写
-
包括
-
a+=b
-
累加
-
a=a+b
-
如果只是+1
-
更简化
- 递增
- a++
-
-
-
-
a-=b
-
a=a-b
-
如果只是-1
-
更简化
- 递减
- a--
-
-
-
-
a*=b
-
a/=b
-
a%=b
-
-
-
何时
- 只要先取出变量值,计算后,想再保存回原变量时
-
递增/递减1
-
++a vs a++
-
单独使用
- 没有差别
-
如果嵌入其它表达式中
-
相同
- 变量a中的值,一定都会被+1
-
不同
-
返回值
- ++a,返回+1后的新值
- a++,返回+1前的旧值
-
-
-
-
-
-
-
-
-
函数
-
什么是
- 封装一项任务步骤清单的代码段,再起一个名字
- 函数名其实只是保存函数对象地址的变量
- 函数也是引用类型的对象
-
为什么
- 代码重用
-
何时
- 只要一项任务可能被反复使用,都要先定义在函数中,再反复调用函数
-
如何
-
声明函数
-
function 函数名(参数列表){ 函数体; return 返回值; }
-
参数
-
函数执行过程中接收必须数据的变量
-
为什么
- 有些功能必须某些外来数据才能正常执行
- 参数可让函数变的更灵活
-
何时
- 只要函数本身需要某些数据才能正常执行时
-
如何
-
声明
-
函数名后的圆括号中, 用逗号分隔每个参数名
- 不用加var
-
-
访问
- 在函数内,参数的用法和普通变量完全一样
-
-
-
返回值
-
函数执行的结果
-
何时
- 只要调用者需要获得函数的执行结果时
-
如何
-
函数结尾 return 值
- 向外部抛出函数处理结果的主要手段
-
-
-
-
-
调用
-
让引擎找到函数,按照函数的步骤清单执行程序
-
如何
-
var 返回值=函数名(参数值列表)
-
如果函数定义时,定义了参数列表,调用时必须传入参数值
- 传入的参数值的顺序和个数必须和函数定义中的参数列表一致
-
如果函数定义时,定义了返回值,调用时可以用变量接收函数的返回值
-
-
-
-
-
作用域(scope)
-
什么是
- 一个变量的可用范围
-
为什么
- 避免不同范围的变量间互相污染
-
包含:
-
2种
-
全局作用域
-
window
-
全局变量
-
什么是
- 在函数外声明的不属于任何函数的变量
-
特点
- 随处可用
- 可反复使用
-
何时:
- 只要一个变量希望反复使用或跨多个函数随处可用时
-
-
-
函数作用域
-
函数内部
-
局部变量
-
包含2种
- 在函数内用var声明的变量
- 参数变量也是局部变量
-
特点
- 仅函数内可用
- 不可重用
-
何时:
- 如果希望一个变量仅在函数内可用,不希望污染函数外部时
-
-
-
-
-
使用顺序
- 优先在函数作用域中找局部变量使用
- 如果局部没有,才去全局找
- 如果全局没有,才报错
-
-
声明提前
-
在程序正式执行前, 先将所有var声明的变量和function声明的函数 提前到当前作用域的顶部,集中创建
- 赋值留在原地
-
问题
- 破坏了程序执行的顺序
-
避免
-
- 尽量将变量或函数的声明集中在当前作用域顶部创建
-
-
用let代替var
- 不允许在let前使用未声明的变量
- 不允许重复声明变量
-
-
- 用let 函数名=function(...){...}代替function 函数名(...){...}
-
-
-
按值传递byValue
-
在两变量间赋值时,或将变量作为参数传入函数时,仅将原变量中的值复制一个副本给对方
-
影响
- 如果传递的是原始类型的值,在函数中修改新变量,不会影响原变量
- 如果传递的是引用类型的对象,在函数中用新变量修改对象,等效于直接修改原对象
-
-
-
全局函数
-
ES标准中规定的,不需要任何前缀.就可直接调用的函数
-
比如:
- Number() String() Boolean() isNaN() parseFloat/parseInt()
- encodeURI/decodeURI()
- encodeURIComponent()/decodeURIComponent()
- eval()
-
反例
- document.write() console.log();
- alert() prompt()
-
-
分支结构
-
让程序根据不同的条件,执行不同的任务
-
包括
-
-
一个条件一件事,满足就做,不满足就不做
-
如果操作简单
- 条件&&(操作1,操作2,...)
-
如果操作复杂
- if(条件){ 操作 }
-
-
-
一个条件2件事,二选一执行
-
如果操作复杂
- if(条件){ 操作1 }else{ 操作2 }
-
如果操作简单
-
三目/三元/条件
- 条件?操作1:操作2;
-
-
如果根据不同条件选择不同的值
-
三目
- 条件?值1:值2
-
-
-
-
多个条件多件事,多选一执行
-
如果操作复杂
-
if(条件1){ 操作1 }else if(条件2){ 操作2 }else if(...){ ... }[else{ 默认操作 }]
- 最后一个else不是必须
-
-
如果操作简单
-
条件1?操作1: 条件2?操作2: ... ? ... : 默认操作
- 最后必须有一个默认操作
-
-
如果只是根据不同条件返回不同的值时
-
条件1?值1: 条件2?值2: ... ? ... : 默认值
- 默认值不能省略!
-
-
特殊
-
如果所有条件都是等于比较时
-
switch(表达式){ case 值1: 操作1; break; case 值2: 操作2; break; case ... : ... ; break; [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(声明循环变量; 循环条件; 修改循环变量){ 循环体; }
-
何时
- 循环变量的变化是有规律的
-
简写
-
-
声明循环变量部分,可同时声明并初始化多个变量,用逗号分隔
-
问题: 循环内声明的变量,循环外是否可用
-
答: 可用
-
因为js没有块级作用域
-
vs Java
-
三级作用域
-
全局
-
函数
-
块级作用域
- if...else, while, do...while, for
-
-
-
-
-
-
修改循环变量部分,可同时执行多个短小的操作,用逗号分隔
-
不能修改原程序的执行顺序
-
结尾的分号不能省
-
了解
-
其实分支结构和循环结构中,如果if/else/else if/for/while之后只有一句话,可省略{}
- 禁止使用!
-
-
-
-
-
-
死循环
- 循环条件永远为true的不能自己退出的循环
- while(true)
- for(;;)
-
break和continue
-
break
- 退出当前结构,不再循环
-
continue
- 仅跳过本轮循环,依然继续执行下一轮
-
-
退出循环
-
2种办法
-
-
用循环条件控制退出
- 优雅
- 难度高
-
-
-
用死循环+break方式退出
- 野蛮
- 简单
-
-
-
-
-
-
数组
-
什么是
- 内存中连续存储多个数据的存储空间,再起一个名字
-
为什么
-
连续存储的一组数据,可极大提高程序的执行效率
- 便于维护和查找
-
-
何时
- 只要存储多个数据都要用数组
-
如何
-
创建
-
-
创建空数组
-
何时
- 如果创建数组时,暂时不知道数组的内容
-
用new
- var arr=new Array();
-
数组直接量
- var arr=[]
-
-
-
创建数组同时初始化数组元素
-
何时
- 如果创建数组时,已经知道数组内容
-
用new
- var arr=new Array(值1,值2,...);
-
数组直接量
- var arr=[值1, 值2,...]
-
-
-
创建n个空元素的数组
-
何时
- 如果创建数组时只知道数组的元素个数,暂时不知道内容时
-
用new
- var arr=new Array(n)
-
-
-
访问
-
元素
- 保存在数组中的一个数据
-
下标
- 数组中唯一标识元素存储位置的序号
- 默认从0开始,依次递增,连续不重复,到length-1结束
-
arr[i]
-
单个元素的用法和单个变量完全一样
- 数组其实是一组变量的集合,再起一个统一的变量名
-
三个不限制
-
不限制元素的数据类型
-
不限制元素个数
-
可随时在任意位置添加新元素
- 添加新元素后,都会自动改变length属性为最大数字下标+1
-
-
不限制下标越界
-
取值
- 不报错,返回undefined
-
赋值
-
不报错,自动在指定位置创建新元素
-
稀疏数组
- 下标不连续的数组
-
自动将length调整到最大下标+1
-
-
-
-
-
-
-
.length属性
-
规定了数组理论上的元素个数
-
始终等于最大下标+1
- 自动维护
-
固定套路
-
获取最后一个元素
-
arr[arr.length-1]
-
获取倒数第n个元素
- arr[arr.length-n]
-
-
末尾追加一个新元素
- arr[arr.length]=值
-
缩容
-
删除末尾一个元素
-
arr.length--
-
删除末尾n个元素
- arr.length-=n
-
-
清空数组
- arr.length=0
-
-
-
-
数组是引用类型
-
不直接存储在变量本地
- 一个变量只能存1个值
-
实际存储在变量之外
-
每个数组都有一个唯一的地址值
-
变量中只保存数组的地址值
- 也称变量引用着数组
-
-
-
按值传递
-
两变量间赋值或将变量传给函数参数时,其实只是将原变量中的值复制一个副本给对方
-
原始类型
- 修改新变量,不影响原变量
-
引用类型
-
通过新变量修改数组,同样会影响原变量
-
为什么
-
将原变量中的地址值复制给新变量
-
结果
- 两个变量引用同一个数组
-
-
-
-
-
-
垃圾回收
-
垃圾
- 不再被任何变量使用的对象
-
垃圾回收
- 程序会自动释放不再被任何变量使用的垃圾对象的内存空间
-
为什么
- 内存中的空间都是有限的,内存占用越多,程序运行越慢
-
垃圾回收器
- js引擎中,专门监控,并释放垃圾的小程序
-
如何
- 垃圾回收器伴随主程序在后台并行执行
- 垃圾回收器会记录每个对象被几个变量使用着
- 只要发现一个对象,不再被任何变量使用,就释放该对象所占内存
-
建议:
- 只要一个变量不再使用,都要主动赋值为null
-
-
遍历
-
依次取出数组中每个元素的值,执行相同的操作
-
何时
- 只要对数组中每个元素执行相同操作时
-
如何
-
索引数组
- for(var i=0;i<arr.length;i++){ arr[i] //当前数组元素 }
-
-
-
关联数组
-
什么是
-
可自定义下标名称的数组
-
vs 索引数组
- 下标都是数字的数组
-
-
-
为什么
-
索引数组的数字下标没有意义,只能通过遍历查找指定的元素
- 查找速度受数组元素个数和元素位置的影响
-
-
何时
-
希望通过下标名称快速查找某个元素时
-
无需遍历
- 不受元素个数和元素存储位置的影响
-
-
-
如何
-
创建
-
2步
-
创建空数组
- var hash=[]
-
向数组中添加新元素
- hash["下标名(key)"]=值(value)
-
-
-
访问
-
hash["下标名(key)"]
- 用法同访问索引数组中的元素
-
-
特点
- .length属性始终为0
- 无法使用索引数组的API
-
遍历
-
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
-
转字符串
-
String(arr)
-
将arr中每个元素转为字符串,用逗号链接
- 拍照
-
-
arr.join("连接符")
-
将arr中每个元素转为字符串,可自定义连接符
-
固定套路
-
无缝拼接
- arr.join("")
-
判断空数组
- arr.join("")===""
-
动态生成页面元素
- var html=“”+arr.join("")+""
- elem.innerHTML=html;
-
-
-
-
拼接和选取
-
强调: 都无权修改原数组,只能返回新数组,必须用变量接住返回值
-
拼接
-
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个元素
-
强调:
- 直接修改原数组
- 不用考虑含头不含尾
-
简写:
-
支持负数参数,表示倒数第n个
-
省略第二个参数
- 删除starti位置后所有元素
-
-
其实有返回值
- 返回被删除的元素组成的临时数组
- 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]; } } }
-
-
arr.sort()
-
将arr中每个元素转为字符串,再按字符串升序排列
-
问题:
- 只能按字符串升序排列
-
解决:
-
自定义比较器函数
-
2步:
-
-
定义比较器函数
-
专门比较两个值大小的函数
-
2个要求
-
- 两个参数a,b
-
-
返回值
- a>b,返回正数
- a<b,返回负数
- a==b,返回0
-
-
-
比如
-
最简单的数字升序比较器
- function compare(a,b){return a-b;}
-
-
-
-
将比较器函数作为对象传入sort方法
-
arr.sort(compare)
-
强调:
-
回调函数
-
将一个函数,作为参数传入另一个函数内,被另一个函数使用
-
传入回调函数时,不要加()
- 因为不是立刻调用!也不是只调用一次!而是交给别人去调用
-
-
-
-
-
其实都会简写为:
-
arr.sort(function(a,b){return a-b;})
-
ES6
- arr.sort((a,b)=>a-b)
-
-
-
-
-
降序排列
-
颠倒比较器函数返回值的正负号
-
比如:
-
数字降序比较器
- function compare(a,b){return b-a;}
-
-
-
-
-
栈和队列
-
何时
- 只要按照顺序使用数组中的元素时
-
js中没有专门的栈和队列的类型,都是用普通数组模拟的
-
栈stack
-
一端封闭只能从另一端进出的数组
-
FILO
-
何时
- 希望始终使用最新进入数组的元素时
-
如何
-
结尾出入栈
-
入
-
arr.push(值)
- arr[arr.length]=值
- 从数组尾部添加新元素
-
arr.push(值)的返回值,是新数组的长度
-
-
出
- var last=arr.pop()
- 返回值是尾部清除的元素组成的临时数组
-
-
开头出入栈
-
入
-
arr.unshift(值)
- arr.splice(0,0,值)
-
从数组头部添加新数组,返回值是新数组的长度
-
-
出
- var first=arr.shift()
- 从数组的头部删除第一个元素,返回值是删除元素组成的临时数组
-
-
强调:
- 开头入栈和结尾入栈的顺序是相反的
-
心法:别在意那些形式,记住核心的东西,从数组尾部、头部添加或者删除元素去理解;只要是添加东西,返回值都是新数组的长度;反之,就是删除元素临时组成的数组
-
-
-
队列queue
-
只能从结尾进入,从开头出的数组
-
FIFO
-
何时
- 只要按照先来后到的顺序使用数组元素时
-
如何
- 结尾入
- 开头出
-
-
-
-
二维数组
-
-
-
内置类型
-
ES标准中规定的,浏览器厂商已经实现的对象
-
11个
-
String Number Boolean
-
包装类型
-
什么是
- 专门封装原始类型的值,并提供操作原始类型值的API
-
为什么
- 原始类型的值本身不具有任何功能
-
何时
-
只要试图对原始类型的值调用函数时,引擎会自动创建对应类型的包装类型对象
- 封装原始类型的值
- 调用包装类型中的方法操作原始类型的值
-
-
比如:
-
n.toFixed(2)
-
typeof n
-
number
- new Number(n).toFixed(2)
-
-
-
str.charCodeAt()
-
typeof str
-
string
- new String(str).charCodeAt();
-
-
-
-
-
-
Array Date Math RegExp
-
Error
-
Function Object
-
Global
-
全局对象
- 在浏览器中被window代替
-
-
-
-
String
-
什么是
-
多个字符组成的只读字符数组
-
vs 数组
-
相同
- 下标
- .length
- for遍历
- 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.length-n
-
-
-
str.substr(starti,n)
- 不考虑含头不含尾
- str.substring(starti,starti+n)
-
-
查找关键词
-
-
查找一个固定的关键词出现的位置
-
var i=str.indexOf("关键词",fromi)
-
在str中从fromi位置开始查找下一个关键词的位置
-
返回值
- 返回找到的关键词所在的位置
- 如果找不到返回-1
-
固定套路
-
查找所有关键词出现的位置
- var i=-1; while((i=str.indexOf("关键词",i+1))!=-1){ i //本次找到的关键词位置 }
-
-
简写:
- 省略fromi,默认为0
- 两个参数都省略,表示完整复制一个数组
-
-
优:
- 可以指定开始位置,可以找所有
-
缺:
- 不支持正则,一次只能找一种关键词
-
专门查找最后一个关键词的位置
- var i=str.lastIndexOf("关键词")
-
-
-
判断是否包含符合规则的关键词
-
var i=str.search(/正则表达式/i)
-
如果返回-1,说明不包含,如果返回不是-1,说明包含
-
忽略大小写
- /正则表达式/i
-
永远只找第一个关键词的位置
-
强调
- 不支持g
-
-
优:
- 支持正则
-
缺:
- 不能设置开始查找位置,只能找第一个,不能找所有
- 只能返回位置,不能返回关键词内容
-
-
-
获取所有关键词的内容
-
var kwards=str.match(/正则表达式/ig)
-
强调:
- 省略g,只找第1个
- 加g,才找所有
-
-
返回包含所有关键词的数组
-
如果没找到,返回null
- 如果一个函数可能返回null,都要先验证,再使用结果
-
-
优:
- 获得所有关键词的内容
-
缺:
- 无法返回每个关键词的位置
-
-
-
即获得每个关键词的内容,又获得每个关键词的位置
- regexp.exec(str)
-
-
-
替换
-
简单替换:
-
将所有关键词都替换为统一的新值
-
str=str.replace(/正则表达式/ig,“替换值”)
- 如果替换一个固定的关键词,则第一个参数可以不用正则,只要写死关键词即可
-
问题: 无法根据不同的关键词,选择不同的值替换
-
-
高级替换:
- 根据每个关键词的不同,动态返回不同的替换值
- str=str.replace(/正则表达式/ig,function(kw,2,...){ //kw: 会自动获得本次找到的完整关键词 //$n: 会自动获得本次找到的关键词中第n个分组的子内容 return 根据不同kw,返回不同替换值 })
-
衍生
-
删除
- 替换为空字符串
-
格式化
-
2步
-
-
用正则对原始字符串分组
- var reg=/(\d{4})(\d{2})(\d{2})/
- 正则中每个分组都会自动获得一个分组序号,从1开始
-
-
-
在replace的替换值中使用$n,重新拼接新的格式
- birth.replace(reg,"2月$3日")
-
-
-
-
-
-
切割
-
简单切割
- var subs=str.split("分隔符")
-
复杂切割
- var subs=str.split(/正则表达式/)
-
返回值
-
多段子字符串组成的数组
- 切割后的结果中不包含分隔符
-
-
固定套路
-
将字符串打散为字符数组
- var chars=str.split("")
-
-
-
-
-
正则表达式
-
什么是
- 规定一个字符串中字符出现规律的规则
-
何时
-
- 按规则模糊查找多种关键词时
-
- 用规则验证用户输入的格式时
-
-
- 关键词的原文就是最简单的正则表达式
-
-
字符集
-
什么是
- 规定一位字符备选字符列表的规则
-
何时
- 只要一位字符有多个备选字时
-
[备选字符列表]
-
强调:
- 一个字符集只能匹配一位字符
-
简写
-
[0-9]
-
[a-z]
- [A-Z]
-
[A-Za-z]
-
[A-Za-z0-9]
-
[\u4e00-\u9fa5]
-
-
除了
- [^47]
-
-
-
预定义字符集
-
\d
- [0-9]
- 0-9之间的数字
-
\w
- [A-Za-z0-9_]
- 大小写字母及数字
-
\s
-
空字符
- 空格,制表符...
-
-
.
- 通配符
-
-
-
量词
-
什么是
- 规定一个字符集出现次数的规则
-
何时
- 只要规定一个字符集出现次数时
-
如何
- 字符集量词
-
强调:
- 默认仅修饰相邻的前一个字符集
-
有明确数量边界
-
字符集{n,m}
- 至少n个,最多m个
-
字符集{n,}
- n个以上
-
字符集{n}
- 必须n个
-
-
没有明确边界
-
字符集?
- {0,1}
- 可有可无,最多一个
-
字符集*
- {0,}
- 可有可无,多了不限
-
字符集+
- {1,}
- 至少一个,多了不限
-
-
-
-
选择和分组
-
分组
-
(多个规则)
-
何时:
-
-
希望一个量词同时修饰多个字符集时
-
身份证号
- \d{15}(\d\d[0-9Xx])?
-
-
-
希望分段获取或处理字符串中部分子内容时
-
格式化生日
- (\d{4})(\d{2})(\d{2})
-
-
-
-
选择
-
规则1|规则2
- | 优先级最低
-
何时
-
在两种规则间任选其一匹配
-
微信
- (微|w(ei)?)\s*(信|x(in)?)
-
-
-
-
-
-
指定匹配位置
-
^ 字符串开头
-
比如
-
开头的空字符
- ^\s+
- 从开头匹配空字符
-
-
-
$ 字符串结尾
-
比如
-
结尾的空字符
- \s+$
-
开头或结尾的空字符
- ^\s+|\s+$
-
-
-
\b 单词边界
- ^ $ 空字符 标点
-
-
-
密码强度:
- 6~8位字母,数字的组合,至少包含一个大写字母和一位数字
- ^(?![a-z0-9]+)[A-Za-z0-9]{6,8}$
-
-
-
RegExp
-
什么是
- 封装一条正则表达式,并提供用正则表达式执行验证和查找的API
-
何时
-
- 使用正则表达式验证字符串格式
-
- 即查找关键词内容,又查找关键词位置
-
-
创建
-
-
直接量
-
var reg=/正则表达式/ig
-
何时
- 如果正则表达式是固定不变的
-
字符冲突
- / -> /
-
-
-
用new
-
var reg=new RegExp("正则表达式","ig");
-
何时
- 如果正则表达式需要动态生成
-
字符冲突:
-
->\ " '
- new RegExp("\d{6}")
-
-
-
-
API
-
查找关键词
-
var arr=reg.exec(str)
-
即查找内容又查找位置
- 在str中查找下一个满足reg要求的关键词
-
返回值:
-
arr: [0: "完整关键词", 1: 2,..., index: 本次找到关键词的位置]
-
reg.lastIndex
- 下次开始位置
-
如果没找到,返回null
- 都要先判断不是null,再使用
-
-
exec做三件事
-
- 将本次找到的关键词,放入数组第0个元素, 将每个分组的子内容放入后续元素
-
- 修改数组的index属性,记录本次找到关键词的位置
-
- 修改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
-
-
取整
-
上取整
- Math.ceil(num)
-
下取整
-
Math.floor(num)
-
只能对纯数字内容下取整
- 如果传入的不是数字,就自动调用Number(x)隐式转换为数字
-
-
vs parseInt(str)
-
先去掉字符串后非数字字符,再省略小数部分
-
如果传入的不是字符串,就自动调用String(x),先转为字符串
-
问题:
- 多数情况下,只去单位,还要保留小数
-
解决:
- 如果只是去单位,首选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); }
-
-
-
-
乘方和开平方
-
乘方
- Math.pow(底数,幂)
-
开平方
-
Math.sqrt(num)
- for(var i=2;i<=Math.sqrt(num);i++){ if(num%i==0){不是质数,return} } 是质数
-
-
-
-
最大值和最小值
-
Math.max/min(值1,值2,...)
-
问题:
- 不支持查找一个数组中的最大值/最小值
-
解决:
- Math.max/min.apply(null,arr)
-
-
-
随机数
-
默认
- 0<=Math.random()<1
-
在任意min~max之间生成随机整数
- parseInt(Math.random()*(max-min+1)+min)
-
在0~max之间生成随机整数
- parseInt(Math.random()*(max+1))
-
-
-
-
Date
-
什么是
- 封装一个时间,并提供操作时间的API
-
何时
- 只要在程序中存储日期和时间,都要用Date对象
-
创建
-
4种
-
-
自动获得客户端当前系统时间
- var now=new Date()
-
-
-
创建日期对象保存自定义时间
- var date=new Date("yyyy/MM/dd hh:mm:ss")
- var date=new Date(yyyy,MM-1,dd,hh,mm,ss)
-
-
-
复制一个日期对象
-
为什么
- 日期的计算都是直接修改原日期对象,计算后,原日期无法保留
-
何时
- 只要需要同时保存开始和结束时间时,都要先将开始时间复制一个副本,再用副本计算截止时间
-
var date2=new Date(date1)
-
-
-
用毫秒数
-
var date=new Date(ms)
-
何时:
- 将获得ms数转化为当地的时间时
-
-
-
-
本质
-
日期对象中存储的是1970年1月1日0点至今的毫秒数
-
为什么
-
毫秒数不受时区影响
- 同一个毫秒数,在不同时区可用new Date()轻松转化为当地时间显示
-
-
var ms=date.getTime();
-
-
API
-
单位
-
FullYear, Month, Date, Day
- 没有s结尾
-
Hours, Minutes, Seconds Milliseconds
- 有s结尾
-
-
每个单位都有一对儿getXXX/setXXX方法
-
date.getXXX()负责获取指定单位的数值
-
date.setXXX(num)负责设置指定单位的数值
-
优
- 自动调整时间进制
-
-
特例:
- Day没有setXXX()方法
-
-
取值范围:
-
只有月中的日Date,从1开始到31结束
-
其余都从0~进制-1结束
-
只有月份Month需要修正
-
0~11
- 计算机中的月份比现实中的月份至小1
-
-
其余都不需要修正
-
-
-
计算
-
两日期对象可相减
-
得到ms差
- 可计算倒计时
-
-
对任意单位做加减
-
date.setXXX(date.getXXX()+n)
-
强调:
-
- 直接修改原日期对象
-
- setXXX() 可自动调整时间进制
-
-
-
-
格式化
-
toString()
- 当地标准时间的完整格式
-
toLocaleString()
- 当地时间的简化版格式
-
toLocaleDateString()
-
当地时间格式
-
仅保留日期部分
- 有兼容性问题
-
-
-
toLocaleTimeString()
-
当地时间格式
- 仅保留时间部分
-
-
toGMTString()
- 国际标准时间(0时区)
-
-
-
Error
-
什么是错误
- 代表程序执行过程中导致程序无法正常执行的原因
-
错误处理
-
什么是
- 即使程序发生错误,也保证不异常退出的机制
-
为什么
- 任何程序只要发生错误,就会立刻中断退出
-
何时
- 只要希望程序即使出错,也不会中断退出
-
如何
-
try{ 可能出错的代码 }catch(err){ 只有出错才执行的错误处理代码 比如: 错误提示,记录日志,保存进度/数据 }finally{ 无论是否出错都必须执行的代码 释放资源 }
-
Error错误对象
-
什么是
- 在发生错误时自动创建的封装错误信息的对象
-
name
-
错误类型
-
6种
- SyntaxError, ReferenceError, TypeError, RangeError, EvalError, URIError
-
-
-
message
- 错误提示信息
-
String(err)
- 错误类型:错误提示信息
-
-
-
-
优化
-
如果可以提前预知错误的原因
- 建议用if...else...代替try...catch
-
-
抛出自定义错误
- 在协作开发中,程序的作者用于提醒调用者错误的使用了你的程序
- throw new Error("自定义错误信息")
-
-
Function
-
什么是
- 函数其实是一个封装一段代码段的对象
- 函数名其实仅是引用函数对象的一个普通变量
-
为什么
- 代码重用
-
何时
- 只要一项任务,可能被反复使用,都要定义为函数,反复使用函数
-
创建
-
3种
-
-
声明
-
function 函数名(参数列表){函数体; return 返回值}
-
会被声明提前
-
hoist
-
什么是
- 在开始执行程序前
- 引擎会首先查找var声明的变量和function声明的函数
- 将其提前到当前作用域的顶部集中创建
- 赋值留在原地
-
鄙视时
- 凡是看到先使用,后声明,都是在声明提前
- 先改为声明提前之后的程序,再判断输出
-
解决问题
-
- 所有变量和函数的声明都放在当前作用域的顶部
-
-
ES6
-
可用let代替var
-
要求
- 当前作用域中let a之前不允许出现未声明的a
-
-
-
- 函数
-
-
-
-
-
-
直接量
- var 函数名=function(参数列表){函数体; return 返回值}
- 不会被声明提前
-
-
-
用new
- var 函数名=new Function("参数名1","参数名2",...,"函数体; return 返回值")
-
-
-
参数:
-
接收函数执行时必须的数据的变量
-
为什么
- 可让函数更灵活
-
何时
- 只要函数必须某些数据才能正常执行
-
-
返回值:
-
函数的执行结果
-
何时
- 如果调用者需要获得函数的返回值
-
-
-
调用
-
让引擎按照函数的步骤清单,执行任务
-
强调:
- 函数不调用不执行
- 只有调用才执行
-
-
重载(overload)
-
什么是
-
相同函数名,不同参数列表的多个函数
-
在调用时
- 根据传入参数的不同,自动选择匹配的函数执行
-
-
为什么
- 减少API的数量,减轻调用者的负担
-
何时
- 只要一项任务,需要根据不同的参数,执行不同的操作时
-
如何
-
问题
-
js语法不支持重载
- js不允许多个同名函数同时存在
-
-
解决
- 每一个函数内,都有一个arguments对象接住所有传入函数的参数值
- 根据arguments的元素内容或元素个数,判断执行不同的操作
-
arguments
-
函数调用时,自动创建的
-
自动接收所有传入函数的参数值的
-
类数组对象
-
长的像数组的对象
-
vs 数组
-
相同
-
- 下标
-
- length
-
- for/for of遍历
-
-
不同
-
类型不同
-
数组是Array类型
-
类数组对象是Object类型
- 类数组对象无法使用数组的API
-
-
-
-
-
-
-
-
匿名函数
-
什么是
-
函数创建时,没有指定函数名
- 使用后自动释放!
-
-
为什么
- 节约内存
- 划分临时作用域,避免全局变量
-
何时
-
只要一个函数只用一次
-
-
回调callback
-
将一个函数作为参数传入另一个函数内,被其他函数调用
-
比如:
-
arr.sort(function(a,b){return a-b;})
-
ES6
- arr.sort((a,b)=>a-b)
-
-
str.replace(/reg/g, function(kw,2,...){return 替换值})
-
ES6
- str.replace(/reg/g, (kw,2,...)=>{return 替换值})
-
-
-
-
-
自调
-
定义函数后自己调用自己,调用后,立刻释放
-
何时
- 定义一个临时作用域,减少使用全局变量, 避免全局污染
-
如何
- (function(参数列表){函数体; return 返回值})(参数值列表)
- !function(参数列表){函数体; return 返回值}(参数值列表)
- +function(参数列表){函数体; return 返回值}(参数值列表)
-
-
-
-
-
作用域和作用域链
-
作用域(scope)
-
变量的可用范围
- 其实是一个对象
-
包括
-
全局作用域对象
-
window
-
保存全局变量
-
优
- 可反复使用
-
缺
-
随处可用
-
全局污染
-
建议:
- 尽量少用全局变量
-
-
-
-
-
-
-
函数作用域对象
-
AO
-
保存局部变量
-
包括
-
2种
- 参数变量
- 函数内var出的变量
-
-
优
-
仅函数内可用
- 不会被污染
-
-
缺
- 不可重用
-
-
-
-
-
-
函数的生命周期
-
开始执行程序前
-
创建ECS
- 专门保存正在调用的函数的执行环境的 数组
-
首先在ECS中添加浏览器主程序的执行环境main
-
创建全局作用域对象window
- 所有全局变量都是保存在window对象中
-
main执行环境引用window
-
-
定义函数时
- 用函数名声明全局变量
- 创建函数对象,封装函数定义
- 函数对象的scope属性,指回函数创建时的作用域
- 函数名变量引用函数对象
-
调用函数时
-
向ECS中压入本次函数调用的执行环境元素
-
创建本次函数调用时使用的函数作用域对象(AO)
-
在AO中创建所有局部变量
-
包括
- 形参变量
- 函数内用var声明的变量
-
-
设置AO的parent属性引用函数的scope属性指向的父级作用域对象
-
函数的执行环境引用AO
-
变量的使用顺序
- 先在AO中找局部变量
- AO中没有才去父级作用域中找
-
-
调用后
-
函数的执行环境出栈
-
导致AO释放
- 导致AO中的局部变量一同被释放
-
-
-
-
作用域链(scope chain)
-
由各级作用域逐级引用,形成的链式结构
-
保存着所有的变量
-
控制着
- 变量的使用顺序
-
-
函数中,没有用任何对象/this就直接访问的变量,在作用域链中找
-
-
闭包(closure)
-
什么是
- 即重用变量,又保护变量不被污染的机制
-
为什么
-
全局变量
-
优:
- 可重用
-
缺:
- 易被污染
-
-
局部变量
-
缺:
- 不可重用
-
优:
- 不会被污染
-
-
-
何时:
- 即重用变量,又保护变量不被污染
-
如何
-
三特点(3步)
-
-
外层函数
- 包裹受保护的变量和操作变量的内层函数
-
-
-
外层函数要返回内层函数的对象
-
3种
-
- return function(){...}
-
- 直接给全局变量赋值一个内部function
-
-
将内部函数保存在一个对象的属性或数组元素中
- return [function,function,function]
- return { fun:function(){...} }
-
-
-
-
-
调用外层函数,用外部变量接住返回的内层函数对象
- 形成闭包
-
-
-
-
闭包如何形成
-
外层函数调用后,外层函数的作用域对象(AO),无法释放
- 被内层函数对象的scope引用着
-
-
缺:
-
比普通函数占用更多内存
- 多的是外层函数的作用域对象(AO)始终存在
-
容易造成内存泄漏
-
解决:
-
如何释放闭包
-
将引用内层函数对象的外部变量置为null
-
导致内层函数对象被释放
- 导致外层函数的AO被释放
-
-
-
-
-
鄙视
-
画简图
-
2步
-
- 找受保护的的变量,确定外层函数调用后,受保护变量的最终值
-
- 找操作受保护的变量的内层函数对象
-
结论:
- 同一次外层函数调用,返回的内层函数对象,共用同一个受保护的变量
-
-
-
-
-
-
OOP
-
什么是面向对象
- 程序中都是用对象结构来描述现实中一个具体事物
-
什么是对象
- 程序中描述现实中一个事物的属性和功能的程序结构
-
为什么
- 现实中,任何数据,必须属于一个具体事物才有意义!
-
面向对象三大特点:
-
封装
- 创建一个对象,集中存储一个事物的属性和功能
-
继承
- 父对象中的成员,子对象无需重复创建,就可直接使用
-
多态
- 同一事物,在不同情况下,表现出不同的状态
-
-
封装——创建对象
-
也称为封装
-
将一个事物的属性和功能集中定义在一个对象中
-
事物的属性会成为对象的属性
- 其实就是保存在对象中的普通变量
-
事物的功能会成为对象的方法
- 其实就是保存在对象中的普通函数
-
对象的成员
- 属性和方法统称为成员
-
-
为什么
- 便于维护
-
何时
- 只要使用面向对象,都要先创建对象,再按需调用对象的方法执行操作
-
如何
-
3种
-
创建一个单独的对象
-
-
用对象直接量
-
var obj={ 属性名:属性值, ... : ... , 方法名: function(){ ... this.属性名... } }
-
ES6
- var obj={ 属性名:属性值, ... : ... , 方法名(){ ... this.属性名... } }
-
-
何时
- 在创建对象时,已知对象的成员
-
问题:
- 对象自己的方法,访问自己的属性,如果不加this,仅会在作用域链中找,不会在对象中找
-
解决:
-
this.属性名
- 今后,只要对象自己的方法要访问自己的属性,必须用this.属性名
-
强调:
- 不带this.的变量,在作用域链中查找
- this.属性在当前对象和当前对象的原型链中找
-
-
-
-
用new
-
先创建一个空对象
-
var obj=new Object();
- {}
-
简写:
-
new可省略,()可省略,但不能同时省略
- 可推广到其它内置对象
-
-
-
为新对象添加新属性
- obj.属性名=属性值
- obj.方法名=function(){ ... this.属性名 ... }
-
何时:
- 在创建对象时,暂时不知道对象的成员
-
揭示:
-
js中一切对象底层都是关联数组
-
相同
-
obj.属性名
-
等效于
-
obj["属性名"]
- 只要访问对象的属性时,属性名是变化的变量,就只能用[变量]
-
-
-
for in 遍历每个成员
- for(var key in obj){ key //当前属性名 obj[key] //当前属性值 }
-
访问不存在的属性,不报错,返回undefined
-
强行给不存在的属性赋值
-
不报错
- 自动添加该属性
-
-
-
不同
-
类型不同
- API不通用
-
-
-
-
-
-
问题:
- 反复创建多个相同结构的对象时,会造成大量重复的代码
-
解决: 用构造函数反复创建多个相同结构的对象
-
-
用构造函数
-
什么是
- 规定一类对象统一结构的函数
-
何时
- 反复创建多个相同结构的对象
-
作用:
-
- 描述统一的结构
-
- 将空对象构建成要求的结构
-
-
如何
-
2步
-
-
定义构造函数
- function 类型名(属性参数){ this.属性名=属性参数; /this.方法名=function(){ ... this.属性名 ... } ///js中强烈不推荐将方法定义在构造函数中 }
-
-
-
用new调用构造函数
-
实例化一个对象
-
var obj=new 类型名(属性值,...,...)
-
new
-
- 创建一个新的空对象
-
- 设置新的子对象的proto继承构造函数的prototype对象
-
-
调用构造函数,将构造函数中的this自动替换为当前新对象
- 构造函数将规定的属性添加到新对象中,并将传入的参数值保存在新对象的新属性中
-
-
- 返回新对象的地址保存到变量中
-
-
-
-
-
优:
- 代码重用
-
问题:
-
无法节约内存
- 放在构造函数中的方法定义,每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[属性名]
-
-
-
调用方法
-
对象.方法名()
- 用法和普通函数完全一样
-
-
-
继承
-
什么是
- 父对象中的成员,子对象无需重复创建即可直接使用
-
何时:
- 只要多个子对象拥有相同的属性值或方法时,仅需要集中定义在父对象中一份,所有子对象共用即可
-
为什么
- 代码重用,节约内存
-
如何
-
js中的继承都是继承原型对象
- 原型继承
-
-
原型对象
-
什么是
- 集中保存同一类型的所有子对象共有成员的父对象
-
何时
- 只要多个子对象,拥有相同的成员时,都要将相同的成员集中保存在原型对象中一份即可
-
如何
-
不用创建
-
买一赠一
- 在定义构造函数同时,已经自动创建了该类型的原型对象
-
构造函数.prototype指向原型对象
-
原型对象.constructor指回构造函数
-
-
new的第二步
- 每创建一个新子对象,都会自动设置子对象的proto继承构造函数的原型对象
-
向原型对象中添加共有成员:
- 构造函数.prototype.成员=值
-
-
自有属性和共有属性
-
自有属性:
- 直接保存在对象本地的属性
-
共有属性:
- 保存在原型对象中,所有子对象共有的属性
-
操作
-
读取
- 子对象.属性
-
修改
-
自有属性
- 必须用 子对象.属性名=值
-
共有属性
- 必须用 构造函数.prototype.属性名=值
-
-
-
判断
-
自有
- var bool=obj.hasOwnProperty("属性名")
-
共有
-
不是自有
-
&&
-
obj.属性名!==undefined
-
属性名 in obj
- 判断属性名是否在obj的原型链中
-
-
-
-
否则,就是没有
-
-
-
内置对象的原型对象
-
其实内置对象类型Array,Date...都是构造函数
-
每种类型都有自己的原型对象
- Array.prototype, Date.prototype, ..
-
内置对象的原型对象中保存了所有该类型的子对象共用的API
-
固定套路:
-
解决旧浏览器无法使用新API的问题
-
2步:
-
判断
- if(typeof 内置类型.prototype.API !=="function")
- if(!("API" in 内置类型.prototype))
-
添加
- 内置类型.prototype.方法=function(参数){ ... this //获得将来调用方法的.前的对象 }
-
-
-
-
-
-
原型链(prototype chain)
-
由多级父对象逐级继承形成的链式结构
-
保存了
- 所有对象的属性
-
控制着
-
对象成员的使用顺序
- 先用自有属性
- 自己没有,才延原型链向父级找
- 原型链上没有,返回undefined
-
-
vs 作用域链
-
保存了
- 局部和全局变量
-
控制着变量的使用顺序
- 先用局部变量
- 局部没有,才去作用域链上找
- 找不到,报错
-
-
总结
-
所有不用.的变量都在作用域链上找
- 作用域链中的变量不用.,可直接访问
-
所有对象.访问的属性都保存在原型链上
- 原型链上的属性必须用“对象.”才能访问
-
-
-
-
判断对象的类型
-
-
typeof
- 只能区分基础类型和function
- 不能进一步区分对象的类型
-
-
-
var bool=类型.prototype.isPrototypeOf(child)
- 不仅检查直接父对象,而且检查整个原型链
-
-
-
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)
- 如果子对象觉得父对象的成员不好用,可在本地定义同名自有成员,来覆盖父对象中的成员
-
-
自定义继承
-
-
仅修改两个对象间的继承关系:
-
获得子对象的父对象
- var father=Object.getPrototypeOf(child)
-
设置子对象继承指定父对象
- Object.setPrototypeOf(child,father)
-
-
-
修改构造函数原型对象,来修改所有子对象的父对象
-
构造函数.prototype=father
-
时机:
- 必须紧跟在构造函数定义之后
- 开始创建子对象之前
-
-
-
仅基于现有父对象,创建子对象,并扩展自有属性: 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; }
-
-
两种类型间的继承
-
何时:
-
如果发现多个类型拥有部分相同的属性结构和方法定义
- 都要抽象父类型
-
-
如何
-
2步:
-
-
定义抽象父类型
- 相同的属性结构定义在父类型的构造函数中
- 相同的方法定义在父类型的原型对象中
-
-
-
让子类型继承父类型
-
-
在子类型构造函数中借用父类型构造
-
extends
- 让父类型构造函数帮助添加相同部分的属性定义
- 子类型构造函数仅负责添加独有的属性定义即可
-
错误:
-
直接调用父类型构造函数
-
this->window
- 父类型中的属性都泄露到全局
-
-
-
正确
-
父类型构造.call(this, 参数1,参数2,...)
-
简写:
- 父类型构造.apply(this, arguments)
-
-
鄙视:
-
call vs apply
-
相同:
- 都是强行借用一个本来无法调用的函数,并临时替换函数中this为指定对象
-
不同:
-
call
- 传入借用函数的参数,必须单独传入,逗号分隔
-
apply
- 传入借用函数的参数,放在一个数组中整体传入
- 可自动打散数组类型参数
-
-
-
-
-
-
让子类型原型对象继承父类型原型对象
- inherits
- Object.setPrototypeOf( 子类型构造.prototype, 父类型构造.prototype )
-
-
-
-
-
-
-
-
ES5
-
对对象的保护
-
对象的属性
-
命名属性
-
可直接用.访问到的属性
-
分为
-
数据属性
-
实际存储属性值的属性
-
四大特性
-
{ value: 属性值, writable: true/false, //控制是否可修改 enumerable: true/false, //控制是否可被for in遍历 configurable: true/false, //1. 控制是否可删除 //2. 控制是否可修改前两个特性 }
-
强调
- configurable一旦改为false,不可逆
-
-
-
访问器属性
-
不实际存储数据,专门提供对其它数据/变量的保护
-
何时
-
-
用自定义规则保护属性时
-
比如
- age属性必须介于18~65之间
-
-
-
为对象添加虚拟属性
-
fullName
-
获取时
- firstName+lastName
-
赋值时
- 将fullName按空格分隔,一半给firstName,另一半给lastName
-
-
-
-
2步:
-
- 定义一个隐藏的数据属性实际存储属性值
-
- 定义访问器属性专门读写隐藏的数据属性
-
-
四大特性
-
{ get:function(){return this.隐藏属性;}, set:function(val){ //如果val符合条件 this.隐藏属性=val //否则 报错 }, enumerable, configurable }
- 试图用访问器属性读取受保护的值时,自动调用get方法
- 试图用访问器属性修改受保护的值时,自动调用set方法,参数val可自动获得要赋的新值
-
如果省略set方法,则该访问器属性相当于只读
-
-
-
-
获取一个属性的四大特性
- 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)
-
-
-
防篡改
-
-
防扩展
-
禁止添加新属性
-
相当于
- 将对象的extensible:false
-
-
判断是否已禁止扩展
- Object.isExtensible(obj)
-
设置防扩展
- Object.preventExtensions(obj)
-
-
-
密封
-
在防扩展同时,禁止删除现有属性
-
相当于
-
将每个属性的configurable:false
- 其他属性在修改特性时,不必反复修改configurable:false
-
-
-
判断是否已密封
- Object.isSealed(obj)
-
密封对象
- Object.seal(obj)
-
最像Java的构造函数
- function Emp(id,ename,salary,age){ this.id=id; this.ename=ename; this.salary=salary;
-
-
-
Object.defineProperties(this,{ id:{writable:false}, salary:{enumerable:false}, age:{ writable:true, enumerable:false }, age:{ get:function(){return this. age;}, set:function(val){ if(val<18||val>65) throw new Error(...) this._age=val; }, enumerable:true } }); this.age=age;
//防篡改: Object.seal(this);//密封 }
- 鄙视: - 实现一个js类型,包含public属性和private属性 - 3. 冻结 - 在密封同时,禁止修改所有属性的值 - 相当于 - 将每个属性的writable:false - 判断是否被冻结 - Object.isFrozen(obj); - 冻结对象 - Object.freeze(obj) - 何时: - 如果一个对象中所有属性值都不允许擅自修改-
call/apply/bind()
-
何时:
- 只要函数中的this不是想要的,就可用call/apply/bind替换
-
call和apply
-
立刻调用函数执行
-
同时临时替换函数中的this
-
何时
- 如果立刻执行,且临时替换this
-
如何
-
fun.call(obj, 参数值列表)
- 调用fun
- 替换fun中的this为obj
- 将参数值列表传递给fun
-
fun.apply(obj, 参数值数组)
-
vs call
- apply要求传入fun的参数必须放在数组中整体传入
- apply可自动将数组打散为单个参数值分别传入
-
-
-
-
bind
-
基于一个现有函数,创建一个新函数,并永久绑定this和部分参数
-
何时:
- 只要替换回调函数中的this时
-
如何:
-
var newFun=fun.bind(obj, 参数值列表 )
- 创建一个和fun功能完全一样的新函数
- 永久绑定新函数中的this为obj
- 永久绑定新函数中的部分参数为参数值列表中的值
-
强调:
- 被bind创建的函数中的this和绑定的变量,任何情况下不能再被call替换
-
-
-
-
数组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() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
- function map(arr, mapCallback) { // 首先,检查传递的参数是否正确。 if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') { return []; } else { let result = []; // 每次调用此函数时,我们都会创建一个 result 数组 // 因为我们不想改变原始数组。 for (let i = 0, len = arr.length; i < len; i++) { result.push(mapCallback(arr[i], i, arr)); // 将 mapCallback 返回的结果 push 到 result 数组中 } return result; } }
-
其实map也可像forEach一样使用
-
-
回调函数中的this,指window
-
-
-
过滤和汇总
-
过滤
- 选取arr中符合条件的元素组成新的子数组
- var subArr=arr.filter(function(val,i,arr){ return 判断条件 })
-
汇总
- 将数组中每个元素值汇总出一个结果
- var r=arr.reduce(function(prev,val,i,arr){ return prev+val; }, 初始值)
-
-
-
严格模式
-
何时:
-
- 新项目,都要启用严格模式
-
- 旧项目,逐个模块向严格模式迁移
-
-
如何
-
2个范围
-
整个代码块或js文件启用严格模式
- 在代码块或js文件的开头插入: "use strict";
-
仅在一个函数内启用严格模式
- 在function内,函数体顶部插入: "use strict";
-
-
-
要求:
-
- 禁止给未声明的变量赋值
-
- 静默失败升级为错误
-
- arguments, arguments.callee不推荐使用
-
- 匿名函数自调中的this不再自动指window,而是undefined
-
-
-
-
ES6
-
let
-
声明一个仅在当前块中有效的变量
-
解决:
-
-
为js添加了块级作用域
-
避免了块内的变量污染外部
-
ES5
- 匿名函数自调,来划分临时作用域
-
-
-
避免了声明提前
- 在同一作用域内,let前不允许提前使用未声明的同名变量
-
-
-
何时
- 今后都用let代替var
-
-
参数增强
-
默认值
-
function fun(参数1,参数2,参数3=默认值)
-
ES5
- 参数3=参数3||默认值
-
-
剩余参数
-
代替arguments
-
arguments问题
-
- 不是数组类型,用不了数组的API
-
- 默认只能获得所有参数值,不能选
-
-
ES5
- var args=Array.prototype.slice.call(arguments) //arguments.slice()
-
-
function fun(参数1,参数2,...数组名){ //数组: 除参数1,参数2以外的剩余所有参数 }
-
-
散播
-
代替apply
-
apply问题
-
- 要求必须所有参数必须放在一个数组中
-
- 第一个参数,必须传入一个对象,用于替换this
-
-
-
定义时
-
每个参数单独定义
- function fun(参数1,参数2,参数3,...)
-
-
调用时
-
部分参数值,可放在一个数组中集中传入
-
fun(参数1, ...数组)
- 将数组打散为单个元素,分配给参数1后的每个参数
-
-
-
-
-
解构
-
简化批量赋值
-
数组解构
-
用下标匹配值和变量
- var [a,b,c]=[1,2,3]
- [a,b]=[b,a]
-
-
对象解构
-
用属性名匹配值和变量
- var {x:a,y:b,z:c}={x:1,y:2,z:3}
-
-
参数解构
-
同数组解构和对象解构
-
function fun([a,b,c]){}
- fun([1,2,3])
-
function fun({x:a,y:b,z:c})
- fun({x:1,y:2,z:3})
-
-
-
-
-
模板字符串
-
简化
- 字符串拼接
-
反引号包裹的字符串
xxx -
支持换行
-
支持动态生成内容
- 在``中使用${js表达式}
- 模板字符串会自动执行js表达式的结果,并拼接到最终生成的普通字符串中
-
-
箭头函数
-
简化:
- 回调函数
-
如何
-
去function,在()和{}之间加=>
-
更简化:
-
-
如果只有一个参数,可省略()
- 但是,如果没有参数,必须保留空()
-
-
-
如果函数体只有一句话,可省略{}
-
更简化
-
且一句话还是return
- 则可省略return和{}
-
-
-
-
-
何时
-
几乎所有回调函数都可用箭头函数简化
-
反例
-
如果回调函数中的this和外部的this不相同时,不能简化
-
//this->window elem.addEventListener("click",function(){ //this->当前elem })
-
如果用箭头函数简化,结果内外this都是window
-
其实:
- //this->window elem.addEventListener("click",e=>{ //this->window //e.target->当前elem })
-
-
-
-
-
for of
-
简化:
- 遍历索引数组和类数组对象
-
问题:
-
- 不支持关联数组和对象
-
-
只能读取元素值,不能修改元素值
- 按值传递
-
-
- 只能连续遍历所有
-
-
如何
- for(var val of arr){ val//当前元素值 }
-
-
class
-
简化:
-
面向对象
- 封装,继承,多态
-
-
如何
-
封装
-
- 用“class 类型名{}”包裹原来的构造函数和原型对象方法
-
- 修改构造函数的“function 函数名”为“constructor”
-
-
原型对象方法:
- 可省略开头的“类型.prototype”
- 可省略方法名后的"=function"
-
-
-
添加访问器属性:
- 在构造函数的平级
- get 访问器属性名(){ return this.受保护的其他属性 }
- set 访问器属性名(val){ if(条件) this.受保护的属性=val else 报错 }
-
-
-
继承
-
- 不用再设置Object.setPrototypeOf
-
- 在"class 类型名"后添加" extends 父类型"
-
-
在构造函数中借用父类型构造函数
- 不用call/apply,不用加this
- super(属性值列表)
-
-
-
-
-
集合
-
为什么
- 为了将查找功能从普通对象身上分离出来
-
何时
- 只要希望通过hash方式快速查找时
-
Set
-
元素不能重复的集合
-
何时
- 如果希望通过元素值查找元素时
- 去掉重复元素
-
如何
-
创建
- var set=new Set(凡是可遍历的对象)
-
API
-
set.size
-
判断是否包含
- var bool=set.has(值)
-
将set转为数组
- var arr=[...set]
-
添加/删除元素
- set.add/delete(value)
-
清空
- set.clear();
-
-
-
-
Map
-
用法和普通关联数组完全一样
-
何时
- 如果希望通过key查找元素时
- 希望保存一组简直对儿的集合
-
如何
-
创建
- var map=new Map();
- map.set(key,value)
-
API
- map.size
- map.has(key)
- map.get(key)
- map.delete(key)
-
-
-
遍历
-
for of
-
为了简化普通for循环
-
缺:
-
- 无法获得位置
-
- 只能从头到尾,逐个遍历,不能跳跃或选取
-
- 只能顺序遍历,无法反向遍历
-
-
-
-
-
Promises
-
为什么
- 避免callback hell
-
何时
- 只要要求,下一个函数必须在前一个函数执行后,才能开始执行时
-
如何
-
2步
-
前一个函数结尾
-
返回Promises对象
- return new Promise((resolve, reject)=>{ 如果正确,就继续调用resolve() 否则,就调用reject("错误提示") })
-
-
连续调用两个函数时
-
前一个函数().then(下一个函数) .catch((err)=>{错误处理函数})
- 不要加()
-
-
-
-
多个函数需要连续调用,每个函数结尾,都要返回Promise对象,才能连续用then调用
-
-
一. JavaScript运行环境
1. 浏览器工作原理
-
浏览器内核
-
渲染引擎工作原理
-
WebKit内核
- WebCore
- JavaScriptCore
2. v8引擎工作原理
-
JavaScript引擎
- SpiderMonkey
- Chakra
- JavaScriptCore
- V8引擎
-
V8引擎工作原理
- Parse:转换器
- Ignition:解释器
- TurboFan:编译器
- Orinoco:垃圾回收
3. JavaScript内存管理
-
理解内存管理
- 什么是内存管理
- 手动管理内存
-
常见的GC内存算法
- JavaScript的垃圾回收(Garbage Collection)
- 引用计数算法
- 标记清除算法
- 标记整理算法
- 分代回收算法
-
V8引擎内存管理
- V8的分代算法
- V8内存的分配
- 新生代对象回收
- 旧生代对象回收
-
Performance调试
-
JavaScript内存泄露
4. JavaScript事件循环
-
浏览器的进程模式
- 线程
- 进程
- JavaScript线程
-
浏览器的事件循环
- 宏任务macrotask
- 微任务microtask
- 常见的面试题
-
Node的事件循环
- libuv
- 阻塞IO
- 非阻塞IO
- 宏任务macrotask
- 微任务microtask
- 常见的面试题
二. JavaScript作用域和函数
1. 认识作用域
-
认识作用域
- JavaScript编译和执行
- 深入理解作用域
- 作用域的嵌套
-
词法作用域
- 认识词法分析
- eval函数
- with关键字
-
作用域提升
- JavaScript疑惑面试题分析
- 编译器中的变量声明和提升
- 函数和变量的提升
-
块级作用域
- with作用域
- try...catch...作用域
- let变量声明
- const变量声明
-
作用域相关的面试题
2. 执行上下文
-
执行上下文
- Global EC
- Function EC
- Eval EC
-
变量对象VO
- VO(G)全局变量对象
- AO(Active Object)私有变量对象
- GO(Global Object)全局对象
3. 深入函数执行
-
call/apply执行函数
-
立即执行函数
-
Scopechain
-
函数的生命周期
- 定义
- 调用时
- 调用后
-
函数执行过程的作用域链
-
作用域链面试题
-
-
深入闭包
- 为什么需要闭包
- 闭包的访问规则
- 闭包的应用场景
- 闭包的注意事项
- 闭包相关的面试题
4. 函数的this绑定
-
this指向什么
- 为什么使用this
- this指向什么
-
this绑定规则
-
默认绑定
-
隐式绑定
-
显示绑定
- call/apply/bind
- 内置函数
-
new绑定
-
-
绑定的优先级
- 默认绑定的优先级最低
- 显示绑定优先级高于隐式绑定
- new绑定优先级高于隐式绑定
- new绑定优先级高于bind
-
绑定之外this
- 忽略显示绑定
- 间接函数引用
- ES6箭头函数
-
this相关的面试题
5. 函数的柯里化
-
认识柯里化
- 什么是柯里化(Currying)
- 柯里化有什么作用?
-
实现柯里化
- 案例代码改造
- 柯里化的实现
-
柯里化应用
- 柯里化应用场景
三. JavaScript面向对象
1. 深入理解对象
-
对象的语法
- 对象字面量
- 对象的类型
- 函数对象
-
对象的内容
-
属性和方法定义
-
对象属性描述符
- [[Configurable]]
- [[Enumerable]]
- [[Writable]]
- [[Value]]
- [[Get]]
- [[Set]]
-
访问器属性使用
- Object.defineProperty
- Object.defineProperties
- Object.getOwnPropertyDescriptor
-
对象的属性判断
- hasOwnProperty
- in关键字
- for..in..
- for..of..
-
-
对象的拷贝
- 对象的引用赋值
- 对象的浅拷贝
- 对象的深拷贝
-
ES6对象增强
-
Object.is()
-
简写属性名
-
可计算属性名称
-
简写方法名
-
对象的解构
- 嵌套解构
- 部分解构
- 参数上下文匹配
-
2. 面向对象编程
-
理解面向对象
-
什么是面向对象编程?
-
面向对象编程的特性
- 封装
- 继承
- 多态
-
类和对象的关系
-
-
创建对象方式
-
工厂模式创建
-
构造函数模式
-
原型创建模式
- 认识原型
- prototype 属性
- constructor 的属性
- __proto__属性
-
-
面向对象继承
-
认识原型链
- 深入原型对象
- 简洁的原型语法
- 修改原型的属性
- 深入理解原型链
- 原型和实例的关系
- 原型链存在的问题
-
继承的实现
-
经典继承
- 实现方式
- 存在弊端
-
组合继承
- 实现方式
- 存在弊端
-
原型式继承
- 实现方式
- 存在弊端
-
寄生式继承
- 实现方式
- 存在弊端
-
集成式组合继承
- 释放方式
-
-
-
ES6类的使用
-
class类的定义
- 构造方法
- 属性定义
- 方法定义
-
类的实例化过程
-
类的构建过程解析
-
类的类型
- function类型
-
-
属性分类解析
- 实例属性和方法
- 原型属性和访问器
- static类方法和属性
-
class类的继承
-
extends关键字
-
super函数的使用
- 构造函数
- 普通函数
-
-
Babel的处理
-
Babel工具对class的处理
- 阅读Babel转换后的代码
-
Babel对继承的转换处理
- Babel继承的源码阅读
- _inherits
- _possibleConstructorReturn
- _classCallCheck
-
-
-
面向对象面试题
四. ES6~12新特性讲解
1.新特性查询
2. ES6常见新特性
-
新类型
- Symbol
- Promise
- Reflect
- Proxy
- WeakSet
- WeakMap
- Set
- Map
-
新特性
- generators
- class
- arrow functions
- template string literals
- for...of
- enhanced object literals
- spread syntax
- rest parameter
- default function parameters
3. ES7常见新特性
-
新特性
- Array.prototype.includes()
4. ES8常见新特性
-
新特性
- rest/spread properties
- await
- async
5. ES9常见新特性
-
新特性
- Promise.prototype.finally()
- rest/spread properties
- asynchronous generators and iteration
6. ES10常见新特性
-
新特性
- Array.sort()
- catch without argument
7. ES11常见新特性
-
新特性
- import.meta
- module namespace exports
- globalThis
- optional chaining syntax
- nullish coalescing operator ??
- dynamic import
8. ES12常见新特性
-
新特性
- Promise.any
- logical assignment operators: &&=, ||=, ??=
五. JavaScript异步处理
1. 迭代器和生成器
-
深入迭代器
-
认识迭代器
- 什么是迭代器
- 什么是迭代器模式
-
可迭代类型
- 字符串String
- 数组Array
- 映射Map
- 集合Set
- arguments对象
- 等等...
-
迭代类型操作
- for-of 循环
- 数组解构
- 扩展操作符
- yield*操作符,在生成器中使用
- 等等...
-
迭代器协议
- Symbol.iterator
- iter.next
-
自定义迭代器
- 实现Symbol.iterator
- 实现next
-
终止迭代器
- break、continue、return、throw
- 结构操作值消耗
-
-
深入生成器
-
认识生成器
- 什么是生成器
- 生成器的作用
-
生成器函数
- 创建生成器函数
- 生成器函数的调用
-
yield关键字
- yield中断执行
- yield输入和输出
- 生成可迭代对象
-
生成器和迭代器
-
终止生成器
- return()
- throw()
-
2. Promise的使用
-
异步代码处理
- 代码执行的顺序
- 异步代码执行
- 事件循环
- 并行和并发执行
- 函数的回调
-
Promise的API
-
认识Promise
-
Promises/A+规范
-
Promise的状态
- 待定(pending)
- 完成(fulfilled)
- 拒绝(rejected)
-
-
创建Promise
-
Promise构造器
- resolve
- reject
-
Promise.resolve()
-
Promise.reject()
-
Promise.all([ .. ])
-
Promise.race([ .. ])
-
-
Promise的处理
- then
- catch
- finally
-
Promise链式
-
3. async和await
-
async、await基础
- 和Promise的关系
- await
- async
-
和generator的关系
-
async、await优缺点
六. 模块化和包管理工具
1. JS模块化开发
-
认识模块化开发
- JavaScript设计缺陷
- 没有模块化带来的问题
-
CommonJS规范
- CommonJS和Node的区别和关系
- exports导出
- module.exports导出
- require导入和细节
- 模块的加载顺序
-
AMD和CMD规范
-
CommonJS规范缺点
-
AMD规范
- require.js
- require.config
- require
- define
-
CMD规范
- SeaJS
- define
- require
- exports
- module
-
-
ESModule模块化
- export关键字
- import关键字
- export和import结合
- default用法
- import()异步导入
-
ES Module和CommonJS
- ES Module和CommonJS的区别
- ES Module和CommonJS的交互
2. npm包管理工具
-
认识npm工具
-
package.json
- 必须填写的属性:name、version
- private属性
- main属性
- scripts属性
- dependencies属性
- devDependencies属性
- engines属性
- browserslist属性
-
版本管理的问题
-
semver版本规范
- X主版本号(major)
- Y次版本号(minor
- Z修订号(patch
-
^x.y.z
-
~x.y.z
-
-
npm install解析
-
npm install命令
- 全局和局部安装
-
npm install原理
- package-lock.json
- 查找规则
- 查找缓存
- 等等...
-
npm其他常见命令
-
-
其他工具对比
- yarn
- cnpm
- npx
七. JavaScript特性补充
1. Proxy和Reflect
-
Proxy
-
认识proxy
- Proxy的作用
- Proxy应用场景
-
处理方法
- get
- set
- has
- deleteProperty
- apply
- construct
- getPrototypeOf
- setPrototypeOf
- ownKeys
- 等等...
-
处理参数
- target
- property
- value
- receiver
-
-
Reflect
-
认识Reflect
- Reflect的作用
- Reflect应用场景
-
处理方法
- get
- set
- has
- deleteProperty
- apply
- construct
- getPrototypeOf
- setPrototypeOf
- ownKeys
- 等等...
-
处理参数
- target
- property
- value
- receiver
-
2. JSON的序列化
-
JSON表示形式
- 简单值
- 对象
- 数组
-
JSON的序列化
-
JSON.stringify
-
使用
-
参数
- 过滤器
- 字符串缩进
-
toJSON方法
-
-
JSON.parse
- 使用
-
3. 浏览器API操作
-
浏览器BOM操作
- window对象
- location对象
- history对象
-
-
浏览器DOM操作
- Node节点类型
- DOM的元素操作
- DOM的样式操作
-
-
-
浏览器事件处理
- 事件冒泡
- 事件捕获
- DOM事件流
- 事件对象
- 常见事件类型
-
-
-
浏览器存储方案
- cookie
- localStorage
- sessionStorage
-
八. 手写工具案例练习
1. 节流函数的实现
-
认识防抖函数
-
防抖的三方库
- lodash
- underscore
-
手写防抖函数
- 防抖基本功能
- 优化的参数
- 绑定this
- 优化取消功能
- 优化立即执行
- 优化返回值
2. 防抖函数的实现
-
认识节流函数
-
节流的三方库
- lodash
- underscore
-
手写节流函数
- 节流基本功能
- 优化参数和this
- 优化最后执行功能
- 优化取消功能
- 优化返回值
3. 实现深拷贝案例
-
认识深拷贝
-
拷贝的三方库
- lodash
- underscore
-
手写深拷贝函数
- 深拷贝基本功能
- 递归拷贝功能
- 不同类型的处理
- 函数的拷贝功能