JS高级02_原型、执行上下文、作用域、闭包
P20 P21没看 +22前11
1. 原型与原型链
- 函数的prototype属性(图)
每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
原型对象中有一个属性constructor, 它指向函数对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14function Person( ) {
}
//1.每一个函数都有一个属性prototype
console.log(Person.prototype);//空对象--->原型对象(显式原型对象)
//Person.prototype.constructor = 函数本身 声明当前的构造器是谁
var person1 = new Person(); //生成实例对象
//2.每一个实例对象身上都有一个属性_proto_,该属性指向当前实例对象的原型对象(隐式原型对象) 带下划线的叫隐式原型对象△
//3.构造函数的显式原型对象 === 当前构造函数实例对象 的隐式原型对象
console.log(Person.prototype === person1._proto_); // true
- 给原型对象添加属性(一般都是方法)
- 作用:函数的所有实例对象 都能有原型中的方法(或属性,属性更少因为它一般不公共),即它是公共的
- 好处:节省内存
1). 所有函数都有一个特别的属性:
`prototype` : 显式原型属性
2). 所有实例对象都有一个特别的属性:
`__proto__` : 隐式原型属性
另外:带下划线的是内置方法 以前不可以改动,ES6中可以改了。
3). 显式原型与隐式原型的关系
函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
原型对象即为当前实例对象的父对象
4). 原型链

原型链:△
当使用对象.属性(方法)的时候,
1.在自身 属性(方法)找
2.在自身的原型(是一个实例对象) 的属性(方法) 找
3.Object的原型的 属性(方法) 再原型对象就 = null了。真没有。
Object是个函数对象(就是个函数,但它也是个对象)不是实例对象,Object的原型是实例对象,它有一个toString()方法。
1 | |
再上课举例:

所有的实例对象都有__proto__属性, 它指向的就是原型对象
这样通过__proto__属性就形成了一个链的结构---->原型链
当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作
另外,新建一个变量,也就是new一个实例。
1 | |
函数对象 与 对象
1 | |
所有函数都由Function() 和 Object()派生

Function可以new自己,Object
Object可以new Function
金字塔:Object类—-Function Array类
另外:a instanceof b何时返回true(属于某一类、new b 能得到a):a的隐式原型链上 可以找到 b的显式原型对象
1 | |
5). 详图
function Foo () {}
var f1 = new Foo()
var f2 = new Foo()
var o1 = {}
var o2 = {}

2. 执行上下文与执行上下文栈
1). 变量提前声明与函数提前声明
提前声明讲得贼好:
https://www.cnblogs.com/lvonve/p/9871226.html
变量提前声明(预解析): 在变量定义语句之前, 就可以访问到这个变量(undefined)
函数提前声明: 在函数定义语句之前, 就执行该函数(函数本身)
先有函数提升, 后有变量提升
原理:
js引擎在js代码正式执行之前会做一些预处理工作
1、找var和function关键字
(不带var关键字的变量可以写,但是分两种情况:1.在全局写不会被预解析 2.在函数里写会自动升成全局的 进行更改或定义)
2.找到var以后将var后边的变量提前声明但是不赋值var a;
3.找到function以后定义该函数
1 | |
冷知识:
1全局定义变量不要定义name 因为window对象上本身就有name属性(一般的不定义的函数、属性会报错,而name不报错但是空的)
2函数有个默认的属性name === 函数名
2). 理解执行上下文 是什么?是全局、局部的 预习变量和函数
执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性(函数)。我认为就是提前声明!+ 找作用域链 + 找this?
执行上下文栈: 用来管理产生的多个执行上下文。不嵌套
在source中的 call stack中可查看
变量被写入对象之后,再遇到还会再读,写入值一遍,函数不会再读一遍了。
4). 执行上下文生命周期
全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
函数 : 准备调用函数时产生, 函数执行完时死亡。
?我理解:没等到调用那行呢,就才遍历到了函数名第一行,就产生了

5). 包含哪些属性:
全局 :
用var定义的全局变量 ==>undefined
使用function声明的函数 ===>function
this ===>window
函数 :
用var定义的局部变量 ==>undefined
使用function声明的函数 ===>function
this ===> 调用函数的对象, 如果没有指定就是window
形参变量 ===>对应实参值
arguments ===>实参列表的伪数组
6). 执行上下文创建和初始化的过程
执行上下文:
原理:
js引擎在js代码正式执行之前会先创建执行环境,在执行环境中作预处理工作工作内容:
过程:
1.创建空对象—->执行上下文对象
2.该空对象用于收集变量,函数,函数的参数(找var和function)
3.创建作用域链
4.确认this的指向: 全局:window 局部: this —>?
1 | |
冷知识:
所有的函数都有返回值,如果不指定返回值,就是undefined
嵌套函数的内部函数 没有被引用到(在fun1里面 fun2() ),fun2将不会被上下文。
1
2
3
4
5
6
7
8
9console. log( "xxx");
var a = 1;
function fun() {
console.log( '-----' );
function fun2(){
//空的 本该有fun2的提前声明,结果没有。
}
}
上下文练习题
1 | |
提前声明易考知识点:
1.当有同名function和变量,不管顺序,预解析结果一定是function。
原理:只要看见了function就不再另声明了。(注意仅限提前声明!!!!)
1 | |
2.
1 | |
3.
1 | |
3. 作用域与作用域链
1). 定义:
作用域: 代码执行区域。 在编码时就确定了, 不会再变化
作用域链: 多个嵌套的作用域 查找变量时 先找当前的,再找往外的,直到全局作用域。如果还没有,会报错,xxx is not defined
2). 分类:
全局
函数(局部)js没有块作用域(在ES6之前)
作用域销毁
局部销毁:函数执行完毕
全局销毁:关闭浏览器
3). 作用域作用
作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突
作用域链: 查找变量
4). 作用域 执行上下文 区别
作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了
执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失
联系:
- 执行上下文环境 是在对应的作用域中的
- 在作用域中查找变量去 当前作用域下的 执行上下文对象中找
1 | |
练习题
1 | |
4. 闭包
定义:是一个闭合的容器,可以理解为是一个对象,以键值对形式存在
特点:闭包在使用的时候通常会将内部的函数return出去
- 产生闭包的条件?
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
- 外部函数调用
2). 作用:
延长外部函数 局部变量的 生命周期
从外部访问函数内部的局部变量
3). 写一个闭包程序
1 | |
外部调用,操作内部变量的操作:
1 | |
4). 闭包应用:
循环遍历加监听(按钮赋值例子)
将内部的函数返出来
将函数作为实参传递给另一个函数调用
JS框架(jQuery)大量使用了闭包
生命周期
1 | |
5). 缺点及解决:
变量占用内存的时间可能会过长
可能导致内存泄露
解决: 能不用就不用闭包/及时释放 : var f = null; //让内部函数变量 成为垃圾对 象
习题
1 | |
1 | |
5. 内存溢出与内存泄露
1). 内存溢出
一种程序运行出现的错误
当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误
2). 内存泄露
占用的内存没有及时释放
内存泄露积累多了就容易导致内存溢出
常见的内存泄露:
意外的全局变量
没有及时清理的计时器或回调函数
闭包
函数高级补充知识:
1.同步与异步
同步:没有回调。阻塞后续代码执行,一个一个执行。
异步:一定有回调函数。可能不立马执行,但是非阻塞,同时进行。例如:定时器。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!