这是我参与「第四届青训营 」笔记创作活动的的第9天
1. 数据类型的分类和判断
1). 基本(值)类型
Number ----- 任意数值 -------- typeof
String ----- 任意字符串 ------ typeof
Boolean ---- true/false ----- typeof
undefined --- undefined ----- typeof/===
null -------- null ---------- ===
2). 对象(引用)类型
Object ----- typeof/instanceof
Array ------ instanceof
Function ---- typeof/instanceof
2. 数据,变量, 内存的理解
1). 什么是数据?
在内存中可读的, 可传递的保存了特定信息的'东东'
一切皆数据, 函数也是数据
在内存中的所有操作的目标: 数据
2). 什么是变量?
在程序运行过程中它的值是允许改变的量
一个变量对应一块小内存, 它的值保存在此内存中
3). 什么是内存?
内存条通电后产生的存储空间(临时的)
一块内存包含2个方面的数据
内部存储的数据
地址值数据
内存空间的分类
栈空间: 全局变量和局部变量
堆空间: 对象
4). 内存,数据, 变量三者之间的关系
内存是容器, 用来存储不同数据
变量是内存的标识, 通过变量我们可以操作(读/写)内存中的数据
3. 对象的理解和使用
1). 什么是对象?
多个数据(属性)的集合
用来保存多个数据(属性)的容器
2). 属性组成:
属性名 : 字符串(标识)
属性值 : 任意类型
3). 属性的分类:
一般 : 属性值不是function 描述对象的状态
方法 : 属性值为function的属性 描述对象的行为
4). 特别的对象
数组: 属性名是0,1,2,3之类的索引
函数: 可以执行的
5). 如何操作内部属性(方法)
.属性名
['属性名']: 属性名有特殊字符/属性名是一个变量
4. 函数的理解和使用
1). 什么是函数?
用来实现特定功能的, n条语句的封装体
只有函数类型的数据是可以执行的, 其它的都不可以
2). 为什么要用函数?
提高复用性
便于阅读交流
3). 函数也是对象
function instanceof Object===true
函数有属性: prototype
函数有方法: call()/apply()
可以添加新的属性/方法
4). 函数的四种不同角色
一般函数 : 直接调用
构造函数 : 通过new调用
方法: 通过对象调用
对象 : 通过.调用内部的属性/方法
5). 函数中的this
1. 理解this:
- 关键字
- 变量
2. this的指向问题
- 函数this不是函数定义的时候决定的
- 函数this指向谁看如何调用当前的函数
3. this指向分类
- 函数自调用: window
- 构造函数(new function): 当前构造函数的实例对象
- 对象.方法(): 对象本身
- fun.call/apply(指定的对象): 指定的对象
6). 匿名函数自调用:
(function(){
//实现代码
})()
专业术语为: IIFE (Immediately Invoked Function Expression) 立即调用函数表达式
7). 回调函数的理解
什么函数才是回调函数?
你定义的
你没有调用
但它最终执行了(在一定条件下或某个时刻)
常用的回调函数
dom事件回调函数
定时器回调函数
ajax请求回调函数(后面讲解)
生命周期回调函数(后面讲解)
1. 原型与原型链
1). 什么是原型对象:
1. 每个函数都有一个prototype属性,该属性指向的是原型对象(显示原型对象)
2. 每个实例对象身上都有一个__proto__属性,该属性指向的也是原型对象(隐式原型对象)
3. 构造函数的显示原型 === 当前构造函数实例对象的隐式原型对象
4. 原型对象的本质: 普通的Object实例
2). 什么是原型链
1. 查找对象的属性的时候先在自身找,如果自身没有沿着__proto__找原型对象
2. 如果原型对象上还没有,继续沿着__proto__,直到找到Object的原型对象
3. 如果还没有找到返回undefined
4. 原型链: 沿着__proto__查找属性(方法)的这条链就是原型链
2. 执行上下文与执行上下文栈
1). 变量提升与函数提升
1. js引擎在js代码正式执行之前会做一些预解析的工作
2. 找关键字: var, function
3. 找到var以后将var后边的变量提前声明,但是不赋值 var a;
4. 找到function以后定义对应的函数,也就是说函数在预解析的时候已经定义完毕
5. 预解析: 全局预解析,局部预解析
6. 注意:
- 全局预解析在定义函数的时候不关心函数是否被使用
- 函数局部预解析的时候如果内部函数没有被使用就不会提前定义
2). 理解
1. 理解:
- 执行上下文抽象的概念,代表了代码执行的环境,包含: 执行环境,变量对象,this,作用域链
2. 流程:
- js引擎在js代码正式执行之前会先创建一个执行环境(开发商批的地,工程队施工的环境)
- 进入该环境以后创建一个变量对象(打地基),该对象用于收集当前环境下的: 变量,函数,函数的参数,this
- 找关键字var ,function
- 确认this的指向
- 创建作用域链
3. 重点:
- 执行上下文是动态创建的
- 尤其是针对函数,每调用一次函数都会创建一次执行上下文
执行上下文栈: 用来管理产生的多个执行上下文
3). 分类:
全局: window
函数: 对程序员来说是透明的
4). 生命周期
全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
函数 : 调用函数时产生, 函数执行完时死亡
5). 包含哪些属性:
全局 :
用var定义的全局变量 ==>undefined
使用function声明的函数 ===>function
this ===>window
函数
用var定义的局部变量 ==>undefined
使用function声明的函数 ===>function
this ===> 调用函数的对象, 如果没有指定就是window
形参变量 ===>对应实参值
arguments ===>实参列表的伪数组
6). 执行上下文创建和初始化的过程
全局:
在全局代码执行前最先创建一个全局执行上下文(window)
收集一些全局变量, 并初始化
将这些变量设置为window的属性
函数:
在调用函数时, 在执行函数体之前先创建一个函数执行上下文
收集一些局部变量, 并初始化
将这些变量设置为执行上下文的属性
3. 作用域与作用域链
1). 理解:
作用域:
- 抽象的概念
- 用来决定代码执行的范围, 变量所属的范围
- 作用域是代码定义的时候决定的
- 作用域作用:
- 隔离变量
- 规定其之后的作用域链是什么样的,体现: [[scopes]]: 上一级作用域链
作用域链:
- 作用域链是一个链表结构
- 该结构内保存的是一个个的变量对象
- 作用域链什么时候创建的:在js代码正式执行之前创建的
2). 分类:
全局
函数
js没有块作用域(在ES6之前)
eval()作用域
3). 作用
作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突
作用域链: 查找变量
4). 区别作用域与执行上下文
作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了
执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失
联系: 执行上下文环境是在对应的作用域中的
4. 闭包
1). 闭包形成的条件:
- 函数嵌套
- 内部函数引用外部函数的局部变量
- 内部函数被使用,注意: 函数变量提升的时候如果内部函数没有被使用,在预解析的过程中不会定义内部函数
2). 什么是闭包:
- 闭包是一个存在内部函数的引用关系
- 该引用指向的是外部函数的局部变量对象(前提是内部函数使用了外部函数的局部变量)
3). 写一个闭包程序
function fn1() {
var a = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn1();
f();
f();
4). 闭包的作用:
- 延长外部函数变量对象的生命周期
- 使用闭包能够间接的从函数外部访问函数内部的私有变量
5). 闭包应用:
循环遍历加监听: 给多个li加点击监听, 读取当前下标
模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为
JS框架(jQuery)大量使用了闭包
6). 闭包的优缺点:
- 优点: 延长外部函数变量对象的生命周期
- 缺点: 延长外部函数变量对象的生命周期(占内存,如果不及时清除容易造成内存溢出,泄漏)
解决: 及时清除闭包 : f = null; //让内部函数对象成为垃圾对象( 内部函数身上没有指针指向)
7). 闭包的生命周期:
- 产生:在嵌套的内部函数定义执行完时就产生(不是在调用)
- 死亡:在嵌套的内部函数成为垃圾对象时
5. 内存溢出与内存泄露
1). 内存溢出
一种程序运行出现的错误
当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误
2). 内存泄露
占用的内存没有及时释放
内存泄露积累多了就容易导致内存溢出
常见的内存泄露:
意外的全局变量
没有及时清理的计时器或回调函数
闭包