javascript高级
基础总结
数据类型
-
基本(值)类型 【保存数据的值】
-
String
-
Number
- 表示任意整数,整数范围有局限性
-
boolean
-
underfind
-
null
- 使用typeof时返回object
-
symbol
- 基本数据类型之一,是symbol原始值的封装
-
bigint
- 弥补number属性类型的缺陷,可以表示任意精度的整数
-
-
对象(引用)类型 【保存数据的地址】
-
Object对象
- 表示任意对象
-
Function
- 一种特别的对象,属性值是一个函数
-
Array
- 一种特别的对象,属性名是数组下表,内部数据是有序的
-
-
判断方法
-
typeof
-
typeof操作符返回一个字符串,表示数据的数据类型
- ps:不能判断null和object 和 object与array
-
-
instanceof
-
专门判断对象的具体类型
- instanceof运算符用于检测构造函数的显示原型属性是否在实例对象的原型链上
-
-
===运算符
- 进行数值和数据类型的比较
-
-
问题:underfinded和null的区别
- under代表定义未赋值
- null定义并赋值,只是数值为null
-
问题:什么时候给变量赋值为null
- 初始赋值:var f = null 表示要将要赋值为对象
- 结束时: f = null 让对象成为垃圾对象
数据、变量、内存
-
什么是数据
- 存储在内存中代表特定信息的东西,本质上是二进制代码
- 数据的特点:可传递,可运算
- 一切皆是数据
-
什么是内存
-
物理硬件通电后产生的可存储数据的临时空间
-
产生和死亡
- 内存条通电 → 产生内存空间 → 存储数据 → 处理数据 → 断电 → 释放内存空间
-
分类
-
栈
- 全局变量和局部变量
-
堆
- 存储对象
-
-
-
什么是变量
- 内存中用来存储数据的容器,由变量名和变量值组成。变量名用来查找对应内存,变量值是内存中保存的数据。
-
问题:js调用函数时传递变量参数,是值传递还是地址传递
- 理解1:都是值(基本数值,地址值)传递
- 理解2:可能是值传递,也可能是地址传递
-
问题:js引擎如何管理内存
-
内存生命周期
- 分配小的内存空间,得到它的使用权
- 存储数据,可以反复操作
- 释放小的内存空间
-
释放内存
- 局部变量:函数执行完自动释放
- 对象:成为垃圾对象 ==> 被垃圾回收器回收
-
对象
-
什么是对象
- 多个数据的封装体
- 用来保存多个数据的容器
- 一个对象代表现实中的一个事物
-
为什么要使用对象
- 统一管理多个数据
-
对象的组成
- 属性:属性名(字符串)和属性值(任意)组成
- 方法:一种特别的属性(属性值是函数)
-
访问对象内部数据
-
点运算符
- 编码简单,有时候不能用
-
方括号运算符
-
编码复杂,能通用
- 属性名包含特殊字符
- 属性名不确定
-
-
函数
-
什么是函数
- 实现特定功能的多条语句的封装体
- 只有函数是可以执行的,其他类型的数据不能执行
-
为什么要使用函数
- 提高代码复用
- 便于阅读交流
-
如何定义函数
- 函数声明
- 函数表达式
-
如何调用函数
- test(): 直接调用
- obj.test(): 通过对象调用
- new test(): new调用
- test.call/apply(obj): 临时让test成为obj的方法进行调用
-
回调函数
-
什么函数才是回调函数
- 你定义的
- 你没有调
- 但最终它执行了(在某个时刻或某个条件下)
-
常见的回调函数
- dom事件回调函数 ==>发生事件的dom元素
- 定时器回调函数 ===>window
- ajax请求回调函数
- 生命周期回调函数
-
-
自调用函数IIFE
-
作用
- 隐藏实现
- 不会污染外部(一般指全局)命名空间
- 用它来编码js模块
-
-
函数中的this
-
this是什么
- 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
- 所有函数内部都有一个变量this
- this的值是:调用函数的当前对象
-
如果确定this的值
- test(): window
- p.test(): p
- new test(): 新创建的对象
- p.call(obj): obj
-
函数高级
原型与原型链
-
原型 [prototype]
-
函数的prototype属性
-
每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
-
原型对象中有一个属性construction,它指向函数对象
-
给原型对象添加属性-->【方法】
- 作用:函数中的所有实例对象自动拥有原型对象中的属性/方法
-
-
显式原型与隐式原型
- 每个函数function都有一个prototype,即显式原型(属性)
- 每个实例对象都有一个[__ proto __],可称为隐式原型(属性)
- 对象的隐式原型的值为其对应构造函数的显式原型的值
- 函数的prototype属性:定义函数时自动添加的,默认是一个空的Object对象
- 对象的[__ proto __]属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
- 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
- 内存结构
-
原型链
-
作用:查找访问对象的属性or方法
- 先在自身属性中查找,找到返回
- 如果没有,沿着隐式原型属性这条链向上查找,找到返回
- 如果最终没有找到,返回underfinded
- 图示
-
构造函数、原型对象、实例对象的关系
-
var o1 = new Object(); var o2 = {};
-
function Foo(){ }
-
-
属性问题
- 读取对象的属性时:会自动到原型链中找
- 设置对象的属性时:不会查找原型链,如果当前对象没有此属性,直接添加此属性并赋值
- 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
-
instanceof
-
表达式:A instanceof B
- A是对象:查找隐式原型属性
- B是函数:查找显式原型属性
-
Function是通过new自己产生的实例
- /* 案例1 */ function Foo() { } var f1 = new Foo() console.log(f1 instanceof Foo) // true console.log(f1 instanceof Object) // true
-
-
/* 案例2 */ console.log(Object instanceof Function) // true console.log(Object instanceof Object) // true console.log(Function instanceof Function) // true console.log(Function instanceof Object) // true
function Foo() {} console.log(Object instanceof Foo) // false
执行上下文与执行上下文栈
-
执行上下文是当前代码执行的一个环境和范围
-
变量提升与函数提升
-
变量声明提升
- 通过var定义的变量,在定义语句之前就可以访问到。其值为undefined
-
函数声明提升
- 通过function声明的函数,在之前就可以直接调用
-
-
执行上下文
-
全局执行上下文
-
在执行全局代码之前就将window确定为全局执行上下文
-
对全局数据进行预处理
- var定义的全局变量==>undefined, 添加为window的属性
- function声明的全局函数==>赋值(fun), 添加为window的方法
- this==>赋值(window)
-
开始执行全局代码
-
-
函数执行上下文
-
在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)
-
对局部数据进行预处理
- 形参变量==>赋值(实参)==>添加为执行上下文的属性
- arguments==>赋值(实参列表), 添加为执行上下文的属性
- var定义的局部变量==>undefined, 添加为执行上下文的属性
- function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
- this==>赋值(调用函数的对象)
-
开始执行函数体代码
-
-
-
执行上下文栈
- 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
- 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)-->所以栈底百分百是[window]
- 在函数执行上下文创建后, 将其添加到栈中(压栈)
- 在当前函数执行完后,将栈顶的对象移除(出栈)
- 当所有的代码执行完后, 栈中只剩下window
- 上下文栈数==函数调用数+1
作用域与作用域链
-
作用域
-
理解
- 就是一块"地盘", 一个代码段所在的区域
- 它是静态的(相对于上下文对象), 在编写代码时就确定了
-
分类
- 全局作用域
- 函数作用域
- 没有块作用域(ES6有了)
-
作用域
- 隔离变量,不同作用域下同名变量不会有冲突
-
-
作用域与执行上下文的区别和联系
-
区别1
- 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
- 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
- 函数执行上下文是在调用函数时, 函数体代码执行之前创建
-
区别2
- 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
- 执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放
-
联系
- 执行上下文(对象)是从属于所在的作用域
- 全局上下文环境==>全局作用域
- 函数上下文环境==>对应的函数使用域
-
-
作用域链
-
理解
- 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
- 查找变量时就是沿着作用域链来查找的
-
查找规则
- 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
- 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
- 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
-
闭包
-
闭包是指一个内层函数可以访问到外层函数的作用域,在js中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
-
作用
- 使函数内部的变量在函数执行完后,仍然存活在内存中——延长局部变量的生命周期
- 让函数外部可以操作函数内部的数据
-
闭包的生命周期
- 产生:在嵌套内部函数定义执行完成时就产生了
- 死亡:在嵌套的内部函数成为垃圾对象时
-
闭包的应用
-
定义js模块
- 具有特定功能的js模块
- 将所有的数据和功能都封装在一个函数内部
- 只向外暴露一个包含n个方法的对象或函数
- 模块的使用者只需要通过模块暴露的对象调用方法来实现特定的功能
-
-
闭包的缺点
- 函数执行完之后,局部变量没有释放,占有内存的时间延长,容易造成内存泄漏
-
内存溢出和内存泄漏
-
内存溢出
- 一种程序运行出现的错误
- 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误
-
内存泄漏
- 占用的内存没有及时释放
- 内存泄露积累多了就容易导致内存溢出
-
面向对象高级
对象创建模式
-
Object构造函数模式
- 套路: 先创建空Object对象, 再动态添加属性/方法
- 适用场景: 起始时不确定对象内部数据
- 问题: 语句太多
- 对象字面量模式
-
对象字面量模式
- 套路: 使用{}创建对象, 同时指定属性/方法
- 适用场景: 起始时对象内部数据是确定的
- 问题: 如果创建多个对象, 有重复代码
-
工厂模式
-
套路: 通过工厂函数动态创建对象并返回
- //返回一个对象的函数===>工厂函数 function createPerson(name, age) { var obj = { name: name, age: age, setName: function (name) { this.name = name } } return obj }
-
适用场景: 需要创建多个对象
-
问题: 对象没有一个具体的类型, 都是Object类型
-
-
自定义构造函数模式
- 套路: 自定义构造函数, 通过new创建对象
- 适用场景: 需要创建多个类型确定的对象,与上方工厂模式有所对比
- 问题: 每个对象都有相同的数据, 浪费内存
-
构造函数+原型的组合模式-->最好用这个写法
- 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上
- 适用场景: 需要创建多个类型确定的对象
- 放在原型上可以节省空间(只需要加载一遍方法)
线程机制与事件机制
进程与线程
-
进程
- 可以通过windows任务管理器查看进程
- 程序的一次执行,它占有一片独有的内存空间
-
线程
- 是进程内的一个独立执行单元
- 是程序执行的一个完整流程
- 是CPU的最小的调度单元
-
问题
-
比较单线程和多线程
-
多线程
-
优点
- 能有效的提升CPU的利用率
-
缺点
- 创建多线程开销
- 线程间切换开销
- 死锁与状态同步问题
-
-
单线程
-
优点
- 顺序编程简单易懂
-
缺点
- 效率低
-
-
-
浏览器内核
-
支撑浏览器运行的最核心的程序
-
组成模块
-
主线程
- js引擎模块 : 负责js程序的编译与运行
- html,css文档解析模块 : 负责页面文本的解析(拆解)
- dom/css模块 : 负责dom/css在内存中的相关处理
- 布局和渲染模块 : 负责页面的布局和效果的绘制
-
分线程
- 定时器模块 : 负责定时器的管理
- 网络请求模块 : 负责服务器请求(常规/Ajax)
- 事件响应模块 : 负责事件的管理
-
-
js为什么是单线程模式
-
JavaScript的单线程,与它的用途有关。
-
作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
-
这决定了它只能是单线程,否则会带来很复杂的同步问题
- 举个栗子:如果我们要实现更新页面上一个dom节点然后删除,用单线程是没问题的
- 但是如果多线程,当我删除线程先删除了dom节点,更新线程要去更新的时候就会出错
-
事件循环机制
-
js代码的执行机制
- javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务。
-
浏览器环境下JS引擎的事件循环机制