JavaScript 学习笔记
基础
- JavaScript 组成:
- ECMAScript:JavaScript 语言基础
- Web APIs
- DOM:页面文档对象模型
- BOM:浏览器对象模型
具体
书写位置
-
书写位置:
<script>放在<body>尾部-
行内:
<body> <button onclick=""></button> </body> -
内部:
<body> <script> <!-- code --> </script> </body> -
外部:只执行
src导入的js文件内容,忽略<></>包裹的内容<body> <script src=""></script> </body>
-
注释
- 单行注释:
// - 块注释:
/**/
结束符
- 可写可不写:
;
输入与输出
-
输出:
// 文字 document.write('content') // 元素 document.write('<h1>content</h1>') // 警告对话框 alert('content') // 控制台调试 console.log('content') -
输入:返回用户输入,返回值为字符串类型
prompt('content')
alert()和prompt()会跳过页面渲染先被执行
变量
-
声明:
let关键字,不允许两个变量名重复let 变量名 -
赋值:
=赋值let a a = 0声明同时赋值:
let a = 0更新:
let a = 0 a = 1声明多个:
let a = 0, b = 'hello'输出:
let a = 0, b = 1 console.log(a, b) -
变量名规则:
- 不能与关键字重复
- 只能用下划线、字母、数字、$ 组成,且不能以数字开头
- 大小写敏感
数组
-
声明:
let 数组名 = [数据1, 数据2, ..., 数据n] let 数组名 = new Array(数据1, 数据2, ..., 数据n)索引:
数组名[索引]索引超出数组长度时取出的值为
undefined长度:
.length添加到末尾:返回数组新长度
.push(data1, ..., dataN)添加到开头:返回数组新长度
.unshift(data1, ..., dataN)删除末尾:返回该元素的值
.pop()删除开头:返回该元素的值
.shift()删除指定起点、指定数量的元素:
deleteCount不写时删除到末尾.splice(start, deleteCount)排序:
// 默认升序 .sort() // 升序 .sort(function(a, b) { return a - b }) // 降序 .sort(function(a, b) { return b - a })map:遍历数组处理数据,返回新数组const newArr = arr.map(function (ele, index) { ... return ... })join:将数组中所有元素转换成一个字符串。参数为分隔符,不写为,arr.join('')
常量
-
声明:必须赋值,后续不允许修改
const 常量名 = 值
数据类型
- 数据类型:
- 基本数据类型:
- number 数字
- string 字符串
- boolean 布尔
- undefined 未定义
- null 空
- 引用数据类型:
- object 对象
- 基本数据类型:
数字类型
弱数字类型
字符串类型
-
声明:
'string' "string" `string`拼接:
+'a' + 'b' 'age: ' + 18模板字符串:使用反引号 `` 包裹,拼接字符串与变量,使用
${}`leftString${variant}rightString`去除两边空格:
.trim()
布尔类型
-
true | false
未定义类型
-
变量只声明不赋值时的值
undefined
空类型
null
undefined代表不确定,null代表没有
检测数据类型
-
typeof x typeof(x)
类型转换
-
隐式转换
// + 双目运算符: 有字符串转换为字符串 1 + '1' // 11 // - 单目运算符: 转换为数字型 +'1' // 1 // - * / : 转换为数字型 2 - '1' // 1 2 * '1' // 2 2 / '1' // 2 -
显式转换
Number() Boolean() // '' 0 undefined null false NaN 都为 false parseInt() // 整数,可以去除非数字后缀 parseFloat() // 小数,可以去除非数字后缀
运算符
赋值运算符
-
= += -= *= /= %=
一元运算符
-
// 分为前置与后置。区别与C语言一致 ++ --
比较运算符
-
> < >= <= == // 值是否相等 != // 是否不等 === // 类型与值都相等 !== // 是否不全等
逻辑运算符
-
&& || ! -
逻辑中断:为未定义值赋值
x = x || 0
语句
分支语句
-
if语句if () { } else if { } else if { ... } else { } -
三元运算符
cond ? stmt1 : stmt2 -
switch语句:与每一个value作全等比较switch (key) { case value1: stmt1 break ... case valueN: stmtN break default: stmt break }
循环结构
-
while循环while (cond) { stmt1 } -
for循环for (stmt1; cond; stmt2) { stmt3 } -
循环退出
continue break
函数
-
声明:
function 函数名(参数列表) { 函数体 } -
命名建议:
动词 含义 can是否可执行 has是否含有 is是否是 get获取 set设置 load加载 -
返回值:
return 返回值 -
后声明的函数会覆盖前面的同名函数:
function fn() { console.log('1') } function fn() { console.log('2') } fn() // 输出:2 -
匿名函数:
-
函数表达式:将匿名函数赋值给变量,通过变量名调用
let fn = function() {} fn()类似于具名函数,但是具名函数的调用可以在声明之前,函数表达式由于
let关键字限制调用必须在变量赋值之后 -
立即执行函数:避免全局变量之间的污染
// 写法 1 (function() { 函数体 })(); // 写法 2 (function() { 函数体 }());注意:立即执行函数必须写结束符
;
-
对象
-
声明:
// 字面量 let 对象名 = { 属性名: 属性值, ... 方法名: 函数, ... } // 默认构造函数 let 对象名 = new Object({ 属性名: 属性值, ... 方法名: 函数, ... }) // 自定义构造函数 function 构造函数名(参数1, 参数2, ...) { this.参数1 = 参数1 this.参数2 = 参数2 ... } const obj = new 构造函数名(实参1, 实参2, ...) -
组成:
- 属性
- 方法
-
访问对象内部成员:
// 方法 1 对象名.成员名 // 方法 2 对象名['属性名'] -
修改或添加新的对象内部成员值:
对象名.成员名 = value -
删除:
delete 对象名.成员名 -
遍历:
for infor (let k in obj) { console.log(obj[k]) } -
内置对象:
-
Math:random ceil floor max min pow abs随机数函数:
Math.random(),返回随机小数// 0-10 整数 Math.floor(Math.random() * (10 + 1)) // 5-10 整数 Math.floor(Math.random() * (5 + 1)) + 5 // N-M 整数 Math.floor(Math.random() * (M - N + 1))) + N -
静态成员与静态方法:
构造函数名.属性名 = 属性值 构造函数名.函数名 = 函数体
-
Web APIs
DOM (Document Object Model) 文档对象模型
-
获取 DOM 对象:
-
借助 CSS 选择器
-
选择匹配的第一个元素
document.querySelector('CSS 选择器') -
选择匹配的所有元素
document.querySelectorAll('CSS 选择器')
-
-
其他方法:现在并不常用
document.getElementById() document.getElementsByTagName() document.getElementsByClassName()
-
-
操作元素内容:
.innerText:显示纯文本,不解析标签.innerHTML:解析标签
-
操作元素属性:
-
操作元素常用属性:
.src .title ... -
操作元素样式属性:
-
通过
style属性操作:.style.width .style.marginTop ... -
通过
className操作:执行className.className = 'className' -
通过
classList操作类// 追加 .classList.add('类名') // 删除 .classList.remove('类名') // 切换:有就删掉,没有就加上 .classList.toggle('类名') // 查询:是否包含 .classList.contains('类名')
-
-
操作表单元素属性:
.type .value .checked = true | false .disabled = true | false ... -
自定义属性:
-
标签上以
data-开头:<div data-id="10"></div> -
获取:
.dataset.id
-
-
-
定时器-间歇函数:
-
开启定时器:间隔时间单位:
ms。返回值代表当前定时器idsetInterval(函数,间隔时间) -
关闭定时器:
clearInterval(id)
-
-
事件监听:
.addEventListener('事件类型', 要执行的函数)事件类型:
-
鼠标事件:
click mouseenter // 不冒泡 mouseleave // 不冒泡 mouseover // 冒泡 mouseout // 冒泡 -
焦点事件:
focus blur -
键盘事件:
keydown keyup -
文本事件:
input
事件对象:
-
获取事件对象:回调函数的第一个参数
e.addEventListener('', function(e) {}) -
事件对象常用属性:
type 事件类型 clientX/clientY 光标相对于浏览器可见窗口左上角的位置 offsetX/offsetY 光标相对于当前 DOM 元素左上角的位置 key 键盘值 pageX/pageY 鼠标在页面中的坐标
环境对象:函数被调用者
回调函数:函数作为函数实参
-
-
事件流:事件完整执行过程中的流动路径
-
捕获:从父到子
.addEventListener('事件类型', 要执行的函数, true) -
冒泡:从子到父
-
阻止传播(捕获与冒泡):
事件对象.stopPropagation() -
解绑事件:
removeEventListener('事件类型', 要执行的函数)匿名函数无法解绑
-
-
事件委托:利用事件冒泡,将事件注册到父元素身上
事件对象.target // 获取触发的元素对象 事件对象.tagName // 获取触发的元素对象标签名,注意大写 -
阻止默认行为:链接的跳转,表单域跳转等
事件对象.preventDefault() -
其他事件:
-
页面加载事件:写在
<body>前需要等待页面加载完毕// 页面全部加载完 window.addEventListener('load', function() {}) // 无需等待样式表、图像完全加载 document.addEventListener('DOMContentLoaded', function() {}) -
页面滚动事件:滚动条滚动时触发
window.addEventListener('scroll', function() {})滚动属性:
.scrollLeft .scrollTopHTML 元素:
document.documentElement滚动到指定的坐标:
window.scrollTo(x, y) -
页面尺寸事件:
window.addEventListener('resize', function() {})获取宽高:不包含边框,
margin,滚动条等.clientWidth .clientHeight -
页面平滑滑动:
html { scroll-behavior: smooth; }
-
-
元素尺寸与位置:要求可视,
// 包含 padding, border 等,位置相对于定位的父级元素 .offsetWidth .offsetHeight .offsetLeft .offsetTop // 相对于视口来说 element.getBoundingClientRect() -
日期对象:
-
实例化:
// 当前时间 const date = new Date() // 指定时间 const date = new Date('xxxx-xx-xx') const date = new Date('xxxx-xx-xx xx:xx:xx') -
日期对象方法:
getFullYear() getMonth() getDate() getDay() getHours() getMinutes() getSeconds() toLocaleString() toLocaleDateString() toLocaleTimeString() -
时间戳:
// 三种获取方法 getTime() +new Date() Date.now()
-
-
节点操作:
-
节点类型:
- 元素节点:标签
- 属性节点:属性
- 文本节点:文本
- 其他
-
查找节点:
-
父节点:找不到返回
null.parentNode -
子节点:
.childNodes // 获得所有子节点,包括文本节点、注释节点等 .children // 仅获得所有元素节点,返回伪数组 -
兄弟节点:
nextElementSibling previousElementSibling
-
-
增加节点:
-
创建节点:
document.createElement('标签名') -
追加节点:
// 插入到父元素最后一个子元素 .appendChild(元素) // 插入到父元素某个子元素的前面 .insertBefore(要插入的元素, 指定位置的元素) -
克隆节点:默认
false// true: 同时克隆后代节点 | false: 不包含后代节点 元素.cloneNode(布尔值)
-
-
删除节点:
.removeChild(元素)
-
-
M 端事件:
-
touch事件:touchstart // 触摸到 touchmove // 滑动 touchend // 移开时
-
-
JS 插件:swiper 插件
BOM (Browser Object Model) 浏览器对象模型
-
定时器-延时函数:延时执行一次就结束
setTimeout(回调函数, 等待的毫秒数) clearTimeout(id) -
JS 执行机制:单线程。先执行同步任务再执行异步任务
-
location对象:// 赋值以跳转 location.href = 'url' // 地址 ? 后的参数 location.search // 刷新当前页面, true 强制刷新 location.reload(true) -
navigator对象:浏览器自身相关信息// 浏览器版本及平台 navigator.userAgent例:检测平台
(function() { const userAgent = navigator.userAgent const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/) const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/) if (android || iphone) { location.href = 'm.url' } })(); -
history对象:back() // 后退 forward() // 前进 go(参数) // -n | n 代表后退 | 前进 n 个页面 -
本地存储:以字符串形式存储
-
localStorage:-
存储数据:键值均为字符串类型
localStorage.setItem(key, value) -
获取数据:
localStorage.getItem(key) -
删除数据:
localStorage.removeItem(key)
-
-
sessionStorage:生命周期是浏览器窗口 -
存储复杂数据类型:转换成 JSON 字符串
localStorage.setItem(key, JSON.stringify(value))JSON 字符串转对象:
JSON.parse(localStorage.getItem(key))
-
-
正则表达式:
-
语法:
// 定义正则表达式 const 规则名 = /表达式/ // 判断是否符合规则,匹配为 true,不匹配为 false 规则名.test(被检测字符串) // 判断是否符合规则,匹配成功返回数组,不匹配为 null 规则名.exec(被检测字符串) -
元字符:
-
边界符:
// 开始 ^ // 结束 $ -
量词:
// >= 0 * // >= 1 + // 0 | 1 ? // n {n} // >= n {n,} // >= n && <= m {n,m} -
字符类:
// 包含任意一个字符 [] [a-z] // 连字符表示范围 [A-Z] [0-9] [a-zA-Z0-9] [^a-z] // [] 里的 ^ 表示取反 // 匹配除换行符外任意单个字符 . // 或 | // 预定义 \d // [0-9] \D // [^0-9] \w // [A-Za-z0-9_] \W // [^A-Za-z0-9_] \s // [ \t\r\n\v\f] \S // [^ \t\r\n\v\f] -
修饰符:
/表达式/修饰符 i // 不区分大小写 g // 匹配所有满足的结果
-
-
替换:替换全部需要加修饰符
gstr = str.replace(/表达式/, '替换文本')
-
进阶
-
作用域:
- 局部作用域:
- 函数作用域
- 块作用域
- 全局作用域:
<script>标签内.js文件中
- 作用域链:变量查找机制
- 局部作用域:
-
垃圾回收机制:
- 引用计数法
- 标记清除法
-
闭包:内存函数 + 外层函数的变量
function f() { const a = 1 function add() { a++ } return add } const add = f() add() // a = 2 add() // a = 3 -
变量提升:使用
var时会出现console.log(a) // undefined var a = 1var型变量未声明时会自动在当前作用域最前面声明,但不赋值,相当于:var a console.log(a) a = 1 -
函数进阶:
-
函数提升:类似于变量提升自动在作用域最前面声明
函数表达式不能提升
-
函数参数:
-
动态参数
arguments:函数内置的伪数组,包含所有实参 -
剩余参数:形参
...后的自定义真数组,接收剩余参数function(int a, int b, ...arr) {}展开运算符:展开数组
const arr = [1, 2, 3] console.log(...arr) // 1 2 3使用场景:参数传递,数组合并等
Math.max(...arr)[...arr1, ...arr2]
-
-
箭头函数:替代某些匿名函数
(形参) => {}特殊的:形参只有一个参数时可以简化为
单个形参 => {}特殊的:只有一行代码时可以省略大括号,并且默认值为该行代码运行结果
(形参) => 单行代码如果返回对象时,避免大括号歧义:使用小括号包裹对象
(a) => ({a: a})箭头函数没有动态参数
arguments,但是有剩余参数箭头函数中的
this指向的是上一层普通函数的this或全局对象,而不一定是调用者
-
-
解构赋值:
-
数组解构:
const [var1, var2, ...] = arr例:变量值交换
;[a, b] = [b, a]注意前面要加
;,防止歧义可以使用剩余数组存储多余值:
const [a, b, ...arr] = [1, 2, 3, 4]此时
arr是真数组支持默认参数:
const [a = 1, b = 0] = []选择性赋值:
const [a, b, , d] = [1, 2, 3, 4] // d = 4 -
对象解构:
const {a, b} = {a: a, b: b}注意左侧变量名与右侧属性名必须相同
重命名:
旧: 新const {a: na, b: nb} = {a: a, b: b}多级解构:
const {a, b: {c, d}} = {a: a, b: {c: c, d: d}}
-
-
forEach遍历数组:arr.forEach(function(item, index) {})只遍历,不返回数组(与
map区分) -
filter:筛选数组元素,true为接受,类似于map返回处理后的新数组arr.filter(function(item, index) { return true }) -
内置构造函数:
-
Object:Object.keys(obj) Object.values(obj) Object.assign(target, source) // 追加或修改属性 -
Array:const arr = new Array(1, 2, 3, ...) // 实例方法: forEach filter map reducereduce:累计处理,例如求和函数arr.reduce(function(previous, current) { return previous + current }, 起始值)数组中每一项不是数字时必须给起始值,可以为 0
其他方法:
join find every some concat sort splice reverse findIndex伪数组转换为真数组:
Array.from(arr) -
String:length split('分隔符') substring(首字符索引[, 尾字符索引]) // 不包含尾字符 startsWidth(字符串[, 检测位置索引]) includes(字符串[, 检测位置索引]) toUpperCase toLowerCase indexof endsWidth replace match -
Number:toFixed(小数位位数) // 四舍五入 num.toString() | String(num) // 转换为字符串
-
-
对象:
-
原型:
构造函数.prototype -
添加公共函数:
// 公共的方法应写在原型身上,追加 构造函数.prototype.函数名 = 函数体 // 一次赋值多个函数,需要带上构造函数 构造函数.prototype = { constructor: 构造函数, 函数名1: 函数体1, 函数名2: 函数体2, ... } -
原型的构造函数:指向
构造函数构造函数.prototype.constructor -
对象原型:
实例对象.__proto__ // 只读 -
原型继承:
function Father() {} function Son() {} Son.prototype = new Father() -
原型链:继承的实现方法,通过原型链查找属性方法
// 非 Object 对象的prototype.__proto__ === Object.prototype // Object.prototype.__proto__ === nullinstanceof实例是否属于原型链上的某类型
-
-
深浅拷贝:
-
浅拷贝:拷贝的地址
Object.assign() {...obj} Array.prototype.concat() [...arr] -
深拷贝:
-
递归:注意
Array分支在Object分支之前function deepCopy(newObj, oldObj) { for (let k in oldObj) { if (oldObj[k] instanceof Array) { newObj[k] = [] deepCopy(newObj[k], oldObj[k]) } if (oldObj[k] instanceof Object) { newObj[k] = {} deepCopy(newObj[k], oldObj[k]) } else { newObj[k] = oldObj[k] } } } -
JS 库
loadash:const newObj = _.cloneDeep(oldObj) -
字符串化:
const newObj = JSON.parse(JSON.stringify(oldObj))
-
-
-
异常处理:
-
throw:直接中断throw 'message' throw new Error('message') -
try/catch/finally:不中断try {} catch(e) {} finally {}finally一定会执行 -
debugger:断点debugger
-
-
this指向:-
call():thisArg为指定的this值,其余为实参传入函数fun.call(thisArg, arg1, arg2, ...) -
apply():thisArg为指定的this值,其余参数放入数组fun.apply(thisArg, [argsArray])例:方便数组展开
Math.max.apply(null, arr) -
bind():不调用函数,返回新函数fun.bind(thisArg, arg1, arg2, ...)例:不立即调用
btn.addEventListener('click', function () { this.disabled = true setTimeout(function () { this.disabled = false }.bind(this), 2000) })
-
-
防抖:只执行最后一次
-
Lodash防抖函数:单位为ms_.debounce(func, [wait]) -
手写防抖函数:
function debounce(func, wait) { let timer return function() { timer && clearTimeout(timer) timer = setTimeout(func, wait) } }
-
-
节流:只执行一次
-
Lodash节流函数:_.throttle(func, wait) -
手写节流函数:定时器开启内无法清除
function throttle(func, wait) { let timer return function() { if (!timer) { timer = setTimeout(function() { func() timer = null }, wait) } } }
-

Comments | NOTHING