函数的内容

一.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>

原型与原型链

  1. 原型(prototype)

    所有函数都有一个特别的属性:
    prototype : 显式原型属性

    所有实例对象都有一个特别的属性:
    proto : 隐式原型属性

  2. 显式原型与隐式原型

    函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
    实例对象的proto: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
    原型对象即为当前实例对象的父对象

    3.原型链

​ 所有的实例对象都有proto属性, 它指向的就是原型对象
​ 这样通过proto属性就形成了一个链的结构---->原型链
​ 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
​ 当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作

原型(prototype)

​ 1.函数的prototype属性(下图)
​ 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
​ 原型对象中有一个属性constructor, 它指向函数对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Ms297gz-1676883782856)(函数.assets\image-20220804111328603.png)]

​ 2.给原型对象添加属性(一般都是方法)
​ 作用: 函数的所有实例对象自动拥有原型中的属性(方法)

案例一 原型:
image-20220804111845446
显式原型与隐式原型

​ 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. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:
  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.闭包
    理解闭包
    闭包的作用
    闭包的生命周期
    闭包的应用
    缺点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值