详解JavaScript的闭包、IIFE、apply、函数与对象

网络编程 2025-04-06 05:25www.168986.cn编程入门

文章标题:JavaScript:闭包、IIFE、apply与函数对象

一、闭包的奥秘

让我们从JavaScript的闭包开始说起。你是否遇到过这样的问题:在页面中放置了多个div,每个div中都有一个字母,当点击这些div时,你希望它们显示各自的索引号。使用普通的for循环绑定事件时,你会发现点击每个div都弹出的是最后一个索引号。这是因为函数内部的变量与外部环境的变量共享同一个作用域。为了解决这个问题,我们需要用到闭包。闭包能够让我们在函数内部形成一块“私有空间”,保存函数的执行上下文。正是这个特性,使得我们可以实现上述的需求。

二、对象的世界

接下来,我们来JavaScript中的对象。对象是一种复合数据类型,它允许我们封装数据和方法。通过对象常量(字面量),我们可以轻松地创建对象。对象的取值、枚举(遍历)、更新与添加等操作,都是我们在日常开发中经常需要用到的功能。除此之外,对象的原型也是JavaScript中的重要概念。通过原型,我们可以实现对象的继承,从而提高了代码的可维护性。我们还可以对对象进行删除和封装操作。

三、函数的力量

在JavaScript中,函数是一等公民,它具有多种属性和方法。我们可以通过参数对象(arguments)获取函数传递的参数。构造函数可以帮助我们创建新的对象实例。函数调用是函数发挥作用的关键。除了基本的函数调用外,我们还可以使用call、apply、caller和callee等方法来调用函数。立即执行函数表达式(IIFE)也是JavaScript中的一大特色。通过IIFE,我们可以创建模块、封装代码块等。在这一部分,我们还会通过示例代码来演示各种IIFE的写法及其作用。

四、示例下载及实践

我们提供了一个示例下载部分,供读者下载并实践。这个示例将演示如何在页面中放置多个div,并使用闭包来实现点击每个div时显示正确的索引号。通过这个实践,你将更深入地理解闭包的原理和应用。

示例代码(HTML部分):

(与原文一致)

运行结果:当你点击每个div时,将会弹出正确的索引号。这是因为我们使用了闭包来保存每个函数的执行上下文,从而实现了预期的效果。

本文详细介绍了JavaScript中的闭包、对象、函数等相关知识,并通过示例代码来演示了这些知识的应用。希望读者能够通过本文的学习,更深入地理解JavaScript的奥秘。理解闭包与对象:深入JavaScript的核心概念

在JavaScript的世界中,闭包与对象是两个核心且深奥的概念。掌握它们,便掌握了JavaScript语言的一大精髓。让我们深入这两个主题,从理解其内在机制开始,领略它们在编程实践中的魅力。

一、闭包

让我们从一段简单的HTML代码和JavaScript脚本开始。在这段代码中,每个div元素都有一个点击事件,点击后会弹出索引值。这背后的机制就是闭包。

闭包概念:当一个内部函数被调用,就会形成闭包。闭包就是能够读取其他函数内部变量的函数。在一个函数内部定义另一个函数,会创建一个闭包环境,让返回的这个子程序“抓住”某些变量,以便在后续执行时可以保持对这些变量的引用。

在JavaScript中,由于其特有的“链式作用域”结构,子对象会向上寻找所有父对象的变量。这就是闭包在JavaScript中的工作原理。闭包让函数拥有比其通常作用域更长的生命周期,可以访问函数创建时的上下文环境。

二、对象

对象是JavaScript中的基本数据结构,它是一个“键/值”对的集合,并拥有一个连接到原型的隐藏连接。对象常量,也就是对象字面量,包含在一对花括号中的零个或多个“键/值”对。对象可以包含的内容多种多样,如数组、函数、对象、基本数据类型等。

对象的定义非常灵活,可以很简单,如一个空对象 `{}`;也可以很复杂,包含属性与方法。例如:

```javascript

var obj4 = {

price: 99,

inc: function() {

this.price += 1;

}

}

```

这里,`obj4`是一个对象,它有一个属性`price`和一个方法`inc`。方法是一种特殊类型的函数,它属于某个对象,可以在该对象的上下文中执行。

