从今天(2021.12.19)起我开始学习红宝书!
第一章
DOM
文档对象模型
BOM
浏览器对象模型:主要针对浏览器窗口和子窗口
- 弹出新窗口的能力
- 移动,缩放和关闭浏览器窗口的能力
- navigator对象,提供关于浏览器的详尽信息
- location对象,提供浏览器加载页面的详尽信息
- screen对象,提供源于用户屏幕分辨率的详尽信息
- performance对象,提供浏览器内存占用,导航行为和时间统计的详尽信息
- 对cookie的支持
第二章
script元素
- async: 立即下载脚本,但不阻止其他页面动作;
- defer: 表示脚本可以延迟到文档完全被解析和显示之后再执行;
defer与async属性只对外部脚本文件才有效,支持H5的浏览器会忽略行内defer属性; defer与async的不同是:标记为async的脚本并不会保证能按他们出现的次序执行
第三章 —— 语言基础
var
- 函数作用域
- 变量提升
let
- 块作用域
- 不允许同一个块作用域中出现两次
- 暂时性锁死:let声明的变量不会在作用域中被提升,在let声明之前的执行瞬间被成为暂时性锁死
块级作用域由最近一对包含的花括号{}界定
const
- extend(let)
- 声明变量必须初始化
null
- 空指针对象,初始化对象
转化为字符
- toString(): 返回为值得等价物
- String(): 一切皆可用
- +number
- 模板字面量
null & undefined 没有toString() 方法
let found = true;
found.toString() // "true"
object
- isPrototypeOf(object) 判断当前对象是否尾另一个对象的原型
第四章 —— 执行上下文与作用域
执行上下文(作用域):全局上下文,函数上下文和块级上下文
原始值 & 引用值
- 原始值大小固定保存在栈内存
- 引用值是对象,存储在堆上
垃圾回收
- 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间回收
- 标记清理,给不适用的值加上标记
提升性能
- 不用的全局对象赋值null
- 使用let const;少使用var
- 避免“先创建再补充”式的动态属性赋值,在构造函数中一次性声明所有属性;把不行要的属性设置为null
- 减少浏览器执行垃圾回收的次数 ——> 提升js性能
当内存中引用的次数为0的时候内存才会被回收
造成内存泄漏的原因
- 意外的全局变量
- 没有清楚定时器
- 闭包(将事件处理函数定义在外部)
function bindEvent(){
var obj=document.createElement('xxx')
obj.onclick=function(){ // Even if it is a empty function }
}
// 将事件处理函数定义在外面
function bindEvent() {
var obj = document.createElement('xxx')
obj.onclick = onclickHandler
}
// 或者在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent() {
var obj = document.createElement('xxx')
obj.onclick = function() {
// Even if it is a empty function
}
obj = null
}
闭包为什么会造成内存泄漏? 因为变量得不到释放,一直被占用
第五,六章
RegExp
元字符需要被转义:([{^$|}])?*+.
Sting
提取子字符串
slice(a, b), sbustring(a, b) // a: 起始位置,b:结束位置
substr(a, b) // a: 起始位置,b: 子字符串个数
单例内置对象
对象字面量:对象定义的简写形式
- Object
- Array
- String
- Global
- Math
Math.random() * total_number_of_choices + first_possibile_value
- Math.random()*9 + 2 //2 -10
第七章 迭代器与生成器
循环是迭代机制的基础,因为它可以指定迭代的次数,以及每次迭代要执行什么样的操作 什么情况下会用到生成器与迭代器呢?
第八章
创建对象的方法
- 工厂模式
function createPerson(name, age, job) {
let o = new Object()
o.name = name;
o.age = age;
o.sayName = function() { }
return o
}
- 构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.sayName = function() {}
}
构造函模式与工厂模式区别
- 没有显示创建对象
- 属性和方法直接赋值给了this
- 没有return
- 原型模式
- 每个函数都会创建一个prototype属性,这个属性是个对象,包含应该由特定引用类型的实例共享的共享的属性与方法;每个原型对象自动获得一个名为constructor的属性,指回构造函数;
- 使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享
- 每次调用构造函数创建一个新实例,这个实例内部[[Prototype]]指针就会被赋值为构造函数的原型对象
- 实例与构造函数原型之间有直接联系,但实例与构造函数之间没有
- 构造函数通过prototype属性链接到原型对象
- 实例通过__proto__链接到原型对象
- 同一个构造函数创建的两个实例共享同一个原型对象 正常的原型链都会终止于Object的原型对象;Object原型的原型是null
- for-in VS Object.keys() 枚举顺序是不确定的;
- getOwnPropertyNames() 枚举是确定的
继承
- 原型链 通过原型继承多个引用类型的属性和方法
原型链:每个构造函数有个原型对象,原型有一个属性指回构造函数;实例有个内部指针指向原型; 如果原型是另外一个类型的实例呢?那就意味着这个原型本身有个内部指针,指向另一个原型
- 使用call, apply 方法以新创建的对象为上下文执行构造函数
function SuperType() {
this.color = ['red', 'blue', 'green']
}
function SubType() {
// 继承Supertype
SuperType.call(this)
}
let instance1 = new SubType();
instance1.color.push('black')
instance1.color() //'red', 'blue', 'green', 'black'
let instance2 = new SubType()
instance2.color() //'red', 'blue', 'green'
- 传递参数
function SuperType(name) {
this.name = name
}
function SubType() {
SuperType.call(this, 'olivia')
this.age = '29'
}
let instance = new SubType()
instance.name //olivai
instance.age //29
- 原型式继承:Object.create()
let person = {
name: 'olivia',
frineds: [1,2,3]
}
let person1 = Object.create(person)
person1.name = 'alice'
person1.frineds.push(4)
let person2 = Object.create(person)
person1.name = 'jane'
person1.frineds.push(5)
console.log(person.frineds) // [ 1, 2, 3, 4, 5 ]
- extends关键字
在类构造函数中使用super(),
- 可以调用父类的构造函数,并将返回的实例赋值给this
- 不能在super前使用this
super() => super.constructor()
类
// 有构造函数的类;
// constructor关键字用于在类定义块内部创建类的构造函数
class Bar {
constructor() {}
}
函数
- 函数式编程
用函数编程;分为两类:过程式和声明式
“过程式”:沿着流程或者步骤走
“声明式”:只表达要做什么,不关心内部实现细节
- 不可变性
不直接更改原始数据,而是创建数据的副本,所有操作都使用副本来进行。
- 高阶函数
- 函数名:指向函数的指针
使用不带括号的函数名会访问函数指针,而不会执行函数
- arguments: 使用function定义函数时,可以在函数内部访问arguments对象,从中取得传来的每个参数
- this
- 标准函数中,this引用的是把函数当成方法调用的上下文对象;函数被调用时才能确定this指向
- 箭头函数中,this引用的是定义箭头函数的上下文
- apply、call、bind: 以指定的this调用方法;
bind会创建一个新的函数实例,this值会绑定到传给bind()对象
- 尾调用优化
- 代码在严格模式下
- 外部函数的返回值是对尾函数的调用
- 尾调用函数不需要执行额外逻辑
- 尾调用函数不返回闭包;
- 闭包
引用了另一个函数作用域中变量的函数 `` window.identity = 'this window'
window.identity = 'this window'
let object = {
identity: 'My Object',
getIdentity() {
return function() {
// 内部函数永远不可能直接访问外部函数的this
return this.identity // this.window
}
},
getIdentity() {
let that = this
return function() {
// 内部函数永远不可能直接访问外部函数的this
return that.identity // My Object
}
},
}
第十一章
Promise
不能用try...catch捕获错误
- 串行化异步任务(下一个请求依赖前一个请求的返回值)
function delayedResolve(str) {
return new Promise((resolve, reject) => {
console.log(str)
setTimeout(resolve, 1000)
})
}
delayedResolve('p1 executor')
.then(() => delayedResolve('p2 executor'))
.then(() => delayedResolve('p3 executor'))
.then(() => delayedResolve('p4 executor'))
async await
- async 如果不包含await关键字,其执行基本跟普通函数没区别
async function foo() {
console.log(await Promise.resolve('foo'))
}
async function bar() {
console.log(await 'bar')
}
async function baz() {
console.log('baz')
}
foo()
bar()
baz()
// baz,foo,bar
- js运行时在碰到await关键字,会记录在哪里暂停执行,等到await右边的值可用了,js会向队列中推送一个任务,这个任务会恢复异步函数的执行;
async function foo() {
console.log(2)
await null
console.log(4)
}
console.log(1)
foo()
console.log(3)
// 1,2,3,4
- await 后面跟一个promise
async function foo() {
console.log(2)
console.log(await Promise.resolve(8))
console.log(9)
}
async function bar() {
console.log(4)
console.log(await 6)
console.log(7)
}
console.log(1)
foo()
console.log(3)
bar()
console.log(5)
// 1,2,3,4,5,8,9,6,7
- 串行执行期约
const add2 = x => x+2
const add3 = x => x+3
const add4 = x => x+4
const add10 = async x => {
for(const fn of [add2, add3, add4]) {
x = await fn(x)
}
return x
}
add10(10).then(console.log)
// 19
第十二章 Bom (Browser Object Model)
window对象在浏览器中有两重身份,一个是Global对象,另一个是js接口
window 对象
- 通过var声明的所有全局变量都会变成window对象的属性和方法(使用let or const,则不会添加到全局变量)
- document.compatMode: 判断浏览器模式(怪异,正常(‘CSS1Compat’))
// innerWidth,innerHeight -> 返回浏览器窗口中页面视口的大小(不包含浏览器边框&工具栏)
console.log('innerWidth', window.innerWidth)
console.log('innerHeight', window.innerHeight)
// console.log('outerWidth', window.outerWidth)
// console.log('outerHeight', window.outerHeight)
// 标准模式:clientWidth, clientHeight 返回页面视口的宽度和高度
console.log(' 标准模式 clientWidth', document.documentElement.clientWidth)
console.log(' 标准模式 clientHeight', document.documentElement.clientHeight)
// 非标准模式:clientWidth, clientHeight 返回页面视口的宽度和高度
console.log('非标准模式 clientWidth', document.body.clientWidth)
console.log('非标准模式 clientHeight', document.body.clientHeight)
- window.open: 通过newWin.opener 与打开它的窗口通信 检查window.open的返回值,可以判断是否屏蔽弹窗
- setTimeout(): 会返回一个timeoutId,取消可以用clearTimeout(timeoutId)
最好不要使用setInterval(),
- 使用setTimeout不一定要记录超时id,会在满足条件下停止,否则开启另一个超时id;
- setInterval,一个任务结束和下个任务之间的时间间隔是无法保障的 使用setTimeout代替 setInterval
//
const max = 10;
let num = 0;
const fun = () => {
num ++
if(num > max) {
clearTimeout(setIimeoutId)
}else {
console.log('num', num)
setTimeout(fun, 1000)
}
}
const setIimeoutId = setTimeout(fun, 1000)
location 对象
它既是window对象,也是document对象
- 查询字符串
let getQueryStringArgs = () => {
let args = {}
let qs = location.search.length > 0 ? location.search.substring(1) : ''
for(let key of qs.split('&').map(item => item.split('='))){
let name = decodeURIComponent(key[0])
let value = decodeURIComponent(key[1])
if(name.length) {
args[name] = value
}
}
return args
}
- URLSearchParams: 检查和修改查询字符串 new URLSearchParams
const qs = '?&rsv_spt=1&rsv_iqid=0x88fb9af2000bd02e&issp=1&f=8&rsv_bp=1&rsv_idx=2'
new URLSearchParams(qs).get('rsv_iqid')
- 修改浏览器地址的方法
- location.assign()
- location.href
- windown.location
以上三种都会在浏览器历史记录增加响应记录
- location.replace() // 不会增加历史记录 - reload(): 重新加载
-
- location.reload() // 可能从缓存中获取
-
- location.reload(true) // 从服务器中获取
screen 对象
保存浏览器窗口外面的客户端显示器的信息
history 对象
navigator 对象
- window.navigator.userAgent
- navigator.geolocation // 感知当前设备的地理位置
第14章 Dom(Document Object Model)
Node 类型
- 元素节点 (1)
- 文本节点 (3)
- 节点关系
- childNodes中只有一个节点,则它的previousSibling & nextSibling属性都是null
- firstChild = someNode.childNodes[0]
- lastChild = someNode.childNodes[someNode.length - 1]
- appendChild
Document 类型
- document.documentElement: 取得对<html>的引用
- document.body: 取得<body>的引用
- document.title: 页面的title
- document.domain: 不同子域的页面无法通过js通信,把每个页面的domain设置成相同值就可以访问了 domain 一旦放松就不能收紧了
- getElementsByName(): 同一字段的单选按钮,必须有相同的name属性,才能确保把正确的值发送给服务器
- 文档写入: write(),writeln(),open(),close()
Element类型
nodeType === 1
- HTML元素
- getAttribute(), setAttribute()
- childNodes属性包含元素所有的子节点,这些子节点可能是其他元素,文本节点,注释或处理指令。不同浏览器在识别这些节点时,表现有明显不同
Text类型
nodeType === 3
DocumentFragment 类型
可以使用此属性,操作dom,避免浏览器多次渲染;
let customFrag = document.createDocumentFragment()
const myUl = document.getElementsByTagName('ul')[0]
for(let i =0; i<5;i++) {
let myLi = document.createElement('li')
myLi.appendChild(document.createTextNode('alice'))
customFrag.appendChild(myLi)
}
myUl.appendChild(customFrag)
selectors API
1. querySelector()
document.querySelector('.classname')
2. querySelectorAll()
它是静态的快照,而非“实时”的查询;nodeList是动态的
3. classList:类列表集合
- add(value): 向类名列表中添加指定的字符串值value 4. compatMode: 浏览器模式
- “CSS1Compat”: 标准模式
- “BackCompat”:混杂模式
样式
- style.getPropertyValue()
- getComputedStyle(): 获取style集合
- offsetHeight/offsetWidth: content + padding + border
- clientWidth/clientHeight: content + border
- scrollWidth/scrollHeight:没有滚动条出现的宽高
- scrollTop: 内容区域隐藏起来的像素数
- getBoundingClientRect(): 返回DOMReact对象,包含left,top,right,bottom,height,width
事件
- 事件流
页面接收事件的顺序 - 事件冒泡(IE事件流)
从最具体的元素触发,向上传播至没有那么具体的元素; - 事件捕获
从document元素捕获,然后沿DOM树依次向下传播,直达目标元素 - DOM事件流
事件捕获,到达目标和事件冒泡 - DOM0事件处理程序
元素.onclick = function(){}
btn.onclick = null, 移除事件处理程序
- DOM2事件处理程序
优势,可以添加多个事件处理函数 addEventListener(事件名,事件处理函数,布尔值) / removeEventListener('click', () => {})
true:捕获阶段处理
false:冒泡阶段处理(默认值)
- IE事件处理程序
- attachEvent('onclick', () => function),detachEvent
- DOM事件对象
this对象始终等于currentTarget的值, 而target只包含事件的实际目标
如何事件处理程序直接添加在了意图的目标,则this,currentTarget,和target的值一样
- IE事件对象
window.event - 用户界面事件
- load/unload
- error/select/resize/scroll
- clientX, clientY (鼠标光标在客户端视口中的坐标)
- pageX和pageY (鼠标在页面的坐标)
- screenX和screenY (屏幕坐标)
没有滚动条pageX与clientX相同
表单
- 阻止表单提交(event.preventDefault())
- 没有提交按钮的表单在按回车键时不会提交
- submit()提交表单时,submit事件不会触发
- elements: 表单中所有控件的集合
跨上下文消息(XDM)
- postMessage(messageContent, origin)
origin: 发送消息的文档源
如果不想限制接受目标,则可以给postMessage()的第二个参数传* 在onmessage事件处理程序中检查数据源是否正确
window.addEventListener('message', (event) => {
// 确保来自预期发送者
if(event.origin == 'xxxx') {
// 对数据进行一些处理
processMessage(event.data)
// 可选,向来源窗口发送一条消息
event.source.postMessage('received', "")
}
})
拖放事件
在某个元素被拖动时,会顺序触发以下事件
- dragstart
- drag
- dragend 在把元素拖动到一个有效放置目标上时,会顺序触发以下事件
- dragenter
- dragover
- dragleave or drop
dataTransfer 对象
event.dataTransfer.setData('text','some text')
event.dataTransfer.setData('url','http://www.baidu.com')
event.dataTransfer.getData()
Web 组件
1. HTML模板
- 使用DocumentFragment
批量向HTML中添加元素;可以一次性添加所有子节点,最多只会有一次布局重排 document.appendChild(), 会导致多次布局重排
const fragment = document.createDocumentFragment()
fragment.appendChild(document.createElement('p'))
fragment.appendChild(document.createElement('p'))
fragment.appendChild(document.createElement('p'))
console.log(fragment.children.length)
document.getElementById('foo').appendChild(fragment)
2.错误处理
a. try...catch...finally
- finally存在会忽略try/catch中的return;
- err.message: 向用户显示错误信息
try {
const abc = 1;
abc = 2
}catch(err) {
console.log(err.message)
}
3.报错类型
- ferenceError:找不到对象时发生
- SyntaxError: 语法错误
- TypeError: 变量不是预期类型,或者访问不存在
JSON
- JSON.Stringify(value, 过滤器, 缩进值)
XMLHttpRequest 对象
const xhr = new XMLHttpRequest()
1. 首先调用open方法
xhr.open(method: 'post | get', url: String, async: Boolean)
2. 要发送定义好的请求
xhr.send(null)
3. 收到服务器响应,XHR对象的以下属性会被填充上数据
responseText: 作为响应体返回的文本
responseXML:
status: 响应http状态
statusText: 响应的http状态描述
if((xhr.status >= 200 & xhr.status < 300) || xhr.status === 304){
console.log(xhr.responseText)
}
status状态码
304: 资源未修改过,从浏览器缓存中拿取
xhr对象,有个readyState属性,
0:尚未调用open方法
1:已经调用open,但为调用send
2:已发送send,尚未收到响应
3:接受中,已收到部分响应
4:完成,已经收到到全部响应
redayState改变会触发 onreadystatechange 事件
完整的XHR发送接收请求
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.responseText)
}
}
}
xhr.open('post', url, true)
xhr.send(null)
请求头
- 发送额外请求头:setRequestHeader()
为保证请求头发送出去,必须在open()之后,send()之前调用setRequestHeader()
get请求(查询)
查询字符串中的每个名和值都必须使用encodeURIComponent()编码
post请求(发送应该保存的数据)
提交表单,需要把Content-Type设置成:application/x-www.formurlencoded
跨域方法
- 图片src
- jsonp
回调,(在页面接收到响应之后应该调用的函数)
数据,作为参数传给回调函数的JSON数据callback({"name":"olivia"})
代理反射
为开发者提供了拦截并向基本操作嵌入额外行为的能力 代理:目标对象的抽象
在代理对象上执行的所有操作都会无障碍传播到目标对象上
- 创建代理
const proxy= new Proxy(target, handler)
target: 目标对象
handler:处理程序对象
创建空代理,可以传一个简单地对象字面量作为处理程序对象
- 定义捕获器
捕获器:在处理程序对象中定义的“基本操作的拦截器”。每个handle对象可以包含零个或者多个捕获器,每个捕获器对应一种基本操作,可以直接或间接在代理对象上调用
只有在代理对象上执行,才会触发捕获器
const target = {
name: 'olivia'
}
const handler = {
// tarpTarget: 接收到的目标对象
// property: 要查询的属性
// receiver:代理对象
get(tarpTarget, property, receiver) {
console.log(tarpTarget === target)
console.log(property)
console.log(receiver)
return '我是捕获器'
}
}
const proxy = new Proxy(target, handler)
proxy.age = '1993'
console.log(proxy.age)
- 反射:处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)
- 撤销代理:revoke()
开发者并不需要手动重建原始行为,而是可以通过调用全局Reflect对象上来轻松重构
const target = {
name: 'olivia'
}
const handler = {
get() {
return Reflect.get(...arguments)
}
}
const proxy = new Proxy(target, handler)
console.log(proxy.name) // olivia