一.JavaScript 高级
1.任务目标
- 函数进阶 自执行函数
- 原型与原型链
- 作用域与作用域链
- 闭包及预解析
2 .重点
- 原型与原型链
- 作用域与作用域链
- 闭包及预解析
3.难点
- 原型与原型链
- 作用域与作用域链
二.核心内容
函数进阶
函数的定义方式
- 函数声明
- 函数表达式
new Function
函数声明
function foo () {
}
函数表达式
var foo = function () {
}
函数声明与函数表达式的区别
- 函数声明必须有名字
- 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
- 函数表达式类似于变量赋值
- 函数表达式可以没有名字,例如匿名函数
- 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用
下面是一个根据条件定义函数的例子:
if (true) {
function f () {
console.log(1)
}
} else {
function f () {
console.log(2)
}
}
以上代码执行结果在不同浏览器中结果不一致。
不过我们可以使用函数表达式解决上面的问题:
var f
if (true) {
f = function () {
console.log(1)
}
} else {
f = function () {
console.log(2)
}
}
函数的调用方式
- 普通函数
- 构造函数
- 对象方法
高阶函数
- 函数可以作为参数
- 函数可以作为返回值
作为参数
案例一:高阶函数之函数作为参数使用
// function f1(fn) {
// console.log("f1的函数");
// fn();//此时fn当成是一个函数来使用的
// }
// //fn是参数,最后作为函数使用了,函数是可以作为参数使用
// //传入匿名函数
// f1(function () {
// console.log("我是匿名函数");
// });
// //命名函数
// function f2() {
// console.log("f2的函数");
// }
// f1(f2);
// //函数作为参数的时候,如果是命名函数,那么只传入命名函数的名字,没有括号
// function f1(fn) {
// setInterval(function () {
// console.log("定时器开始");
// fn();
// console.log("定时器结束");
// },1000);
// }
//
// f1(function () {
// console.log("好困啊,好累啊,就是想睡觉");
// });
</script>
作为返回值
案例一:高阶函数之函数作为返回值使用
<script>
// function f1() {
// console.log("f1函数开始");
// return function () {
// console.log("我是函数,但是此时是作为返回值使用的");
// }
//
// }
//
// var ff=f1();
// ff();
// var num=10;
// console.log(typeof num);//获取num这个变量的数据类型
// var obj={};//对象
// //判断这个对象是不是某个类型的
// console.log(obj instanceof Object);
// //获取某个对象的数据类型的样子
// //Object.prototype.toString.call(对象);//此时得到的就是这个对象的类型的样子
//
//
//
// //此时输出的是Object的数据类型 [object Object]
// console.log(Object.prototype.toString());
// //输出的数组的数据类型 [object Array]
// console.log(Object.prototype.toString.call([]));
//
// var arr=[10,20,30];
// console.log(Object.prototype.toString.call(arr));
//
console.log(Object.prototype.toString.call(new Date()));
//获取某个对象的类型是不是你传入的类型
//[10,20,30] 是不是"[object Array]"
//type---是变量----是参数----"[object Array]"
//obj---是变量-----是参数----[10,20,30];
//判断这个对象和传入的类型是不是同一个类型
function getFunc(type) {
return function (obj) {
return Object.prototype.toString.call(obj) === type;
}
}
var ff = getFunc("[object Array]");
var result = ff([10, 20, 30]);
console.log(result);
var ff1 = getFunc("[object Object]");
var dt = new Date();
var result1 = ff1(dt);
console.log(result1);
</script>
原型与原型链
-
原型(prototype)
所有函数都有一个特别的属性:
prototype : 显式原型属性所有实例对象都有一个特别的属性:
proto : 隐式原型属性 -
显式原型与隐式原型
函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
实例对象的proto: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
原型对象即为当前实例对象的父对象3.原型链
所有的实例对象都有proto属性, 它指向的就是原型对象
这样通过proto属性就形成了一个链的结构---->原型链
当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作
原型(prototype)
1.函数的prototype属性(下图)
每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
原型对象中有一个属性constructor, 它指向函数对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Ms297gz-1676883782856)(函数.assets\image-20220804111328603.png)]
2.给原型对象添加属性(一般都是方法)
作用: 函数的所有实例对象自动拥有原型中的属性(方法)
案例一 原型:
![image-20220804111845446](函数.assets\image-20220804111845446.png)
显式原型与隐式原型
1.每个函数function都有一个prototype,即显式原型(属性)
2.每个实例对象都有一个__proto,可称为隐式原型(属性)
3.对象的隐式原型的值为其对应构造函数的显式原型的值
4.内存结构(下图)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9S1XxQdt-1676883782857)(函数.assets\image-20220804112356074.png)]
5.总结:
函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
对象的proto属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了
案例二:显式原型与隐式原型
<!--
1. 每个函数function都有一个prototype,即显式原型(属性)
2. 每个实例对象都有一个__proto__,可称为隐式原型(属性)
3. 对象的隐式原型的值为其对应构造函数的显式原型的值
4. 内存结构(图)
5. 总结:
* 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
* 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
* 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
-->
<script type="text/javascript">
//定义构造函数
function Fn() { // 内部语句: this.prototype = {}
}
// 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象
console.log(Fn.prototype)
// 2. 每个实例对象都有一个__proto__,可称为隐式原型
//创建实例对象
var fn = new Fn() // 内部语句: this.__proto__ = Fn.prototype
console.log(fn.__proto__)
// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype===fn.__proto__) // true
//给原型添加方法
Fn.prototype.test = function () {
console.log('test()')
}
//通过实例调用原型的方法
fn.test()
</script>
原型链
1.原型链(下图)
1.1访问一个对象的属性时,
1.1.1 先在自身属性中查找,找到返回
1.1.2如果没有, 再沿着proto这条链向上查找, 找到返回
1.1.3如果最终没找到, 返回undefined
1.2别名: 隐式原型链
1.3作用: 查找对象的属性(方法)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mjYqxvfs-1676883782857)(函数.assets\image-20220804112944041.png)]
案例三:原型链
<!--
1. 原型链(图解)
* 访问一个对象的属性时,
* 先在自身属性中查找,找到返回
* 如果没有, 再沿着__proto__这条链向上查找, 找到返回
* 如果最终没找到, 返回undefined
* 别名: 隐式原型链
* 作用: 查找对象的属性(方法)
2. 构造函数/原型/实体对象的关系(图解)
3. 构造函数/原型/实体对象的关系2(图解)
-->
<script type="text/javascript">
// console.log(Object)
//console.log(Object.prototype)
console.log(Object.prototype.__proto__)
function Fn() {
this.test1 = function () {
console.log('test1()')
}
}
console.log(Fn.prototype)
Fn.prototype.test2 = function () {
console.log('test2()')
}
var fn = new Fn()
fn.test1()
fn.test2()
console.log(fn.toString())
console.log(fn.test3)
// fn.test3()
/*
1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
*/
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // true
/*
2. 所有函数都是Function的实例(包含Function)
*/
console.log(Function.__proto__===Function.prototype)
/*
3. Object的原型对象是原型链尽头
*/
console.log(Object.prototype.__proto__) // null
</script>
案例四:_原型链_属性问题
<!--
1. 读取对象的属性值时: 会自动到原型链中查找
2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
-->
<script type="text/javascript">
function Fn() {
}
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a, fn1)
var fn2 = new Fn()
fn2.a = 'yyy'
console.log(fn1.a, fn2.a, fn2)
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
var p1 = new Person('Tom', 12)
p1.setName('Bob')
console.log(p1)
var p2 = new Person('Jack', 12)
p2.setName('Cat')
console.log(p2)
console.log(p1.__proto__===p2.__proto__) // true
</script>
案例五:原型综合示例
<!--
1. 函数的prototype属性(图)
* 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
* 原型对象中有一个属性constructor, 它指向函数对象
2. 给原型对象添加属性(一般都是方法)
* 作用: 函数的所有实例对象自动拥有原型中的属性(方法)
-->
<script type="text/javascript">
function MyClass() {}
console.log(MyClass.prototype, typeof MyClass.prototype)//object "object"
//通过MyClass创建的实例mc,mc2
var mc = new MyClass();
var mc2 = new MyClass();
//注意1:当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,
//----指向该构造函数的原型对象,我们可以通过__proto__来访问该属性
console.log(mc2.__proto__ == MyClass.prototype);//true
//注意2:原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,
//----我们可以将对象中共有的内容,统一设置到原型对象中。
MyClass.prototype.a = 123; //向MyClass的原型中添加属性a
MyClass.prototype.sayHello = function() { //向MyClass的原型中添加一个方法
alert("hello");
};
mc.a = "我是mc中的a"; // 向mc中添加a属性
//注意3:当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,
//----如果没有则会去原型对象中寻找,如果找到则直接使用
console.log(mc.a);//我是mc中的a
console.log(mc2.a);//123
mc2.sayHello();//弹窗hello
</script>
其它in , hasOwnProperty()
1.使用in检查对象中是否含有某个属性时
即使对象中没有,但是原型中有
---- 返回 true
2.使用对象的hasOwnProperty()来检查对象自身中是否含有某个属性
只有当对象自身中含有
----- 返回 true
案例六:判断属性
<script type="text/javascript">
function MyClass() {}
MyClass.prototype.name = "我是原型中的名字";
var mc = new MyClass();
mc.age = 18;
console.log(mc.name); //我是原型中的名字
console.log("name" in mc); //true
console.log(mc.hasOwnProperty("age")); //true
console.log(mc.hasOwnProperty("hasOwnProperty")); //false
/*
* 原型对象也是对象,所以它也有原型,
* 当我们使用一个对象的属性或方法时,会现在自身中寻找,
* 自身中如果有,则直接使用,
* 如果没有则去原型对象中寻找,如果原型对象中有,则使用,
* 如果没有则去原型的原型中寻找,直到找到Object对象的原型,
* Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined
*/
//console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));
//console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));
//console.log(mc.__proto__.__proto__.__proto__);
//console.log(mc.hello);
//console.log(mc.__proto__.__proto__.__proto__)
</script>
其它:instanceof
1.表达式: A instanceof B
2.B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
案例七:探索instanceof
<!--
1. instanceof是如何判断的?
* 表达式: A instanceof B
* 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
2. Function是通过new自己产生的实例
-->
<script type="text/javascript">
/*
案例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
</script>
作用域与作用域链
1.理解:
作用域: 一块代码区域, 在编码时就确定了, 不会再变化
作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量
2.分类:
全局作用域
函数作用域(局部作用域)
没有块作用域(ES6有了)
3.作用
作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突
作用域链: 查找变量
4.区别作用域与执行上下文
作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了
执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失
联系: 执行上下文环境是在对应的作用域中的
案例一:作用域
<!--
1. 理解
* 就是一块"地盘", 一个代码段所在的区域
* 它是静态的(相对于上下文对象), 在编写代码时就确定了
2. 分类
* 全局作用域
* 函数作用域
* 没有块作用域(ES6有了)
3. 作用
* 隔离变量,不同作用域下同名变量不会有冲突
-->
<script type="text/javascript">
/* //没块作用域
if(true) {
var c = 3
}
console.log(c)*/
var a = 10,
b = 20;
function fn(x) {
var a = 100,
c = 300;
console.log('is fn()', a, b, c, x)
function bar(x) {
var a = 1000,
d = 400
console.log('id bar()', a, b, c, d, x)
}
bar(100)
bar(200)
}
fn(10)
</script>
案例二: 变量提升和函数提升
<script>
//变量提升!
var name = "haha";
function changeName() {
console.log(name);
var name = "xixi";
}
changeName(); //undefined ??
console.log(name); //haha
/* 函数提升:
* 只有函数声明形式才能被提升
* */
//函数声明
function myTest1() {
func();
function func() {
console.log("我可以被提升");
}
}
myTest1();
//函数表达式
function myTest2() {
func();
var func = function() {
console.log("我不能被提升");// > TypeError:func is not a function
}
}
myTest2();
</script>
执行上下文区别作用域
执行上下文创建和初始化的过程
全局:
在全局代码执行前最先创建一个全局执行上下文(window)
收集一些全局变量, 并初始化
将这些变量设置为window的属性
函数:
在调用函数时, 在执行函数体之前先创建一个函数执行上下文
收集一些局部变量, 并初始化
将这些变量设置为执行上下文的属性
案例 三:作用域与执行上下文
<!--
1. 区别1
* 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
* 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
* 函数执行上下文是在调用函数时, 函数体代码执行之前创建
2. 区别2
* 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
* 执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放
3. 联系
* 执行上下文(对象)是从属于所在的作用域
* 全局上下文环境==>全局作用域
* 函数上下文环境==>对应的函数使用域
-->
<script type="text/javascript">
var a = 10,
b = 20
function fn(x) {
var a = 100,
c = 300;
console.log('is fn()', a, b, c, x)
function bar(x) {
var a = 1000,
d = 400
console.log('is bar()', a, b, c, d, x)
}
bar(100)
bar(200)
}
fn(10);
/*
is fn() 100 20 300 10
is bar() 1000 20 300 400 100
is bar() 1000 20 300 400 200
*/
</script>
作用域链
理解
多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
查找变量时就是沿着作用域链来查找的
查找一个变量的查找规则
在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
<!--
1. 理解
* 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
* 查找变量时就是沿着作用域链来查找的
2. 查找一个变量的查找规则
* 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
* 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
* 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
-->
<script type="text/javascript">
var a = 1
function fn1() {
var b = 2
function fn2() {
var c = 3
console.log(c)
console.log(b)
console.log(a)
console.log(d)
}
fn2()
}
fn1()
/*
3
2
1
ReferenceError: d is not defined
*/
</script>
闭包
什么是闭包
闭包就是能够读取其他函数内部变量的函数,
由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
理解一: 闭包是嵌套的内部函数(绝大部分人)
理解二: 包含被引用变量(函数)的对象(极少数人)
注意: 闭包存在于嵌套的内部函数中
案例一:闭包的说明-引入
需求: 点击某个按钮, 提示"点击的是第n个按钮"
<button>测试1</button>
<button>测试2</button>
<button>测试3</button>
<!--
需求: 点击某个按钮, 提示"点击的是第n个按钮"
-->
<script type="text/javascript">
var btns = document.getElementsByTagName('button')
//遍历加监听
// for (var i = 0,length=btns.length; i < length; i++) {
// var btn = btns[i]
// //将btn所对应的下标保存在btn上
// btn.index = i
// btn.onclick = function () {
// alert('第'+(this.index+1)+'个')
// }
// }
/**/
//利用闭包
for (var i = 0,length=btns.length; i < length; i++) {
(function (j) {
var btn = btns[j]
btn.onclick = function () {
alert('第'+(j+1)+'个')
}
})(i)
}
</script>
闭包的用途:
- 可以在函数外部读取函数内部成员
- 让函数内成员始终存活在内存中
如何产生闭包
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
产生闭包的条件
函数嵌套
内部函数引用了外部函数的数据(变量/函数)
案例二:理解闭包
<!--
1. 如何产生闭包?
* 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
2. 闭包到底是什么?
* 使用chrome调试查看
* 理解一: 闭包是嵌套的内部函数(绝大部分人)
* 理解二: 包含被引用变量(函数)的对象(极少数人)
* 注意: 闭包存在于嵌套的内部函数中
3. 产生闭包的条件?
* 函数嵌套
* 内部函数引用了外部函数的数据(变量/函数)
-->
<script type="text/javascript">
function fn1 () {
var a = 2
var b = 'abc'
function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)
console.log(a)
}
// fn2()
}
fn1()
function fun1() {
var a = 3
var fun2 = function () {
console.log(a)
}
}
fun1()
</script>
案例三:常见的闭包
<!--
1. 将函数作为另一个函数的返回值
2. 将函数作为实参传递给另一个函数调用
-->
<script type="text/javascript">
// 1. 将函数作为另一个函数的返回值
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4
// 2. 将函数作为实参传递给另一个函数调用
function showDelay(msg, time) {
setTimeout(function() {
alert(msg)
}, time)
}
showDelay('zhengzhou', 2000)
</script>
闭包的作用
延长局部变量的生命周期
让函数外部能操作内部的局部变量
问题:
<!--
1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
1. 函数执行完后, 函数内部声明的局部变量是否还存在? 一般是不存在, 存在于闭中的变量才可能存在
2. 在函数外部能直接访问函数内部的局部变量吗? 不能, 但我们可以通过闭包让外部操作它
-->
<script type="text/javascript">
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
// return a
}
function fn3() {
a--
console.log(a)
}
return fn3
}
var f = fn1()
f() // 1
f() // 0
</script>
闭包的生命周期
产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
死亡: 在嵌套的内部函数成为垃圾对象时
案例四:闭包的生命周期
<!--
1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
2. 死亡: 在嵌套的内部函数成为垃圾对象时
-->
<script type="text/javascript">
function fn1() {
//此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
var a = 2
function fn2 () {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4
f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
</script>
应用
定义JS模块
具有特定功能的js文件
将所有的数据和功能都封装在一个函数内部(私有的)
只向外暴露一个包信n个方法的对象或函数
模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
案例五:闭包的应用_自定义JS模块 1
创建myModule.js
function myModule() {
//私有数据
var msg = 'My Class'
//操作数据的函数
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}
在页面中引入创建的JS
<!--
闭包的应用2 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
var module = myModule()
module.doSomething()
module.doOtherthing()
</script>
案例五:闭包的应用_自定义JS模块2
创建myModule2.js
(function () {
//私有数据
var msg = 'My Class'
//操作数据的函数
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法)
window.myModule2 = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})()
在页面中引入创建的JS
<!--
闭包的应用2 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
myModule2.doSomething()
myModule2.doOtherthing()
</script>
缺点
函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
容易造成内存泄露
解决方案
能不用闭包就不用
及时释放
案例六:闭包的缺点及解决
<!--
1. 缺点
* 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
* 容易造成内存泄露
2. 解决
* 能不用闭包就不用
* 及时释放
-->
<script type="text/javascript">
function fn1() {
var arr = new Array[100000]
function fn2() {
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
f = null //让内部函数成为垃圾对象-->回收闭包
</script>
三、总结
-
1.原型与原型链
原型(prototype)
显式原型与隐式原型
原型链
其他 -
2.作用域与作用域链
作用域
作用域与执行上下文
作用域链
其他 -
3.闭包
理解闭包
闭包的作用
闭包的生命周期
闭包的应用
LowerCase())
}//向外暴露对象(给外部使用的方法)
window.myModule2 = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})()
在页面中引入创建的JS
```javascript
<!--
闭包的应用2 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
myModule2.doSomething()
myModule2.doOtherthing()
</script>
缺点
函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
容易造成内存泄露
解决方案
能不用闭包就不用
及时释放
案例六:闭包的缺点及解决
<!--
1. 缺点
* 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
* 容易造成内存泄露
2. 解决
* 能不用闭包就不用
* 及时释放
-->
<script type="text/javascript">
function fn1() {
var arr = new Array[100000]
function fn2() {
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
f = null //让内部函数成为垃圾对象-->回收闭包
</script>
三、总结
-
1.原型与原型链
原型(prototype)
显式原型与隐式原型
原型链
其他 -
2.作用域与作用域链
作用域
作用域与执行上下文
作用域链
其他 -
3.闭包
理解闭包
闭包的作用
闭包的生命周期
闭包的应用
缺点