js红宝书 前十二章总结(按原书总结归纳)

199 阅读10分钟

第一章 什么是JavaScript

完整的js包含以下几个部分

ECMAScript(核心)

DOM(文档对象模型)

BOM(浏览器对象模型)


web浏览器只是ECMAScript实现可能存在的一种宿主环境

DOM 文档对象模型 (DOM,Document Object Model)是一个应用编程接口(API),用于在HTML中使用扩展的XML。

BOM 浏览器对象模型,主要针对浏览器窗口和子窗口。

第二章 HTML中的JavaScript

标签 < script >

async: 立即下载脚本,但不阻止其他页面动作,异步

defer: 同步,立即下载,延迟执行,可以延迟到文档完全被解析和显示之后再执行

代码中不可以出现< script />否则会报错

第三章 语言基础

var

在全局声明会变成windox的属性,具有变量提升,函数作用域,无块级作用域的概念,可重复声明

//变量提升
function test(s){
    console.log(s)
    var s = 2 
    function s(){}
    console.log(s)
}
test(1)
//ƒ s(){}
// 2

相当于

var s = 1
(function(){
    console.log(s)
    var s = 2
    function s(){}
    console.log(s)
})

// 函数提升的优先级高于var 变量提升 ,优先提升函数,所以先输出function (){},随后,输出s=2
console.log(a)
var a = 5 

//等于 
// var a
// console.log(a)
// a = 5

function test(){
    message = "ok"  //全局对象
}
test()
console.log(message) // ok

let

块级作用域( 看 {} ) 块级作用域小于或等于函数作用域,不可重复声明

暂时性死区:使用未声明的let/const变量时,js引擎也会注意到后面的let变量,但是在这个变量声明之前,不能够使用,这段时间就叫做,暂时性死区

for循环的时候

for( var i = 0; i<5 ; i++){
    
}
console.log(i)// 5
//let 报错

const

和let类似,但是声明时必须要赋值,而且不能够重新赋值和重复声明。

优先用const,其次let

数据类型

undefined 未定义

null 对空对象的引用,逻辑上是表示一个空对象指针。

NaN not a number 表示本来要返回数值的操作,但失败了 ,such as (0/0,5/0)

isNaN('red') // true
isNaN(true) //false 可以转为1
//判断是否 “不是数值”

Symbol

符号是原始值,且符号实例是唯一,不可变的,符号的用途是确保对象属性使用唯一标识,防止冲突。

操作符

3 ** 2 = 9

4 ** 2 = 16

2 + ‘2’ = 22

“a” > "A" //true 比的是字符编号

“23” < 2 //false 23 > 2

只要和NaN 比较都为false

NaN < 3 false

NaN >= 3 false

NaN != NaN true

语法

for in 枚举对象中非符号键的属性

for(let key in obj){}  //key obj属性名

for of 遍历可迭代对象

for(let el of [1,2,4]){console.log(el)}  // 1 2 4

第四章 作用域与内存

原始值(undefined null boolean number string symbol)大小固定,保持在栈内存

对象存储在堆内存

任何变量都存在于某个执行上下文中(也称作用域),这个上下文(作用域)决定了变量的生命周期,以及它们可以访问哪些代码部分。

执行上下文分全局上下文、函数上下文、块级上下文

代码执行流每流进入一个新上下文,都会创建一个作用域,用于搜素变量和函数

传递参数

所有函数的参数都是按值来传递的。就算是传引用形数据类型(对象)也是按值来传

function test(a){
   console.log(a)  //Identifier 'a' has already been declared
   let a = 5
   console.log(a)  //Identifier 'a' has already been declared
}
test(1)

function test(a){
   console.log(a)  //1
   var a = 5
   console.log(a)  //5
}
test(1)

执行上下文与作用域

在v8环境下,函数会被提升,会被先解析

每一个执行上下文会关联一个vo对象(variable Object)变量对象

全局代码被执行,关联的就是GO(gobal Object)

函数被执行,关联的就是AO(activation Object)

执行上下文栈,底层一般是全局上下文

vo会有一个 作用域链对象 scope chain对象,决定各级上下文中代码在访问变量和函数时的顺序是

函数优先访问自己的AO,找不到变量就通过作用域链,向上查找,最后是向GO查找,作用域链仅仅和函数定义的位置有关,和调用无关

function(){ a = 5 } 未定义直接赋值会变成全局变量