理解闭包和对象的关系:在复杂的应用中,对象可能会包含闭包。例如,一个对象的属性可能是另一个函数,这个函数形成了一个闭包,可以访问并操作对象的其它属性。这种结构使得JavaScript在处理复杂逻辑和数据存储时非常灵活和强大。

闭包和对象是JavaScript语言的核心部分,深入理解它们对于编写高效、优雅的JavaScript代码至关重要。通过掌握这两个概念,你可以在JavaScript的世界中自由驰骋,创造出无限可能。在JavaScript的世界里,对象是一种强大的数据结构,它如同一个容器,可以存储各种类型的数据。让我们深入了解JavaScript的对象特性,从创建、访问、遍历到更新和原型链的奥秘。

一、对象的创建与访问

我们来创建一个对象`obj5`,它包含了名字、爱好、朋友信息以及一个展示自身的函数。当我们调用`obj5[1].show()`时,控制台会输出“我是lucy”。这里的`this`关键字在函数中是动态的,指向的是调用者。

二、对象的取值

取值有两种常见方法。方法一直接使用点号运算,如`obj6.realname`。但如果对象的键名包含空格,如`"nick name"`,就不能直接使用点号运算。此时可以使用方法二,通过索引器获取值,如`obj6["nick name"]`。

三、对象的遍历

遍历对象可以使用`for..`循环。以`obj7`为例,循环会输出其所有属性和值。但需要注意的是,对象的属性是无序的,所以输出的顺序是不能保证的。

四、对象的更新与添加

在JavaScript中,对象可以通过引用传递。这意味着,当我们修改或添加对象的属性时,这些更改会影响到原始对象。以`obj8`为例,我们首先修改其`realname`属性为"Queen",然后添加了一个`weight`属性并赋予其值1000。我们还给对象添加了一个方法`show`,用于展示对象的属性。当我们调用`obj8.show()`时,会输出"Queen,1000"。进一步地,如果我们让另一个对象`obj9`指向`obj8`的引用,并修改`obj9.realname`为"Jack",再调用`obj8.show()`时,输出的将会是"Jack,1000",说明对象的修改是双向的。

五、对象的原型

JavaScript是一种动态类型的语言,与其他静态类型的语言如C和Java有所不同。JavaScript的对象可以连接到原型(prototype)以实现功能的扩展与继承。每个对象都链接到一个原型对象,这个原型对象可以拥有属性和方法,对象可以从其原型中继承这些属性和方法。通过字面量创建的对象都连接到`Object.prototype`,它是JavaScript中的顶级对象,类似于其他高级语言中的根类。这种原型继承的机制使得JavaScript的对象非常灵活和强大。

JavaScript的对象是一种复杂且功能丰富的数据结构。从创建、访问、遍历到更新和原型链的深入理解,都是掌握JavaScript编程的重要部分。一、扩展对象与原型链机制

在JavaScript中,我们正在为Object对象添加一个创建方法,模拟类似继承的功能。这个方法允许我们指定新创建对象的原型。通过模拟这一功能,我们能深入理解原型链机制在JavaScript中的核心作用。

假设我们有一段如下的JavaScript代码:

```javascript

if (!Object.hasOwnProperty('beget')) { // 如果Object没有beget方法,则添加它

Object.create = function(o) { // 创建对象的方法,指定原型为o

var F = function() {}; // 一个空的构造函数

F.prototype = o; // 指定该构造函数的原型为传入的对象o

return new F(); // 使用这个构造函数创建一个新对象并返回它

};

}

```

二、原型链的动态性和影响

原型关系是一种动态关系。如果我们修改了原型对象,那么所有基于这个原型创建的对象都会受到影响。例如:

```javascript

var lucy = Object.create(rose); // lucy继承rose的属性和方法

var jack = Object.create(rose); // jack也继承rose的属性和方法

// 修改rose的show方法

rose.show = function() { console.log("姓名->" + this.name); }; // 修改原型中的方法对所有继承者都有效

lucy.show(); // 输出新定义的show方法内容,"姓名->lucy" 访问的是修改后的原型中的方法而不是自身的属性或方法。jack也同样。所以此处可展示JavaScript原型链的动态性及其影响。对于属性和方法的变化都可以影响到已经创建的对象。关于函数和原型链的更深层次关系会在后续讲解中详细介绍。关于原型链在函数中的应用,将在后续章节中详细讨论。

参数对象(arguments)

在JavaScript函数中,有一个特殊的对象叫做arguments,它是一个类似数组的对象,用于表示传递给函数的参数。这个对象不声明也在函数中,可以在函数内部访问外部函数的任意内容。它不能直接访问外部函数的arguments和this对象。举个例子:

```javascript

