Day18 JavaScript 高级笔记
1 回顾
1.1 垃圾回收机制
- 引用计数 -
- 标记清除 -
1.2 执行上下文和执行栈
① 执行上下文对象
全局执行上下文对象:
① 代码执行之前创建,将全局执行上下文对象赋值给 window
② 预处理:
变量提升
函数提升
this 赋值
③ 正式执行全局代码
函数的执行上下文对象:
① 调用函数的时候创建执行上下文对象
② 预处理:
形参赋值
arguments 赋值
局部变量提升
局部函数提升
this 赋值(调用函数的对象)
③ 正式执行函数体代码
② 执行栈
1. 特点: 先进后出,后进先出
2. 用于存储执行上下文对象
1.3 闭包
1. 闭包概念
使用其他函数作用域中的数据,称为闭包
2. 如何产生闭包
① 函数A中嵌套函数B
② 函数B中使用函数A中的数据
③ 函数B对外引用(返回、赋值给全局变量、作为回调函数)
3. 闭包与作用域的关系
① 可以使用上层作用域的数据
② 作用域只与函数声明位置有关
4. 闭包与垃圾回收
闭包延长了数据的声明周期
5. 闭包缺点
增加了内存泄漏的风险
2 对象高级
2.1 原型链总结
① __proto__
和 prototype 属性
1. 任何对象都有 __proto__ 属性,通过该属性可以获取自己的原型。
2. 只有函数才具有 prototype 属性,该属性获取函数的实例的原型。
3. 一般情况 函数.prototype 与 函数.__proto__ 绝对不等。
特殊情况 Function.prototype === Function.__proto__; Function 的构造函数还是 Function。
② constructor 属性
1. 并不是所有的对象都有 constructor 属性,需要使用原型上的 constructor 属性
2. 本身具有 constructor 属性的对象,它的 constructor 指向并不是自己的构造函数,而是以它为原型的那些个对象的构造函数
3. 对象本身没有constructor属性,可以通过原型的 constructor 获取自己的构造函数
对象本身有 constructor 属性,并不是给自己用的,给以它为原型的那些个对象用的
③ 原型链
// 定义构造函数
function Foo() {
}
// 实例化 Foo
var f1 = new Foo();
var f2 = new Foo();
// 实例化 Object
var o1 = new Object();
var o2 = new Object();
f1、f2 -> Foo.prototype -> Object.prototype
o1、o2 -> Object.prototype
Foo、Object、Function -> Function.prototype -> Object.prototype
prototype 和 __proto__
:
f1.__proto__ === Foo.prototype;
Foo.prototype.__proto__ === Object.prototype;
o1.__proto__ === Object.prototype;
Foo.__proto__ === Function.prototype;
Object.__proto__ === Function.prototype;
Function.__proto__ === Function.prototype;
本身的 constructor 属性:
Foo.prototype.constructor === Foo;
Object.prototype.constructor === Object;
Function.prototype.constructor === Function;
2.2 面向对象继承
① 面向对象语言的继承规则
// 作为父类
class User {
// 定义属性 实例的属性
private username;
private age;
// 定义方法
say() {
}
}
// 定义子类
class VipUser extends User {
// 定义属性
private address;
}
② JS 中继承关系的特点
1. JS 中依靠原型链进行集成,对象可以使用原型链上的属性和方法。
2. intanceof 运算符判断对象和自己构造函数,对象和原型链上的对象的构造函数,都成立。
3. 对象的 __proto__ 等于它构造函数的 prototype
4. 如果一个对象本身具有 constructor 属性,指向以该对象为原型的那些对象的构造函数。
5. JS 是单继承, 一个对象只能有一个原型,一个对象可以给多个对象当原型。
③ 实现JS中构造函数和构造函数之间继承
// 定义构造函数 作为父类
function User(username, age) {
// 对形参进行判断
if (age < 0 || age > 200) {
age = 0;
}
// 给实例添加属性
this.username = username;
this.age = age;
}
// 在实例的原型上添加方法
User.prototype.addShopcart = function() {
console.log(this.username + '把商品添加到购物车!');
};
User.prototype.buy = function(product) {
console.log(this.username + '买了' + product);
};
// 定义构造函数 作为子类 继承 User
function VipUser(username, age, address) {
// 调用 父类中对属性赋值的操作
User.call(this, username, age);
// 添加子类自己实例的属性
this.address = address;
}
// 设置VipUser的实例的原型 是 User的一个实例
VipUser.prototype = new User();
// 给VipUser的实例的原型设置 construct 属性
VipUser.prototype.constructor = VipUser;
VipUser.prototype.getInfo = function() {
console.log('我叫' + this.username +', 年龄:'+this.age+',地址:'+ this.address);
};
1. 让子类的实例的原型指向父类的一个实例(最重要)
2. 给子类的实例的原型设置属性 constructor 属性,指向子类(构造函数)
3. 子类中,使用 call 调用父类,设置父类中的 this 指向子类的实例。
2.3 安全的类型检测
function getType(data) {
var typeStr = Object.prototype.toString.call(data);
return typeStr.slice(8, typeStr.length - 1);
}
3 单线程和事件轮询机制
3.1 进程和线程
进程:
程序使用内存的最小单位,一个进程独占一块内存空间。
线程:
线程是 CPU 的最小调度单位。
进程和线程:
1. 一个进程可以同时运行多个线程,多线程运行
2. 进程中至少有一个线程,主线程
3. 同一个进程中的多个线程之间可以直接共享数据,共用一块内存
4. 不同进程之间,无法直接共享数据。
3.2 JS 单线程运行
1. JS 是单线程运行的,一各时刻只能进行一个计算任务
2. JS 为什么设计为单线程?
多线程会有线程调度以及线程开启关闭的开启
JavaScript在浏览器上操作DOM,如果不是单线程,不好解决渲染同步问题。
3.3 同步代码和异步代码
1. 同步代码
同步代码,同步任务,安装顺序依次执行,上一个任务结束下一个任务开始
2. 异步代码
异步代码,异步任务,满足条件且线程空闲才能执行的任务。
3. 哪些是异步任务?
① 定时器的回调函数
② DOM 事件的回调函数
③ Ajax 的回调函数
④ Promise 的回调函数
4. 注意事项:
① 异步任务通常都是回调函数的形式,回调函数不一定是异步任务。
② 开启定时器、监听事件等操作是同步任务。
3.4 事件轮询机制
1. 执行栈
在主线程中,所有的任务(不论是同步任务还是异步任务)都在执行栈里面执行
2. 异步任务管理模块
包括定时器管理模块、DOM事件管理模块、Ajax管理模块等, 负责监听异步任务(回调函数)是否满足执行条件
3. 回调队列
满足执行条件的异步任务会进入回调队列,等待执行。
队列的特点: 先进先出,后进后出
4. 事件轮询模块(Event Loop)
负责监听执行栈是否空闲,一旦空闲,从回调队列中将异步任务依次放入执行栈执行
4 JS 实现多线程(了解)
1. 利用 Worker, JS 中可以实现多线程运行
2. 多线程运算适合耗时地计算,在子线程中计算不影响主线程
3. 子线程中无法使用 BOM、DOM
Worker 构造函数
Worker.prototype.postMessage() 向子线程发送数据
Worker.prototype.onmessage 事件,监听子线程发回数据