垃圾回收

js通过自动内存管理实现内存分配和闲置资源回收,哪个变量不用了就会释放它的内存,这个过程是周期性。垃圾回收程序需要跟踪记录哪个变量不使用,标记策略一般有两种(标记清理和引用计数)

垃圾回收是周期性运行,时间调度很重要,在移动设备上,垃圾回收有可能明显拖慢渲染速度和帧速率。

一般基本都是根据已分配对象的大小和数量来判断何时运行,可手动触发回收但不推荐。

标记清理(最常见)

当变量进入上下文,比如在函数内部声明一个变量时,这个变量会被加上存在于上下文的标记。而在上下文的变量,只要不离开上下文就永远不会被释放内存,因为有可能用到。离开时也会被加上离开的上下文的标记。

加标记的方式:例如当变量进入上下文,反转某一位

垃圾回收程序运行:标记内存中存储的所有变量,然后去掉所有上下文变量的标记(包括引用的变量),最后剩下的,再被加上标记的变量就是等待删除的。随后垃圾回收程序做一次内存清理。

引用计数

记录每个值的引用次数,声明并赋值变量+1,引用被覆盖-1,0的时候被回收

缺点:对象相互引用,不能释放内存

let obj1 = {}
let obj2 = {}
obj1.a = obj2
obj2.a = obj1

内存管理

优化内存的最好方法,只保存需要用的数据,不用的就设为空

1 多用let、const ,不用var

2 隐藏类和删除操作(极致性能)

//创建好后又补充新属性,导致两个不是共用一个隐藏类
function test(){this.name="zhangshan"}
let a1 = new test()
let a2 = new test()
//a1 a2 共享相同隐藏类,如果补充a1.age = 18,a1 a2将对应两个不同隐藏类
//解决方案
function test(age){this.name="zhangshan",this.age = age}
let a1 = new test()
let a2 = new test(18)

//删除一个属性,保持同一个隐藏类,用null
function test(){this.name="zhangshan" this.age =18}
let a1 = new test()
let a2 = new test()

delete a1.author //不在用同一个隐藏类
a1.author = null //还是同一个隐藏类

内存泄漏问题

意外全局变量

function(){ age =18 }

定时器

let name = "a"
setInterval(()=>{
    console.log(name)
},1000)
//一直使用name,无法释放name内存

闭包

// 调用outer 会使name内存泄漏
//创建一个内部闭包,返回函数占用name,导致内存无法释放,如果name占的空间很大就有大问题了
let outer = function(){
    let name = "a"
    return function(){
        return name
    }
}

静态分配与对象池

减少浏览器垃圾回收的次数,不能直接控制什么时候开始收集垃圾,但是可以间接控制触发垃圾回收条件。(合理分配内存)

对象池 解决对象更替速度过快导致频繁垃圾回收

在初始化的某一时刻,创建一个对象池,统一管理一组可回收的对象,应用程序可以向这个对象池请求一个对象,设置其属性,使用它,然后操作完成后再把它还给对象池,由于没发生对象初始化,垃圾回收探测就不会发现有对象更替,垃圾回收就不会那么频繁的运行。

第五章 基本引用类型

Data

RegExp

Boolean

Number

String

Global

Math

具体用到,忘记api看红宝书

第六章 集合引用类型

API

map set

第七章 迭代器和生成器

第八章 对象与面向对象编程

属性类型 vue2

属性:分为数据属性和访问器属性

数据属性

  • Configurable 是否可以被修改特性,是否可以被delete删除并重新定义 默认true
  • Enumerable 是否可以通过for-in循环返回 默认true
  • Writable 属性值是否可以被修改 默认true
  • Value 包含属性实际值 默认undefined

要修改属性默认特性,必须使用Obecgt.defineProperty(要给其添加属性的对象,属性名称和一个描述对象,描述对象上的属性可以包含)

let person = {}
Object.defineProperty(person,"name",{
    writable:false,
    value:"zhangshan"
})
console.log(person.name) //zhangshan


访问器属性 vue2双向绑定核心

它包含一个获取(get)函数和一个设置(setter)函数

  • Configurable 是否可以被修改特性,是否可以被delete删除并重新定义 默认true
  • Enumerable 是否可以通过for-in循环返回 默认true
  • Get 获取函数,读取属性时调用,默认值为undefined
  • Set 设置函数,在写入属性时调用,默认值为undefined