function counter() {

var sum = 0;

for (var i = 0; i < arguments.length; i++) {

sum += arguments[i];

}

return sum;

}

console.log(counter(199, 991, 1, 2, 3, 4, 5)); // 输出结果:1205

console.log(counter()); // 输出结果:0

```

在这个例子中,我们创建了一个名为counter的函数,它通过遍历arguments对象中的每个参数并将它们相加来计算总和。当我们调用这个函数并传入多个参数时,它会返回这些参数的总和。如果不传入任何参数,则返回结果为0。另外值得注意的是,尽管arguments是一个隐式对象,但我们可以在函数内部使用它来获取传递给函数的参数信息。关于函数内部的arguments对象和this对象的访问权限问题,需要注意不能直接访问外部函数的这两个对象。

构造函数

在JavaScript中,对象构造函数是用于创建一个对象的特殊函数。它们允许我们定义对象的属性和方法。通过构造函数创建的对象实例具有特定的属性和行为。例如:

```javascript

function Student(name, age) { // 定义构造函数Student来创建学生对象实例

this.name = name; // 定义学生对象的属性name并赋值参数name的值给该属性

this.age = age; // 定义学生对象的属性age并赋值参数age的值给该属性

this.show = function() { // 定义学生对象的方法show用于输出学生的姓名和年龄信息到控制台日志中

console.log(this.name + "," + this.age); // 输出格式是“姓名,年龄”的格式信息字符串到控制台日志中展示给用户查看。这样就实现了通过构造函数创建学生对象实例的功能。同时每个实例都有各自独立的属性和方法供使用。这就是构造函数的强大之处所在。我们可以通过new关键字调用构造函数来创建一个新的对象实例并将其赋值给变量存储起来方便后续使用和管理。例如通过var rose = new Student("rose", 18);和var jack = new Student("jack", 20);创建了两个不同的学生对象实例分别存储在变量rose和jack中方便后续操作和使用它们提供的功能和属性方法等特性实现相关的操作任务或业务逻辑等应用场景。在这个例子中通过调用Student构造函数的show方法打印出了对象的属性和值体现了构造函数定义对象和方法的便捷性和高效性从而帮助我们更加高效地开发程序或构建应用程序等应用场景的需求和功能实现等目标。同时展示了JavaScript语言面向对象编程的特性和优势所在使得我们可以更加灵活地运用面向对象编程的思想和方法来设计和实现程序的功能需求。在实际应用中构造函数的用法非常广泛我们可以根据不同的应用场景和需求定义不同的构造函数来实现不同的功能需求例如创建不同的类定义不同的属性和方法等来满足实际的需求和目标实现等应用场景的拓展和开发需求等等等等…)。 ```javascript function Student(name, age) { this.name = name; this.age = age; this.show = function() { console.log("" + this.name + "," + this.age); }; } var rose = new Student("rose", 18); var jack = new Student("jack", 20); // 使用call方法调用show函数指定上下文为rose对象并输出信息到控制台日志中展示给用户查看示例中的运行结果说明call方法可以将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象从而实现了在调用函数时指定上下文的功能同时展示了JavaScript语言中函数调用的灵活性和高效性在实际应用中可以根据不同的需求使用不同的函数调用方式来实现不同的功能需求和目标实现等应用场景的拓展和开发需求等等等等…) 在实际应用中构造函数的用法非常广泛可以用于创建不同类型的对象实例并赋予它们不同的属性和行为从而实现不同的功能需求和目标实现等应用场景的拓展和开发需求等等等等…)。同时JavaScript语言中的函数调用方式也非常灵活除了直接使用函数名调用函数外还可以使用apply和call等方法来调用函数这些方法允许我们在调用函数时指定函数的上下文或传递参数等功能从而实现更加灵活和高效的函数调用和操作方式使得JavaScript语言在面向对象编程方面更加强大和易用从而提高了开发效率和代码质量在实际应用中可以根据具体的需求选择适合的函数调用方式来实现不同的功能需求和目标实现等应用场景的拓展和开发需求等等等等…)。在 JavaScript 中,方法调用和对象上下文之间有着密切的关联。这种关系通过 `call`、`apply` 和 `caller` 等属性得以体现。以下是对这些概念的深入和生动阐述。

让我们理解 `call` 和 `apply` 的区别与相似之处。在 JavaScript 中,每个函数都有其自身的上下文对象 `this`。当你调用一个函数时,可以选择为 `this` 指定一个特定的对象上下文。这就是 `call` 和 `apply` 的作用。它们允许你调用一个函数,并为其指定一个特定的上下文对象以及参数列表。

`call` 方法接受一个对象和一个参数列表,像这样:`func.call(thisArg, arg1, arg2, ...)`。而 `apply` 方法也接受一个对象和一个数组作为参数,数组中的元素将作为函数的参数传入,像这样:`func.apply(thisArg, [arg1, arg2, ...])`。它们的主要区别在于参数的传递方式。

接下来看一个生动的例子:

```javascript

// 定义一个构造函数和一个展示函数

function Student(name, age) {

this.name = name;

this.age = age;

}

function show(greeting, height) {

console.log(greeting + " " + this.name + ",年龄:" + this.age + ",身高:" + height);

}

// 创建两个学生对象

var rose = new Student("Rose", 18);

var jack = new Student("Jack", 20);

// 使用 call 调用 show 函数,指定上下文为 rose 对象

show.call(rose, "大家好", "178cm");

// 使用 apply 调用 show 函数,同样指定上下文为 jack 对象

show.apply(jack, ["Hello", "188cm"]);

```

在这个例子中,通过 `call` 和 `apply` 方法,我们可以改变 `show` 函数中的 `this` 指向不同的学生对象,从而输出不同的信息。

再来说说 `caller` 属性。当一个函数被另一个函数调用时,被调用函数的 `caller` 属性会指向调用它的函数。这在某些情况下可以帮助我们追踪函数的调用路径或进行调试。例如:

```javascript

function add() {

console.log("add 被调用");

console.log("调用 add 的函数是:" + add.caller);

}

function calc() {

add(); // 间接通过 calc 方法调用 add 方法

}

add(); // 直接调用 add 方法用于演示 caller 为 null 的情况

calc(); // 通过 calc 方法调用 add 方法来演示 caller 的功能

```

在这个例子中,通过 `caller` 属性,我们可以看到 `add` 函数是被哪个函数调用的。这对于理解代码的执行流程和调试非常有帮助。值得注意的是,如果在非函数调用的情况下尝试访问 `caller` 属性(例如直接在全局作用域中),它将是 `null`。由于安全性和隐私原因,某些浏览器可能会限制或禁用 `caller` 属性。因此在实际开发中需要注意其可用性。

JavaScript中的函数奥秘

在JavaScript中,函数是一种非常强大的工具,它们允许我们执行特定的任务并返回结果。让我们深入了解几个关于函数的重要概念,包括Callee、链式编程、立即执行函数表达式(IIFE)、匿名函数与函数表达式等。

Callee的概念

当函数被调用时,它的arguments.callee对象会指向自身,也就是一个对自己的引用。这是一个非常有用的特性,允许我们在函数内部引用函数本身。例如,在递归函数中,我们可以使用arguments.callee来调用自身。

链式编程的实现

在JavaScript中,我们可以通过返回函数本身来实现链式编程。当调用一个函数时,我们可以立即返回该函数,以便我们可以继续链式调用其他函数。这种技术使我们能够编写更简洁、更流畅的代码。

立即执行函数表达式(IIFE)

IIFE是Immediately-Invoked Function Expression的缩写,也就是立即执行的函数表达式。这种技术允许我们定义一个匿名函数并立即调用它。这是一种非常有用的模式,用于初始化代码、设置命名空间等。

匿名函数与函数表达式

匿名函数是没有名称的函数。在JavaScript中,我们经常使用匿名函数来实现事件绑定、回调等。它们也可以用于创建私有作用域。另一方面,函数表达式是一种可以赋值的函数声明。与函数定义不同,函数表达式中的参数和标识符都是可选的。完整的函数表达式需要一个赋值操作。

为了更好地理解这些概念,让我们看一些示例代码:

对于Callee:

```javascript

function add(n) {

console.log("add被调用");

if (n <= 2) {

return 1;

}

return add.caller(n - 1) + add.caller(n - 2);

}

```

在这个例子中,我们可以看到Callee是如何在递归函数中引用自身的。

对于链式编程:

```javascript

function chainCall() {

console.log("第一个函数调用");

return function() {

console.log("第二个函数调用");

return function() {

console.log("第三个函数调用");

};

};

}

在JavaScript编程中,函数和表达式的使用是非常核心的部分。让我们深入理解一下立即执行函数表达式(IIFE)和匿名对象。

让我们看一些基本的函数定义和表达式。

正常定义函数

这是一个普通的函数定义:

```javascript

function f1(){

console.log("正常定义f1函数");

}

```

被误解的函数表达式

如果我们尝试以下写法,会出现错误:

```javascript

function(){

console.log("报错Unexpected token (");

}()

```

原因是JavaScript解释器期待的是一个函数声明,而不是一个函数表达式。这里的括号使代码变成了一个尝试立即执行的函数表达式,但由于语法错误,所以会报错。

立即执行函数表达式(IIFE)

为了解决这个问题,我们需要正确使用函数表达式,如下:

```javascript

(function(){

console.log("IIFE,正常执行");

})();

```

这里的括号使得内部的函数表达式成为一个立即执行的函数表达式。解释器看到括号中的函数表达式时,会立即执行它。如果不加括号,它只会被视为一个函数声明,不会立即执行。当解释器遇到括号中的函数关键字时,它会识别这是一个函数表达式而不是声明。它能够正确执行而不会报错。这就是第3种写法能够立即执行并且不报错的原因。如果要调用一个匿名对象的方法,可以像下面这样操作:

``

分号的重要性与IIFE的魅力

在你的JavaScript代码中,你是否注意到过一个小小的符号——分号?它虽小,却在代码中扮演着举足轻重的角色。让我们先来通过一个简单的例子理解它的作用。

曾经有一段脚本,它试图通过立即执行的函数表达式(IIFE)来输出一个数值。脚本是这样的:

```javascript

var k = 100

(function (n){

console.log(n);

})(k);

```

不幸的是,这段脚本会报错。原因在于JavaScript解释器会认为`100`是函数名,而不是传递给函数的参数。这是因为JavaScript中语句的结束可以靠分号来标识,如果不加分号,解释器可能会误解你的意图。在IIFE之前添加一个分号是个明智的选择:

```javascript

var k = 100;

(function (n){

console.log(n);

})(k);

```

现在让我们进一步IIFE的作用。它不仅帮助我们避免语法错误,还能提升性能、压缩空间、避免冲突以及实现依赖加载。具体来说:

性能提升:通过参数传递常用全局对象如window、document、jQuery,在函数作用域内引用这些对象,可以减少作用域查找时间。JavaScript解释器在作用域内查找属性时,会沿着作用域链向上查找,直到全局范围。将全局对象放在IIFE作用域内可以提高查找速度和性能。例如:

```javascript

function (window, document, $) { / code here / }(window, document, window.jQuery);

```

空间压缩:通过参数传递全局对象,可以在压缩代码时将这些全局对象缩写为更精简的变量名。这有助于减小文件大小,加快加载速度。例如上面的函数就是一个很好的例子。

避免冲突:匿名函数内部可以形成一个块级私有作用域,这意味着你可以在函数内部定义变量和函数,而不必担心与全局作用域中的其他变量或函数发生冲突。这对于管理复杂的代码结构和避免命名冲突非常有用。

依赖加载:IIFE可以灵活地加载第三方插件,使用模块化加载方式如AMD、CMD等。例如,在A.html和B.html文件中引用公共的mon.js文件。只有A.html需要使用到StuObj对象,而B.html则不需要,但仍可以依赖其他方法来实现功能。通过这种方式,你可以更好地组织和管理你的代码,使其更加模块化和可维护。

分号虽小,作用却大;IIFE虽简单,却能展现出无穷的魔力。掌握好这些基础知识,你的JavaScript代码将变得更加优雅、高效且易于管理。JavaScript 代码重构与解读

让我们解读一下给出的代码片段。这段代码主要包含了两个 JavaScript 文件,Student.js 和 Common.js,以及两个 HTML 文件,A.HTML 和 B.HTML。还提到了关于 IIFE(立即执行函数表达式)的变形。接下来,我将对这段代码的每一部分进行解读并重构。

一、Student.js 和 Common.js 的解读与重构

在 Student.js 中,定义了一个对象 StuObj,它有一个方法 getStu 用于创建一个新的 Student 对象。Student 是一个构造函数,用于创建学生对象,并定义了一个 show 方法用于输出学生的名字。我们可以将这段代码重构得更加简洁易读:

```javascript

// Student.js

var StuObj = {

createStudent: function(name) {

return new Student(name);

}

}

function Student(name) {

this.name = name;

this.show = function() {

console.log("Hello, " + this.name);

}

}

```

Common.js 中的代码看起来有些混乱,似乎包含了无关的代码。假设我们的目标是在页面加载时创建一个新的 Student 对象并调用其 show 方法,我们可以将其重构如下:

```javascript

// Common.js

(function($) {

if($) {

$.createStudent("Tom").show();

}

})(StuObj || false); // 使用 || 运算符来确保当 StuObj 未定义时传递 false 给函数

```

二、HTML 文件的解读与重构

A.HTML 和 B.HTML 的主要区别在于加载的 JavaScript 文件和页面上的脚本。我们可以确保脚本按照正确的顺序加载,并根据需要进行调整。对于 HTML 文件本身,如果没有其他特定需求,可以保持原样或进行简单的格式化调整。

三 立即执行函数表达式的变形解读与重构

关于 IIFE 的变形部分,原始的 IIFE 变形是为了解决代码组织问题,将代码分成几个部分以便于阅读和维护。变形的 IIFE 可以将主要的功能函数作为参数传递给立即执行的函数,这样可以更好地组织代码并提高其可读性。我们可以将给出的变形代码重构为更加清晰和可维护的形式:

原始代码:

```javascript

Bootstrap编码奥秘:优雅构建现代网页的秘籍

让我们一起走进Bootstrap的世界,这是一个以简洁、灵活和可重用性为特点的开源前端框架。通过Bootstrap,开发者可以轻松地创建响应式和移动优先的网页。让我们一起揭开这个神奇框架的神秘面纱。

当我们提及Bootstrap的写法时,一个典型的模式是使用自执行函数(Immediately Invoked Function Expression,IIFE)。这是一个利用JavaScript的函数特性来创建封闭命名空间的技巧。代码示例如下:

```javascript

+function(yourcode) {

yourcode(window.jQuery, window, document);

}(function($, window, document) {

$(function() {

// jQuery DOM加载完成事件处理代码在此处编写

});

});

```

在这个例子中,我们看到了如何使用自执行函数来传递参数并执行代码块。这是一种封装代码、避免全局命名冲突的好方法。在Bootstrap开发中,这种模式非常常见。

我们还可以使用call或apply方法来调用函数。这两个方法都可以用来设置函数的执行上下文和传递参数。示例如下:

```javascript

(function(x){console.log(x)}).call(window,888); // 输出 888

(function(x){console.log(x)}).apply(window,[999]); // 输出 999

```

在这个例子中,我们看到了如何使用call和apply方法来设置函数的执行上下文并传递参数。这是一个强大的技巧,可以帮助我们更好地理解和使用JavaScript的函数特性。

以上就是本文的全部内容,希望这篇文章能够帮助你更好地理解Bootstrap的编码方式以及如何利用JavaScript的函数特性来构建优雅的现代网页。如果你对Bootstrap或其他前端技术有兴趣,不妨多多关注我们的博客和教程,我们会定期分享的技术和经验。记得多多支持狼蚁SEO!让我们一起在开发的世界里畅游!

对于Cambrian的渲染部分,我们可以使用`Cambrian.render('body')`来触发页面的渲染过程,确保网页内容能够正确展示给用户。让我们共同更多关于前端开发技术的奥秘吧!

上一篇:我的心里只有你没有他 下一篇:没有了

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by