TIPS
前端JavaScript学习笔记
# 特点
- 解释性
- 单线程
# JavaScript三大部分
- ECMAScript
- DOM(操作HTML)
- BOM(操作浏览器)
# 值类型
原始值
指的是 原始类型 的值, 也叫 基本类型, 存储在 栈(stack) 中的简单数据段, 也就是说, 它们的值直接存储在变量访问的位置,
Number,String,Boolean,Undefined,Null,Symbol,BigInt引用值
指的是 引用类型 的值, 存储在 堆(heap) 中的对象, 也就是说, 存储在变量处的值是一个 指针(point), 指向存储对象的 内存 处,
Object,Function,Array,Date,RegExpWARNING
在许多语言中, 字符串都被看作引用类型, 而非原始类型, 因为字符串的长度是可变的, ECMAScript 打破了这一传统
# 值的比较与计算
任何数据类型加字符串或者字符串加任何数据类型都等于字符串
与运算符: &&
先看第一个表达式, 如果布尔值为假, 则返回第一个表达式值, 如果为真, 就返回第二个表达式的值, 如果是多个表达式依次往后看. 全真为真, 碰见假的就停
或运算符: ||
先看第一个表达式, 如果布尔值为真, 则返回第一个表达式值, 如果为假, 就看第二个表达式的值, 如果是多个表达式依次往后看. 全假为假 , 碰见真的就停
typeof返回值的类型 (opens new window)
类型 结果 Undefined "undefined" Null "object" Boolean "boolean" Number "number" BigInt(ECMAScript 2020 新增) "bigint" String "string" Symbol(ECMAScript 2015 新增) "symbol" 宿主对象(由JS环境提供) 取决于具体实现 Function "function" 其他任何对象 "object" 布尔值为false的值
false(布尔值的false, 字符串"false"布尔值为true)undefinednull(代表空值)0(数字0, 字符串"0"布尔值为true)'',""(双引号或单引号的空字符串, 中间有空格时也是true)NaN(无法计算结果时出现, 表示"非数值", 但是typeof NaN === "number"为true)
WARNING
注意空数组(
[]), 空对象({}), 负值转的布尔值时都为true显示类型转换
Number(mix)把字符串转换成数字类型parseInt(string, radix)解析一个字符串并返回指定基数的十进制整数, radix表示以该进制转换成十进制parseFloat(string)把字符串转换成浮点型String(mix)转换成字符串toString(radix)转换成字符串,demo.toString(), (undefined和Null不能用),Number.prototype.toString(radix):radix表示转换成目标进制的字符串(范围2-36,其他报错,默认为10)Boolean()转换成布尔值
隐式类型转换
isNaN()调用Number();++/--,+/-(一元正负) 调用Number();+如果两侧有字符串则调用String();-*/%调用Number();&&||!<>>=<===!====!==
值的比较
undefined == null // true undefined === null // false NaN == NaN // false NaN === NaN // false // NaN 不等于任何值 // 原始值比较的是值, 引用值比较的是地址1
2
3
4
5
6
WARNING
JavaScript计算的精度 小数点前16位, 小数点后16位
# 函数
函数声明
function test(){}1函数表达式
var test = function abc (){} // 匿名函数 var test = function (){}1
2
3构造函数
var test = new Function("a", "b", "return a * b")1
TIPS
在函数最后加上return this可以实现函数的连续调用
# JS运行三部曲
- 语法分析, 通篇扫描
- 预编译
- 解释执行
WARNING
imply global暗示全局变量: 即任何变量未经声明就赋值, 此变量就为全局对象所有(全局window)
一切声明的全局变量, 全是window属性, var a = 123; window.a=123; window就是全局的域
# 预编译四部曲
- 创建AO对象(Activation Object)(执行期上下文)
- 找形参和变量声明, 将变量和形参名作为AO属性名, 值为undefined
- 将实参值和形参统一
- 在函数体内找到函数声明, 值赋予函数体
WARNING
[[scope]]就是我们所说的作用域, 存储了执行期上下文的集合, 这个集合程链式链接, 叫做作用域链
# 立即执行函数
此函数没有声明, 在一次执行过后即释放, 适合初始化工作, 针对初始化功能的函数(只调用一次或者只需要返回值)
只有表达式才能被执行符号执行 (), 执行之后名称失效
(function(形参) {} (实参))
// 或者
(function() {}) ()
// 例:
var num = (function(a,b) {
return a+b;}(1,2)
)
2
3
4
5
6
7
# 闭包
当内部函数被保存到外部时, 将会生成闭包, 闭包会导致原有作用域链不释放, 造成内存泄漏
闭包的作用
实现共有变量, 如函数累加器
var count = 0; function test(){ count ++; console.log(count); } // 或者不用全局变量 function add(){ var num = 0; function a(){ console.log(++num); } return a; } var myAdd = add(); myAdd(); myAdd(); myAdd(); myAdd();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18可以做缓存
可以实现封装, 属性私有化
模块化开发, 防止污染全局变量变量
getter和setter
# 对象
对象字面量/对象直接量
var obj = {}1构造函数
系统自带的构造函数
// Object() // Array() // Number() var obj = new Object();1
2
3
4自定义(结构上和函数没有区别)大驼峰命名规则
var person1 = new Person(); // new操作符隐式的在开始会有 var this = {}, 在结束时有 return this1
2
TIPS
包装类, 每当我们给原始值赋属性值时, 后台都会给我们偷偷转换 —— 调用包装类
- new String();
- new Boolean();
- new Number();
WARNING
未经声明访问会报错, 对象的属性未经声明访问报 undefined
解释程序则需尝试判断该值是否为 ECMAScript 的原始类型之一, 即 Undefined, Null, Boolean, Number 和 String 型, 原始值没有属性和方法
# 原型
定义
原型是
function对象的一个属性, 它定义了构造函数制造出的对象的公共祖先, 通过该构造函数产生的对象, 可以继承该原型的属性和方法, 原型也是对象利用原型特点和概念, 可以提取共有属性
对象如何查看原型 -> 隐式属性
__proto__对象如何查看对象的构造函数 ->
constructor
绝大多数对象的最终都会继承自 Object.prototype
Object.create(原型)
WARNING
__proto__ 是每个对象都有的一个属性, 而 prototype 是函数才会有的属性!!!
prototype 和__proto__ 都指向原型对象, 任意一个函数(包括构造函数)都有一个 prototype 属性, 指向该函数的原型对象, 同样任意一个构造函数实例化的对象, 都有一个 __proto__ 属性
# 继承发展史
传统形式 -> 原型链: 过多的继承了没用的属性
借用构造函数 call/apply: 不能继承借用构造函数的原型, 每次构造函数都要多走一个函数
共享原型: 不能随便改动自己的原型
extend/inherit, Target.prototype=Origin.prototype
圣杯模式(也是公有原型)加中间函数
function inherit(Target, Origin) { function F() {} F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constuctor = Target; Target.prototype.user = Origin.prototype; // 最终父级 }1
2
3
4
5
6
7
# 对象的遍历
for...in循环, 打印自身和自己设置的原型上的属性hasOwnProperty判断是否自己的属性A instanceof B, A对象是不是B构造函数构造出来的, 看A的原型链上有没有B的原型
for(var prop in obj){
// prop为一个变量, 不是属性, 不能obj.prop obj.['prop']
console.log(obj[prop]);
}
2
3
4
# 区别数组和对象
// constructor
([]).constructor // Array()
({}).constructor // 是Object()
// instanceof
([]) instanceof Array // true
({}) instanceof Array // false
([]) instanceof Object // true
({}) instanceof Object // true
// toString
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
2
3
4
5
6
7
8
9
10
11
12
13
TIPS
逗号操作符: 返回逗号后面的值
1,2 // 2
function test() {}
test,2 // 2
2
3
# this
- 函数预编译过程 this -> window
- 全局作用域里 this -> window
- calI/apply 可以改变函数运行时this指向
- obj.function(); function()里面的 this 指向 obj (谁调用这个函数, this就指向谁)
arguments.callee 指向函数自身引用, 比如在立即执行函数里使用
function.caller 指向调用者, 即在哪里被调用的
TIPS
call/apply
作用: 改变this指向 区别: 后面传的参数形式不同 call: 需要把实参按照形参的个数传进去 对象+ 实参 apply: 需要传一个arguments 对象 + 实参数组
三目运算符
条件判断 ? 值1 : 值2, 真则返回值1, 假则范围值2
# 数组
# 数组的定义
// 构造函数
var arr1 = new Array(length/content); // 如果只写一个值并且数number类型则表示数组长度
// 字面量
var arr2 = [1, 2, 3, 4, 5]
2
3
4
# 数组的读和写
- 可以溢出读, 结果为
undefined, 此时数组类似对象, num对象的属性
arr[index]
2. 可以溢出写
arr[index] = xxx, 中间的值为 empty, 取值为 undefined
# 数组常用的方法
改变原数组
push(在数组最后添加数据), 返回最新长度
Array.protorype.push = function (target) { this[this.length] = target this.length++ }1
2
3
4pop(把数组最后一位剪切出来, 不能传参数), 返回剪切的值
shift(在数组前面删除数据), 返回删除的值
unshift(在数组最前面添加数据, 和push一样), 返回最新长度
sort(把原数组升序排列), 返回排序后的新数组 可以自定义排序规则, 接收一个函数, 函数有两个参数, 排序规则根据返回值决定
- 当返回值为负数时, 那么前面的数放在前面
- 为正数, 那么后面的数在前
- 为0, 不动
升序:
return a - b, 降序:return b - a, 乱序:return Math.random() - 0.5
reverse(逆转数组的顺序), 返回逆序后的新数组
splice(从第几位开始, 截取多少长度(可以截取0个), 从切口处添加新的数据(相当于在此push), 返回被截取的数据
不改变原数组
- concat(把后面的数组拼接在前面的数组上形成一个全新的数组), 返回最新数组
- join(让数组中的数据用传入的字符串连接起来, 不传就按逗号连接), 返回字符串
- split(把字符串按照传入的字符串拆分成数组), 返回拆分后的数组
- toString(把数组重写成字符串), 效果和
join(',')一样, 返回字符串 - slice 截取, 返回被截取的数组
- 两个参数(从第几位开始, 截取到第几位不包括这位,
(start, end]) - 一个参数(从第几位开始一直截取到最后) 没有参数就是截取整个长度
- 两个参数(从第几位开始, 截取到第几位不包括这位,
# 类数组
- 可以利用属性名模拟数组的特性
- 可以动态的增长
length属性 - 如果强行让类数组调用
push方法, 则会根据length属性值的位置进行属性的扩充
属性要为索引(数字)属性, 必须有 length 属性, 最好加上 push
try-catch
try{
} catch(e){
console.log(e.name + e.message)
}
// 在try里面发生错误, 不会执行错误后的try里面的代码
// Error.name的六种值对应的信息
// 1. EvalError:eval()的使用与定义不一致
// 2. RangeError:数值越界
// 3. ReferenceError:非法或不能识别的引用数值
// 4. SyntaxError:发生语法解析错误
// 5. TypeError:操作数类型错误
// 6. URIError:URI处理函数使用不当
2
3
4
5
6
7
8
9
10
11
12
13
"use strict", ES5.0严格模式启动, 写在页面逻辑的最顶端或者局部函数的最顶端(推荐), 不再兼容ES3的一些不规则语法, 使用全新的ES5规范
两种用法:
- 全局严格模式
- 局部函数内严格模式(推荐)
就是一行字符串, 不会对不兼容严格模式的浏览器产生影响
不支持with(改变作用域链, 让里面的执行直接访问制定的作用域, 如 with(document) { write('a') }),arguments.callee, func.caller, 变量赋值前必须声明, 局部this必须被赋值(Person.call(null/undefined)赋值什么就是什么), 拒绝重复属性和参数(但是目前不会报错)
# DOM(Document Object Model)
DOM定义了表示和修改文档所需的方法, DOM对象即为宿主对象, 由浏览器厂商定义, 用来操作html和xml功能的一类对象的集合, 也有人称DOM是对HTML以及XML的标准编程接囗
# 节点的属性与增删改查
查看元素节点
- ducument 代表整个文档
- document.getElementByld() 元素id在IE8以下的浏览器, 不区分id大小写, 而且也返回匹配name属性的元素
- .getElementsByTagName() 标签名
- getElementsByName() 需注意, 只有部分标签name可生效(表单, 表单元素, img, iframe)
- .getElementsByClassName() 类名, IE8和IE8以下的IE版本中没有, 可以多个class一起
- .querySelector() 在IE7和IE7以下的版本中没有
- .querySelectorAIl() 在IE7和IE7以下的版本中没有(query选出的元素不是实时的)
遍历节点树
- parentNode 父节点(最顶端的parentNode为#document);
- childNodes 子节点们
- firstChiId 第一个子节点
- lastChiId 最后一个子节点
- nextSibling 后一个兄弟节点
- previousSibling 前一个兄弟节点
基于元素节点树的遍历
- parentElement 返回当前元素的父元素节点(IE9以下不兼容)
- children 只返回当前元素的元素子节点
- node.childElementCount === node.children.length当前元素节点的子元素节点个数(IE不兼容)
- firstElementChild 返回的是第一个元素节点(IE不兼容)
- lastElementChild 返回的是最后一个元素节点(IE不兼容)
- nextElementSibling/previousElementSibling 返回后一个/前一个兄弟元素(IE不兼容)
节点的四个属性
- nodeName: 元素的标签名, 以大写形式表示, 只读
- nodeValue: Text节点或Comment节点的文本内容, 可读写
- nodeType: 该节点的类型, 只读
- attributes: Element节点的属性集合
- 节点的一个方法Node.hasChildNodes();
节点的类型
- 元素节点 1
- 属性节点 2
- 文本节点 3
- 注释节点 8
- document 9
- DocumentFragment 11
TIPS
- getElementByld方法定义在Document.prototype上, 即Element节点上不能使用
- getElementsByName方法定义在HTMLDocument.prototype上, 即非html中的document不能使用(xml document,Element)
- getElementsByTagName方法定义在Document.prototype和Element.prototype上
- HTMLDocument.prototype定义了一些常用的属性, body, head,分别指代HTML文档中的
<body><head>标签 - Document.prototype上定义了documentElement属性, 指代文档的根元素, 在HTML文档中, 他总是指代
<html>元素 - getElementsByClassName, querySelectorAll, querySelector 在 Document.prototype, Element.prototype类中均有定义
增
- document.createElement();创建元素节点
- document.createTextNode();创建文本节点
- document.createComment();创建注释节点
- document.createDocumentFragment();创建文档碎片节点
插
- PARENTNODE.appendChild();把元素插入(已有的就剪切)到PARENTNODE
- PARENTNODE.insertBefore(a,b);在a插入到b之前
删
- parent.removeChild();剪切式删除
- child.remove();
替换
- parent.replaceChild(new,origin);
Element元素节点的一些属性
- innerHTML 查看元素的HTML(是获取该标签里面的所有元素, 包括其他标签)
- innerText(火狐不兼容)/textContent(老版本IE不好使) 查看元素的文本信息(是获取标签里面的文本, 不会识别其它标签)
Element节点的一些方法
- ele.setAttribute() 设置属性(前面属性名, 后面属性值)
- ele.getAttribute() 获取行间属性的值
# JS定时器
setInterval(); // 定时循环
setTimeout(); // 定时执行, 只执行一次
clearInterval();
clearTimeout();
// 全局对象window上的方法, 内部函数this指向window
2
3
4
5
# 滚动
查看滚动条的滚动距离
- window.pageXOffset/pageYOffset (IE8及IE8以下不兼容)
- document.body/documentElement.scrollLeft/scrolITop
兼容性比较混乱, 用时取两个值相加, 因为不可能存在两个同时有值(必有一个为0)
查看视囗的尺寸
- window.innerWidth/innerHeight IE8及以下不兼容
- document.documentElement.clientWidth/clientHeight 标准模式下, 任意浏览器都兼容
- document.body.cIientWidth/clientHeight 适用于怪异模式下的浏览器
查看元素的几何尺寸
- dom.getBoundingClientRect(); 兼容性很好
该方法返回一个对象, 对象里面有left, top, right, bottom等属性, left 和 top 代表该元素左上角的 x 和 y 坐标, right 和 bottom 代表元素右下角的 x 和 Y 坐标 height 和 width 属性老版本IE并未实现, 返回的结果并不是实时的
查看元素的尺寸
- dom.offsetWidth
- dom.offsetHeight
查看元素的位置
- dom.offsetLeft
- dom.offsetTop
对于无定位父级的元素, 返回相对文档的坐标, 对于有定位父级的元素, 返回相对于最近的有定位的父级的坐标
- dom.offsetParent 返回最近的有定位的父级, 如无, 返回body, body.offsetParent返回null
让滚动条滚动
window上有三个方法: scroll(), scroIITo(), scrollBy()
三个方法功能类似, 用法都是将x, y坐标传入, 即实现让滚动轮滚动到当前位置
区别: scrollBy() 会在之前的数据基础之上做累加
# 脚本化CSS
读写元素css属性
dom.style.prop(获取的只是行间的样式, 没有就没有), 可读写行间样式, 没有兼容性问题, 碰到float这样的保留字属性, 前面应加css,eg:
.float -> cssFloat, 复合属性必须拆解, 组合单词变成小驼峰式写法, 写入的值必须是字符串格式查询计算样式
window.getComputedStyle(eIe, null);(最终的展示样式)第二个参数操作伪元素, 计算样式只读, 返回的计算样式的值都是绝对值, 没有相对单位, IE8及以下不兼容
查询样式
ele.currentStyle, 计算样式只读,返回的计算样式的值不是经过转换的绝对值, IE独有的属性
# 事件
如何绑定事件处理函数
- ele.onxxx=function (event) { }
兼容性很好, 但是一个元素的同一个事件上只能绑定一个处理程序, 基本等同于写在HTML行间上, 程序this指向是dom元素本身
- ele.addEventListener(type,fn, false)
IE9以下不兼容, 可以为一个事件绑定多个处理程序, 程序this指向是dom元素本身
- ele.attachEvent('on'+type, fn)
IE独有, 一个事件同样可以绑定多个处理程序, 程序this指向window
解除事件处理程序
- ele.onclick = false/""/null
- ele.removeEventListener(type, fn, faIse)
- ele.detachEvent('on'+type, fn)
注: 若绑定匿名函数, 则无法解除
事件处理模型一事件冒泡, 捕获
- 事件冒泡
结构上(非视觉上)嵌套关系的元素, 会存在事件冒泡的功能, 即同一事件, 自子元素冒泡向父元素(自底向上)
- 事件捕获
结构上(非视觉上)嵌套关系的元素, 会存在事件捕获的功能, 即同一事件, 自父元素捕获至子元素(事件源元素)(自顶向下) IE没有捕获事件(ele.setCapture();是一种强行的事件捕获, 把其他事件加到自己身上) 触发顺序, 先捕获, 后冒泡 focus, blur, change,submit, reset, select等事件不冒泡
取消冒泡和阻止默认事件
- 取消冒泡
W3C标准event.stopPropagation( ), 但不支持IE9以下版本 IE独有event.cancelBubble=true;
- 阻止默认事件
默认事件——表单提交, a标签跳转, 右键菜单等
- return.false;以对象属性的方式注册的事件才生效
- event.preventDefauIt();W3C标注, IE9以下不兼容
- event.returnVaIue=false;兼容IE
# 事件对象
event || window.event 用于 IE
事件源对象
event.target 火狐只有这个, event.srcEIement IE只有这个, 这俩chrome都有
事件委托: 利用事件冒泡, 和事件源对象进行处理
优点:
- 性能: 不需要循环所有的元素一个个绑定事件
- 灵活: 当有新的子元素时不需要重新绑定事件
# 事件分类
鼠标事件
click, mousedown, mousemove, mouseup, contextmenu(右键菜单), mouseover, mouseout, mouseenter, mouseleave, 用button来区分鼠标的按键, 0/1/2, DOM3标准规定: click事件只能监听左键, 只能通过mousedown和mouseup来判断鼠标键
键盘事件
keydown,keyup,keypresskeydown>keypress>keyupkeydown和keypress的区别- keydown可以响应任意键盘按键, keypress只可以相应字符类键盘按键
- keypress返回ASCII码, 可以转换成相应字符
文本操作事件
input,focus,blur,change, 窗体操作类(window上的事件):scroll,load
# JSON
是一种传输数据的格式(以对象为样板, 本质上就是对象, 但用途有区别, 对象就是本地用的, json是用来传输的)
JSON.parse(); // string -> json 字符串转换成JSON
JSON.stringify(); // json -> string JSON转换为字符串
2
# 异步加载js
js加载的缺点: 加载工具方法没必要阻塞文档, 过得js加载会影响页面效率, 一旦网速不好, 那么整个网站将等待js加载而不进行后续渲染等工作
有些工具方法需要按需加载, 用到再加载, 不用不加载
异步加载js
javascript异步加载的三种方案
- defer异步加载, 但要等到dom文档全部解析完才会被执行, 只有IE能用, 也可以将代码写到内部
- async异步加载, 加载完就执行, async只能加载外部脚本, 不能把js写在script标签里
- 创建script, 插入到DOM中, 加载完毕后callBack
js加载时间线
- 创建Document对象, 开始解析web页, 解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中, 这个阶段
document.readyState='loading' - 遇到link外部css, 创建线程加载, 并继续解析文档
- 遇到script外部js, 并且没有设置async, defer, 浏览器加载, 并阻塞, 等待js加载完成并执行该脚本, 然后继续解析文档
- 遇到script外部js,并且设置有async, defer,浏览器创建线程加载, 并继续解析文档, 对于async属性的脚本, 脚本加载完成后立即执行(异步禁止使用document.write())
- 遇到img等, 先正常解析dom结构, 然后浏览器异步加载src, 并继续解析文档
- 当文档解析完成, document.readyState='interactive'
- 文档解析完成后, 所有设置有defer的脚本会按照顺序执行(注意与async的不同, 但同样禁止使用document.write())
- document对象触发DOMContentLoaded事件, 这也标志着程序执行从同步脚本执行阶段, 转化为事件驱动阶段
- 当所有async的脚本加载完成并执行后, img等加载完成后, document.readyState='complete', window对象触发load事件
- 从此, 以异步响应方式处理用户输入, 网络事件等
- 创建Document对象, 开始解析web页, 解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中, 这个阶段
# 请求
TIPS
同源策略
浏览器有一个很重要的概念 - 同源策略(same-origin policy)
所谓同源是指,域名,协议,端口相同, 不同源的客户端脚本在没明确授权的情况下,不能读写对方的资源
注意:src不受同源策略限制
# JSONP的原理
- web页面上用
<script></script>引入js文件时不受跨域的影响,不仅如此,只要是拥有src属性的标签都有跨域能力,比如<script></script>,<img>,<iframe></iframe> - 于是我们把数据放在服务器上,并且以
JSON的形式返回 - 定义好处理跨域请求获取到的数据的函数,如
function doJSON(data) {} - 用
src获取数据时添加一个参数cb='doJSON',服务端会根据cb的值返回对应的内容,此内容为以cb对应的值doJSON为函数真实要传递的数据为函数的参数的一串字符,如doJSON('数据')
如何标记记录身份
标记用户身份http请求首部
form:e-mail user-agent:浏览器 referer:跳转地址
IP地址 动态ip不稳定
用户登录
胖URL(自动在url后加上一段标记)只存在窗口存在的时间
cookie
如何获取iframe内的window
获取子窗口
document.getElementsByTagName('iframe')[0].contentWindow
document.getElementsById('id').contentWindow
// 简写法
window.frames['iframe的name']
// IE专用
document.iframes[name].contentWindow
document.iframes[i].contentWindow
window.self // 自己本身
window.top // 最终父级
window.parent //父级
// 需要onload事件监听(ie使用onreadystatechange)
2
3
4
5
6
7
8
9
10
11