`
koolC
  • 浏览: 19101 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
最近访客 更多访客>>
社区版块
存档分类
最新评论

谈JS中匿名函数的上下文环境

阅读更多

     受一位热心网友的关注和提问,我又对JS中的匿名函数的上下文环境作了详细地分析和实验,下面把我对该问题的一点思考与大家分享:

 

    关键字 this    


    在JavaScript的对象系统中,使用关键字 this 的地方:在对象的方法(或属性)被调用时,指代调用该方法(或属性)的对象实例。举个例子:
   

function foo(){
	this.property = "red";
	this.method = function(){...}
}

 

 

    使用 this 的原因 

    因为在实例化对象时,总是不能确定开发者会使用什么样的变量名。使用 this,即可在任何多个地方重用同一个函数。请思考下面的例子:

   

function showColor() {
	alert(this.color);
};

var oCar1 = new Object;
oCar1.color = "red";
oCar1.showColor = showColor;

var oCar2 = new Object;
oCar2.color = "blue";
oCar2.showColor = showColor;

oCar1.showColor();		//输出 "red"
oCar2.showColor();		//输出 "blue"

 
    注意,引用对象的属性时,必须使用 this 关键字。例如,如果采用下面的代码,showColor() 方法不能运行:

 

   

function showColor() {
	alert(color);
};

 

 

    如果不用对象或 this 关键字引用变量,ECMAScript 就会把它看作局部变量或全局变量。然后该函数将查找名为 color 的局部或全局变量,但是不会找到。结果如何呢?该函数将在警告中显示 "null"。

 

    函数与方法  

    由于在JavaScript中不明确区分函数与方法。因此有些代码看起来很奇怪:

   

function foo() {
	// 下面的this指代调用该方法的对象实例
	if (this===window) {
		document.write("call a function.", "<br />");
	}
	else {
		document.write("call a method, by object: ", this.name, "<br />");
	}
}

function MyObject(name) {
	// 下面的this指代new关键字新创建实例
	this.name = name;
	this.foo = foo;
}

var obj1 = new MyObject("obj1");
var obj2 = new MyObject("obj2");

// 测试1:	作为函数调用
foo();		//Output=>call a function.

// 测试2:	作为对象方法的调用
obj1.foo();	//Output=>call a method, by object: obj1
obj2.foo();	//Output=>call a method, by object: obj2

// 测试3:	将函数作为“指定对象的”方法调用
foo.call(obj1);	//Output=>call a method, by object: obj1
foo.apply(obj2);	//Output=>call a method, by object: obj2

 

 

    在测试2里,obj1/obj2对foo()的调用是很普通的调用方法。
    而测试3中的call()与apply()就比较特殊。在这个测试中,foo()仍然作为普通函数来调用,只是JavaScript的语言特性允许在call()/apply()时,传入一个对象实例来指定foo()的上下文环境中所出现的this关键字的引用。需要注意的是,此时的foo()仍旧是一个普通函数调用,而不是对象方法调用,该测试只是将foo函数的this原来引用‘window’更改为后来参数对象,不要被测试三的结果迷惑了,不信看下面例子:

   

function foo() {
	// 下面的this指代调用该方法的对象实例
	if (this===window) {
		document.write("call a function.", "<br	/>");
	}
	else{
		document.write("call a function or method.", "<br />");
	}
}

function MyObject(name) {
	// 下面的this指代new关键字新创建实例
	this.name = name;
	this.foo = function(){
		document.write("call a method, by object: ", this.name, " ; and then ");
		foo();
	};
}

var obj1 = new MyObject("obj1");
var obj2 = new MyObject("obj2");

// 测试1: 作为函数调用
foo();		//Output=>call a function.

// 测试2: 作为对象方法的调用
obj1.foo();	//Output=>call a method, by object: obj1 ; and then call a function.
obj2.foo();	//Output=>call a method, by object: obj2 ; and then call a function.

// 测试3: 将函数作为“指定对象的”方法调用
foo.call(obj1);	//Output=>call a function or method.
foo.apply(obj2);	//Output=>call a function or method.

 

 

    测试3结果中明显不包含字符串“call a method, by object:”,说明该调用明显不是method,而是function。

    但再仔细想想,严格来说,全局foo()函数也是方法,它其实是其上下文环境(window)的方法,所以以下三种调用方式等价:
   

foo();
this.foo();
window.foo();

 
    这不由得让我想到JS里面的另一个机制:函数即对象,对象即可以有属性和方法;而方法又可以是函数。这样就拧成一个类似“鸡生蛋,蛋生鸡”的循环了。

 

 

    署名函数和匿名函数的上下文环境  

 

    有了以上基础,再比对一下署名函数和匿名函数的上下文环境,看他们之间有什么区别之处,例子如下:

   

// 署名函数
function foo() {
	// 下面的this指代调用该方法的对象实例
	if (this===window) {
		document.write("foo的上下文环境(即this值)是window.", "<br	/>");
	}
}

// 作为函数调用
window.foo(); //Output=>foo的上下文环境(即this值)是window. 说明foo函数属于window这个上下文环境,即foo是window对象的一个方法

this.foo(); //Output=>foo的上下文环境(即this值)是window.	说明此处的this指代的就是window对象

foo(); //Output=>foo的上下文环境(即this值)是window. 说明foo如果本就属于这个上下文环境时,this可以省略

//三个函数调用最终结果相同,说明全局函数的上下文环境就是window。

// 匿名函数
(function(){
// 下面的this指代调用该方法的对象实例
	if (this===window) {
		document.write("匿名函数的上下文环境(即this值)是window.",	"<br />");
	}
})() //Output=>匿名函数的上下文环境(即this值)是window.

 

 

    从上面两个函数例子对比可以说明,匿名函数的上下文环境(this)也是window,它也可以称得上是window对象的方法,只不过是匿名的,而且不能像window.foo()这样方式直接调用执行它(假设如果能像这样方式调用执行,那调用格式就该是window.匿名函数()了,当然这是开玩笑了,要不然JS规定匿名函数这一规则就没必要了),只能让它自动执行。
    在这种条件下,看来署名函数和匿名函数区别不在于上下文环境,而是调用执行的方式。
    既然两种函数的上下文环境相同,而且函数就是对象,那么在塑造两种不同函数的对象,并访问其中的属性(或方法)试试,看看二者有没有区别,例子如下:
   

// 署名函数
function foo() {
	this.color = "red";
}

var f = new foo();
alert(window.f.color);	// Output=>red
alert(this.f.color);		// Output=>red
alert(f.color);		// Output=>red
alert(window.color);		// Output=>undefined
alert(this.color);   		// Output=>undefined
alert(color);		// Output=>js error:color未定义
// 六种调用最终结果对比,说明对象f的上下文环境不是window,而是window树结构的一个分支,应该说f属于window。

// 匿名函数
(function(){
	this.color="blue";
})();

alert(window.color);		// Output=>blue

(function(){
alert(window.color);		// Output=>blue 说明匿名函数的上下文环境是window
alert(this.color);		// Output=>blue 在window中的变量被称为全局变量,可以省略this
alert(color);		// Output=>blue 尽管可以省略this,但不建议用这样的方式,因为如果该匿名函数存在一个同名的局部变量,那么它调用的就不再是全局变量了
})();
// 通过对匿名函数属性的调用方式及结果对比,更坚定一个事实:匿名函数的上下文环境是window!

 

 

    通过上面的详细举例,相信您一定明白了。以上如有错误,请多批评指正!

分享到:
评论
1 楼 ninedoors 2012-07-28  

相关推荐

    decofun:调试工具。 根据周围的上下文命名匿名函数

    它解析您的代码并根据上下文命名任何匿名函数。 版本 1.2.x安装npm i decofun特征命令行工具 sudo npm -g i decofun从 1.2.x 版开始,我们可以简单地使用deco可执行文件代替node来检测匿名函数。 deco examples/...

    使用环境React上下文api

    被包装在Context.Consumer中,或者可以使用useContext钩子,或者可以将静态contextType值设置为Context(仅适用于类组件) 这将通过上下文变量使数据可用于组件,该上下文变量通常使用匿名函数参数来提供。...

    浅谈javascript的call()、apply()、bind()的用法

    函数式编程:由于JavaScript支持匿名函数,因此可以将函数作为对象来使用, 所以JavaScript不仅支持过程式编程(面向对象也是过程式编程的一种),还支持函数式编程。 上下文 函数的每次调用都会拥有一个特殊值——...

    浅析JavaScript作用域链、执行上下文与闭包

    以下面的代码片段举例说明,通常来说(基于栈的实现,如 C 语言) foo 被调用之后函数内的本地变量 scope 会被释放,但是从词法上看 foo 的内嵌匿名函数中 scope 应该指的是 foo 的本地变量 scope ,并且实际上代码...

    lindenmayer:具有完整的经典L系统库(分支,上下文相关,参数化)和多用途现代L系统LSystem实现,可以将javascript函数用作生产。 对于如何进行可视化没有意见

    这个想法是要有一个功能强大但简单的基本功能,该功能可以通过简单地允许匿名函数作为生产程序来处理大多数用例,这使其与传统的L系统相比非常灵活。 该库还可以在某种程度上解析Aristid Lindenmayers 1990年原创的...

    JavaScript详解(第2版)

     14.9.3 继承和上下文选择器   14.10 定位元素和层   14.10.1 绝对定位   14.10.2 〈div〉容器   14.10.3 绝对定位   14.10.4 相对定位   14.10.5 z索引和三维   14.11 如何与JavaScript融合...

    JavaScript中的数组遍历forEach()与map()方法以及兼容写法介绍

    •每一次执行匿名函数的时候,还给其传递了三个参数值:数组中的当前项item,当前项的索引index,原始数组input; •理论上这个方法是没有返回值的,仅仅是遍历数组中的每一项,不对原来数组进行修改;但是我们可以...

    毕业设计电商网站源码-fe_base_knowledge:我也来整理一份前端基础知识(面试题)

    当调用一个函数时,程序流就进入该被调用函数内,此时引擎就会为该函数创建一个新的执行上下文,并且将其压入到执行栈顶部(作用域链)。浏览器总是执行位于执行栈顶部的当前执行上下文,一旦执行完毕,该执行上下文...

    rails-js-kickstart

    如果statefits实用程序“适应”了它们的当前上下文,则会遍历给定的一组CSS类。 安装 将此行添加到您的应用程序的Gemfile中: gem 'rails-js-kickstart' 然后执行: $ bundle 或将其自己安装为: $ gem ...

    javascript 面向对象全新理练之继承与多态

    1 又是几个基本概念 为什么要说又呢? 在讨论继承时,我们已经列出了一些基本概念了,那些概念是跟... 也是两个过程,第一个过程是定义变量 a 和一个匿名函数,第二个过程是把匿名函数赋值给变量 a。 变量定义和函数

    python入门到高级全栈工程师培训 第3期 附课件代码

    02 上下文管理协议 04 异常的构成简单了解 05 描述符应用 08 类的装饰器的基本原理 09 类的装饰器增强版 10 类的装饰器的应用 11 自定制property 12 自定制property流程分析 13 自定制property实现延迟计算功能 14 ...

    IBM WebSphere Portal门户开发笔记01

    26、EJPAS0012E: 属性页面已启动(不带任何上下文信息) 133 27、详细内容页面展现的BODY内容间隙太大 134 28、修改默认应用编写模板 135 29、使用导出与导入WCM内容库的方式来部署WCM 135 30、命令EXPORT/IMPORT...

    Javascript技巧之不要用for in语句对数组进行遍历

    一,为什么不要用for in语句 jqModal这个jquery插件估计很多人都使用过,在jqModal... 第二个for in遍历,根据上下文确认this[i]是一个数组对象(Array)。 很多JS先驱者都告诫过我们不要对数组对象使用for in语句进行

    jquery插件使用方法大全

    第四行代码得到context为上下文的table里面所有的链接元素。 如果你熟悉CSS,你会觉得这些写法很眼熟!对了。正是。看出奥妙了吧。jQuery就是如此强大,你可以轻易地找到DOM中的任何元素,而这也是jQuery设计之初...

    ASP.NET.4揭秘

    21.3 使用包含数据上下文的数据服务773 21.4 小结775 第五部分 站点导航 第22章 使用导航控件778 22.1 理解站点地图778 22.2 使用sitemappath控件780 22.3 使用menu控件785 22.3.1 声明式添加menu条目785 22.3.2 ...

Global site tag (gtag.js) - Google Analytics