实例解析:对象/闭包函数/自调用 this指向与作用域问题

实例代码

<script>
	var num = 20;
	var obj = {
		num: 30,
		fn: (function(num){
			this.num *= 3;
			num += 15;
			var num = 45;
			return function(){
					this.num *= 4; 
					num += 20;
					}
			})(num)
	};
	var fn = obj.fn;
	fn();
	obj.fn();
	console.log(window.num,obj.num);//240 120
</script>

代码解析

在开始代码解析之前,需要先强调一个要点,即自调用函数是在它被定义的时候运行,而通常不是等待被调用,但是在本文实例中,自调用函数不仅被调用了,并且还运行了一部分,这来自于它有趣的结构——自调用函数与闭包的结合。

代码结构解析

对象的定义与调用

	var num = 20;
	var obj = {
		num: 30,
		fn: (function(num){...})(num)
	}
	var fn = obj.fn;
	fn();
	obj.fn();

隐藏部分代码后,可以看到,代码总体由全局变量num,对象obj组成,obj中含有属性num与方法fn。
obj.fn指向的是一个自调用函数1,有趣的是,自调用函数中为一个闭包2 ,这意味着在定义对象obj时,自调用函数将自动运行返回return3后的函数,定义结束后,obj.fn中将保存return后的值。

var fn = obj.fn;

该句指将obj.fn的值赋给全局量fn,上段指出,定义结束后,obj.fn存放的为return后的函数,故此句为将return后的函数赋给fn,运行后,全局fn为存放return后函数的函数。

注:此刻不能为obj.fn(),方法加括号是调用的含义,语句变为调用方法obj.fn,因return后的函数中无返回值设置,全局fn将被赋值underfind。

fn();

该句为调用全局函数fn(),全局函数fn的值来自于obj.fn,即return后的函数。

obj.fn();

该句为调用obj内部的方法fn,其实仍然是return后的函数。

综上,整段代码的结构为,定义一个带有自调用闭包函数的对象obj,对象定义后自调用函数运行完毕,返回内层函数给obj.fn,后将该方法赋给全局函数fn,在全局函数与obj环境下,分别调用fn。

自调用
调用
赋值
调用
定义对象obj
obj.fn==return function
obj对象
全局函数window.fn
全局window

代码内部解析

变量的作用域与this指代

文中所指代码均在普通模式下运行,严格模式下this无法指向window,而是指向undefined。

代码的难点在于对象的方法fn。

1		fn: (function(num){
2			this.num *= 3;                  
3			num += 15;
4			var num = 45;
5			return function(){
6					this.num *= 4;
7					num += 20;
8					}
9			})(num)//定义即运行

fn为自调用函数,即对象obj定义时,1-5行代码运行,运行后返回5-8行代码给obj.fn。
值得一提的是,由于自调用函数无调用对象,默认为全局对象window,故形参num指向全局变量num,传入值为20。
此后函数运行中用到了两个num,一个是this.num,此时由于调用者为全局对象window,this.num==window.num。
另一个是num,num是传入的形参,虽然实参值也来自于全局变量num,但是与window.num作用域不同,是完全不同的两个变量
num经过了两次声明,首先是作为实参的隐式声明,其后显式的声明提前后无效化。
运行中先将全局变量num20乘3变为60,再将形参num20+15变为35,后将45赋给形参,执行结束后全局变量num变为60,内部变量num变为45

定义后的obj.fn
1		fn: function(){
6					this.num *= 4;
7					num += 20;
8					}

fn与obj.fn

全局函数fn来自于obj.fn,函数代码相同,区别在于调用环境不同,代码运行时,this的指代也不同,fn中的this.num指代全局变量num,而obj.fn中指代obj.num的属性值
全局fn与obj.fn中相同的为内部num的值,当fn中出现num时,根据作用域链,默认先从函数内部找,函数内部未声明,因此函数为闭包内层函数,故从外层父函数找,得到父函数中的变量num,值为65。
作用域链中专有一格指向外层父函数的变量,阻止变量释放,当obj.fn赋给fn时一同赋值,故两者中内部变量num指向的是同一个变量,即return外层函数中的num变量。
调用全局函数fn时,全局变量num60乘4变为240,内部变量指向外层变量num45+20=65。
调用obj.fn时,obj.num属性30乘4变为120,指向外层变量的num65+20变为85。
最终输出结果为:

console.log(window.num,obj.num);//240 120

  1. 自调用函数格式为(function(形参){代码块})(实参)↩︎

  2. 闭包格式为function(形参){代码块;return function(形参){代码块}}↩︎

  3. 返回值函数,当程序运行至此时,结束函数并返回return后的语句值。 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值