let person ={age:18}
Object.defineProperty(person,"age",{
    get(){
        return this.age
    },
    set(newValue){
        if(newValue>20)
            return "超过20岁"
        else{
            return newValue
        }
    }
})
//定义多个属性
let person = {age:18,name:zhangsan}
Object.defineProperties(person,{
    sex:{
        value:"男"
    }
    age:{
        get(){
            return this.age
        }
    }
})

Object.getOwnPropertyDescriptor()获取指定属性和属性描述词 Object.assign() 合并对象,接收一个或多个源对象作为参数

判断NaN
Object.is(NaN,NaN) true

创建对象

工厂模式

函数内直接创建一个新对象,并设置好属性,最后返回出去

构造函数模式

和工厂模式类似,但是没有new新对象,用this赋值属性和方法,没有return

new的过程

  1. 内存中创建一个新对象
  2. 绑定prototype原型链
  3. 把构造函数内部的this被赋值为这个新对象(this指向新对象)
  4. 执行构造函数内部代码(给新对象添加属性)
  5. 如果构造函数返回非空对象,则返回该对象,否则返回刚创建的新对象

构造函数也是函数,可以直接执行,但是此时this为Global对象(浏览器为window)

问题:分别创建两个实例,方法名相同但是不是同一个方法,每次都new一个新的函数

this.say = new Function(“console.log()”)

不同实例上的函数同名但不相等(person1.sayName == person2.sayName) false

原型模式

解决上面的构造函数问题,属性和方法可以被对象实例共享。直接在prototype上绑定属性方法

盗用构造函数

解决原型包含引用值导致的继承问题,并且可以在子类构造函数向父类构造函数传参,在子类调用父类构造函数,使用apply() call()

区别开属性共享问题,每个实例有自己的属性

缺点:必须在构造函数中定义方法,因此函数不能重用

组合继承

综合原型链和盗用函数,集中两者优点,使用原型链继承原型的属性和方法,盗用构造函数继承实例属性,让方法定义在原型上实现重用,又可以让每个实例都有自己的属性。

使用最多的方法

原型继承

适合不需要单独构建构造函数,但仍然需要在对象间共享信息的场合。本质上给定对象执行浅复制

寄生式继承

和原型式比较接近。先基于一个对象创建一个新对象,然后再增强这个新对象,最后返回新对象。

寄生式组合继承

存在效率问题,父类构造函数始终被调用两次。被认为是基于类型继承的最有效方式

类和函数相似,但是类不能提升

和构造函数的区别,类必须new

继承

class Bus extends Vehicle{}

访问父类静态方法super.xxxx()

第九章 代理与反射

代理:作为目标对象的替身,但又完全独立于目标对象,目标对象可以直接被操作,也可以代理操作 用处:重新定义对象基本操作跟踪

proxy(目标对象,处理程序对象)

核心-定义捕获器 :拦截自定义目标对象操作(访问属性,调用方法)

//手动写代码
const target = {age:18}
const handler = {
    get(trapTarget,property,receiver){
        return trapTarget[property]
    }
}
const proxy = new Proxy(target,handler)
//借助Reflect
const target = {age:18}
const handler = {
    get(){
        return Reflect.get(...arguments)
    }
}
//

revocable()可以撤销代理对象和目标对象的关联

可以多层代理

//set(target,property,value,receiver)
const person = {age:10}
const proxy =new Proxy(person,{
    set(target,property,value,receiver){
        if(value <18){
            return false
        }else{
           return Reflect.set(...arguments)
        }

    }
})

proxy.age = 12 // 10
proxy.age = 20 // 20

第十章 函数

箭头函数

不能使用arguments、super、new.target,也不能作构函数,此外,箭头函数也没有protorype属性。

理解参数

调用函数可以传任意个参数都不会报错,原因是函数参数在内部表现为一个数组,非箭头函数可以用arguments查看参数。

闭包

函数访问函数外的变量,一般是嵌套函数,闭包存在内存泄漏问题。

立即调用函数

(function(){
var i = 5
})()
console.log(i) //报错,访问不到i

私有变量

静态私有变量

模块模式、模块增强模式

第十一章 期约与异步函数

手写promise核心代码 - 掘金 (juejin.cn)

Promise.all()和Promise.race()

第十二章 BOM

window

BOM核心,windox对象,两个身份Global对象和浏览器窗口的js接口。

系统对话aler(),confirm(),prompt()