`
koolC
  • 浏览: 19315 次
  • 性别: 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  

相关推荐

    JS中匿名函数的多种调用方法1

    它们在各种上下文中有着广泛的应用,如事件处理、回调函数等。本篇文章将详细讲解14种不同的匿名函数调用方法。 1. **调用函数得到返回值**: 使用强制运算符`()`来立即执行函数,例如 `(function(x,y){ alert(x+y...

    javascript笔记之匿名函数和闭包.docx

    在JavaScript中,匿名函数是一种没有名称的函数,通常作为参数传递或者用于立即执行。在上述文档中,我们看到几种不同的匿名函数使用方式: 1. **匿名函数自执行**:`(function(){})()`,这种写法会立即执行该匿名...

    Javascript中匿名函数的调用与写法实例详解(多种)

    JavaScript中的匿名函数是一种没有名称的...它们可以用于执行一次性任务、创建私有作用域、封装变量、实现模块化,以及在各种上下文中作为参数传递。了解这些不同的用法可以帮助开发者更有效地利用JavaScript的特性。

    JavaScript中匿名函数用法实例

    在编程语言JavaScript中,匿名函数是函数定义的一种特殊形式,它没有具体的函数名。在JavaScript编程实践中,匿名函数非常灵活且用途广泛。可以用于事件处理、回调函数、立即执行函数表达式(IIFE)等多种场景。 在...

    JS匿名函数内部this指向问题详析

    JavaScript中的`this`关键字是一个非常重要的概念,它在不同场景下有不同的指向,特别是在匿名函数中,`this`的指向问题经常让开发者感到困惑。本文将深入解析JS匿名函数内部`this`的指向,并通过实例来阐述相关知识...

    理解JavaScript中函数的使用

    当一个函数被调用时,JavaScript引擎会为该函数创建一个新的执行上下文。执行上下文包括了变量环境、作用域链和this绑定等关键组成部分。了解这些概念有助于理解函数如何工作以及如何有效地使用它们。 #### 示例...

    浅析PHP中的闭包和匿名函数

    这允许闭包在对象的上下文中执行,就像它是一个成员函数: ```php class Person { private $name; public function __construct($name) { $this-&gt;name = $name; } public function greetUsingClosure($...

    JS中this上下文对象使用方式

    JavaScript中的this关键字是极为重要的一部分,它指向函数执行的上下文对象。由于JavaScript是一门基于原型的动态语言,其函数的调用方式对this的指向有着直接的影响。在不同的上下文中,this可能指向不同的对象,...

    Node.js-Microjob是Node.js线程的一个微小封装旨在使用匿名函数执行繁重的CPU负载

    Microjob的工作原理是,它接收一个匿名函数作为参数,这个函数就是将在Worker线程中执行的CPU密集型任务。Microjob会自动管理线程的创建、任务分发以及结果的收集。这样做的好处是,开发者无需直接与复杂的Worker ...

    JavaScript函数的特性与应用实践深入详解

    在创建函数时,JavaScript引擎会为每个函数添加两个隐藏属性:一个是函数上下文(也称为调用者或this值),另一个是实现函数功能的代码。函数对象还有一个特别的属性prototype,它指向一个新的对象,这个对象的...

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

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

    Javascript自执行匿名函数(function() { })()的原理浅析

    自执行匿名函数常用于创建独立的执行上下文,避免污染全局作用域,或者在定义时就立即执行一段代码。 首先,我们来看看自执行匿名函数的基本形式: ```javascript (function() { // 函数体 })(); ``` 在这个结构...

    JS函数总结

    // 绑定到obj对象的上下文中 ``` #### 五、递归函数与特殊构造器 递归函数是在函数体内直接调用自身的一类函数。在递归调用中,必须有一个终止条件,否则会导致无限循环和栈溢出错误。 ```javascript var func =...

    浅谈JavaScript function函数种类

    这意味着即使外部函数的执行上下文已经从调用栈中清除,闭包仍然可以访问那些变量。这在实现某些算法和模式时非常有用,比如模块化代码和私有变量。 在实际编程中,这些函数类型的使用取决于具体的场景和需求。普通...

    Javascript函数手册.rar

    在JavaScript中,函数可以有多种类型,包括匿名函数、箭头函数、默认参数、剩余参数以及立即执行的函数表达式(IIFE)。这些特性使得JavaScript函数在实际开发中非常灵活。例如,匿名函数常用于事件处理或作为其他...

    JSFunction函数深入解析.pdf

    在这个深入解析中,我们将探讨`Function`的一些关键特性和用法,包括函数的定义方式、返回值、执行环境、作用域、闭包以及上下文。 1. **函数定义** - **函数声明**:`function sum(num1, num2) { return num1 + ...

    【JavaScript源代码】JavaScript中箭头函数与普通函数的区别详解.docx

    - 箭头函数的语法更紧凑,可以视为匿名函数的一种简写形式。如果函数体只包含一个表达式,那么可以省略大括号和return关键字。例如: ```javascript let fun = () =&gt; console.log('lalalala'); ``` - 普通函数...

    javascript强制函数自动执行.pdf

    这两种定义方式各有特点,并且在不同的上下文中有着不同的行为。 #### 函数声明(Function Declaration) 函数声明是定义一个命名函数的最常见方式。它由`function`关键字后跟函数名、参数列表以及函数体组成。...

    浅谈Javascript中的函数、this以及原型

    `this`关键字在JavaScript中用于引用函数调用时的上下文对象,它指向函数执行时所在的对象。在全局环境中,`this`通常指向`window`对象。当我们通过`.call()`或`.apply()`方法显式改变函数的上下文时,`this`会指向...

    this和执行上下文实现代码

    // 实例化后的b不包含this_v,因为返回的匿名函数上下文是全局对象。 b(); // v1v1v1 // undefined ``` 理解`this`和执行上下文对于解决JavaScript中常见的问题,如闭包、事件处理和对象方法的调用至关重要。在编写...

Global site tag (gtag.js) - Google Analytics