Nodejs JavaScript 闭包的理解

Nodejs JavaScript 闭包的理解

关于闭包,阮一峰的这篇文章 写得比较清晰易懂。 不过需要注意的是闭包不是函数,而是函数的作用域。由于 JavaScript 的作用域不是由 block 符号 {} 来界定,而是函数,所以两者容易混淆。阮一峰说闭包是函数估计是帮助大家理解吧。

var name = "The Window";
 var object = {
     name : "My Object",
     getNameFunc : function(){
         var that = this;
         return function(){
             return that.name;
    	};
  	}
 };
 alert(object.getNameFunc()()); 

以上是他最后的思考题。因为闭包内的函数只能访问闭包内的变量,所以 this 必须要赋给 that 才能引用。

作用域

JavaScript 的闭包其实是一个作用域(scope),而这个作用域就是闭包内部的函数可以访问和修改变量的范围(注意因为闭包是外部函数的 {} 划定的作用域,所以提到函数时一般是指 {} 大括号内部申明的函数)。换句话说,闭包允许一个函数访问内部的所有变量和其他函数,只要这个函数是在这个闭包的作用域内申明的。

一个函数可以访问含有自身申明的闭包的原始作用域。

闭包作用域:

  • 函数的参数在函数的闭包作用域内。
  • 所有在函数自身作用域(函数的{})外的变量,甚至那些在函数申明之后申明的变量,都可以在函数内引用。

使用时注意闭包的开销,可能会影响性能。阮的文章有解释。

闭包的一个常用功能就是封装变量,类似其他语言的私有变量,来限制变量的作用域,不污染全局作用域。

我们也可以通过闭包内的函数来修改闭包内的变量值。所以,闭包不是一个简单的固定状态,而是可以随时改变的封装。

函数的偏应用(Partially Applying Function)

偏应用就是为一个多元函数(接受多个参数的函数)在调用前指定部分参数,从而在调用时可以省略这些参数。实际上,偏应用化一个函数就是返回一个预定义参数的新函数。 这种使用返回函数预先提供前几个参数的的技术叫做科里化(currying)。

立即被执行的函数

(function(){console.log("ran!");})();

这个函数会被立即执行,而不仅仅是定义。

在这个 <code>(…)()</code> 立即执行函数里,第一对括号只是分隔号,就像 <code>(3+2) * 4</code> 的括号功能一样。不过第二对括号是操作符,类似 <code>var sum = add(1,2);</code> 的括号。

this

在对象中,调用变量必须使用 this,不然会调用到对象外的全局同名变量。 在函数中,this 是函数所属的对象。如果使用 ‘use strict’, 函数里的 this 会变为 undefined。一般只有在初始函数(Constructor) 里才使用 this。

注意:闭包里没有 this 参数,因为每个函数调用有自己的 this。

在阮的思考题里,我们可以使用 bind 来达到目的。

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        return function(){
            return this.name;
        }.bind(this);
	}
};
console.log(object.getNameFunc()());

‘use strict’

尽量在 js 文件开始处使用 ‘use strict’ 来减少陷入 JavaScript 里暗藏的坑。比如下面的代码就在 strict 模式下不允许。

function func(){
    this.a = 100;
    a  = 33;
};
this.a = 200;
console.log(this.a); // 非 strict 输出 200
func();
console.log(this.a); // 非 strict 输出 33

在闭包里使用循环

需要注意的一点: 闭包和循环如果同时使用的话有时会有问题,因为闭包内的变量是保存变化的,如果创建闭包之后再使用函数的话,循环里的 i 可能会一直是最后一个值(比如最大值)。


9 回复

Nodejs JavaScript 闭包的理解

闭包是JavaScript中一个非常重要的概念,它涉及到函数的作用域链和变量的访问权限。闭包并不是一个新的函数,而是一种机制,使得一个函数能够访问其外部函数的变量。

闭包的基本概念

闭包使一个函数可以访问并操作其外部函数的变量。即使外部函数已经执行完毕,闭包仍然可以让内部函数访问这些变量。这种机制使得闭包非常适合用于封装变量和实现数据的私有性。

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        var that = this;
        return function () {
            return that.name;
        };
    }
};

console.log(object.getNameFunc()()); // 输出 "My Object"

在这段代码中,getNameFunc 返回了一个内部函数,该内部函数可以访问 object 对象中的 name 属性。尽管 getNameFunc 已经执行完毕,但内部函数依然可以通过闭包访问 object.name

使用 this 关键字

在对象方法中,this 关键字通常指向当前对象。但在闭包中,this 并不能直接传递给内部函数,因此需要使用 that 进行引用。

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            return this.name;
        }.bind(this);
    }
};

console.log(object.getNameFunc()()); // 输出 "My Object"

这里使用了 .bind(this) 方法,确保内部函数中的 this 指向 object 对象。

偏应用(Partial Application)

偏应用是指预先绑定函数的部分参数,从而生成新的函数。这在某些场景下非常有用,可以简化代码。

function multiply(x, y) {
    return x * y;
}

// 偏应用:预先绑定 x=2
const double = multiply.bind(null, 2);

console.log(double(5)); // 输出 10

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

立即执行函数表达式是一种常见的模式,用于创建独立的作用域,避免全局命名空间污染。

(function () {
    console.log("ran!");
})();

use strict 模式

使用 "use strict"; 可以启用严格模式,防止一些潜在的错误,并提高代码的可维护性。

"use strict";

function func() {
    this.a = 100;
    a = 33; // 在严格模式下会报错
}

this.a = 200;
console.log(this.a); // 输出 200
func();
console.log(this.a); // 输出 200

闭包与循环

在循环中使用闭包时需要注意,闭包可能会捕获循环变量的最终值,而不是每次迭代的值。

for (let i = 0; i < 5; i++) {
    setTimeout(() => {
        console.log(i);
    }, 1000);
}

上面的代码会输出 5 5 5 5 5,而不是 0 1 2 3 4。为了避免这种情况,可以使用 let 替代 var,或者在每次迭代中创建一个新的闭包。

通过上述例子,我们可以看到闭包在JavaScript中的强大之处以及如何正确地使用它们。希望这些示例能帮助你更好地理解和使用闭包。


那js中为什么有闭包呢?你有没有你考虑过?

看不懂,有点高深

this是不是用做指定作用域的

说的太复杂了

刚遇到的一个闭包使用 $(e).on(“click”,dismiss,function(){ var $this = $(this),$e = $("#e"); $e.unbind(“click”); $e.bind(“click”,function(){ return function(){ var text = $("#fomuDIVContent").text(); $this.data(“fomu”,text) } }($this)) });

在《javascript高级程序设计》书中,闭包章节讲的非常清楚,看过的所有解释中最浅显易懂的

PS LZ例子中的 getNameFunc : function(){ var that = this; return function(){ return that.name; }; } 返回的是一个匿名函数,匿名函数的this是会绑定到window的,所以需要用that指定回object。

闭包可以用在实现私有变量,以及需要局部变量常驻内存的场景。

其实我很纳闷,为什么一直流传JS简单……我觉得一旦涉及对象和原型,JS一点都不简单。至少网络上都没有一个能够很简明简单回答这些基本概念的帖子。于是这个简单的语言,大家一直在围绕着一些基础的概念在争论。。。。每个人都解释的很有道理的样子……但是到底是啥。。

js一点都不简单~~~

Node.js 中 JavaScript 闭包的理解

闭包是一个常见的概念,尤其是在 JavaScript 中。闭包不仅是一个函数,而是一个函数和它周围状态(词法环境)的组合。这意味着,函数可以访问其自身作用域内的变量、外部函数的变量,以及全局变量。

示例代码

首先,让我们来看一个简单的闭包示例:

function createCounter() {
    let count = 0;

    return function() {
        count++;
        console.log(count);
    }
}

const counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2

在这个例子中,createCounter 函数返回了一个匿名函数,该匿名函数访问了 createCounter 函数内部定义的变量 count。每次调用 counter() 时,count 的值都会增加并输出,这表明闭包使得 count 变量在多次调用中保持其状态。

闭包的作用

闭包的主要用途之一是封装变量。例如,你可以使用闭包来模拟类中的私有变量:

function Counter() {
    let count = 0;

    this.increment = function() {
        count++;
        console.log(count);
    }

    this.decrement = function() {
        count--;
        console.log(count);
    }
}

const myCounter = new Counter();
myCounter.increment(); // 输出 1
myCounter.increment(); // 输出 2
myCounter.decrement(); // 输出 1

这里,count 变量只能在 Counter 对象的方法中访问,不能从外部直接访问。

this 关键字

this 关键字在闭包中通常用于引用当前对象。如果不使用 bind 或箭头函数,this 在回调函数中可能会指向全局对象或 undefined(在严格模式下)。

var object = {
    name: "My Object",
    getNameFunc: function() {
        return () => {
            console.log(this.name);
        };
    }
};

console.log(object.getNameFunc()()); // 输出 "My Object"

在这个例子中,使用箭头函数确保 this 指向 object 对象。

立即执行函数 (IIFE)

立即执行函数表达式 (IIFE) 是另一种常见的闭包用法,它可以避免变量泄露到全局作用域:

(function() {
    console.log('ran!');
})();

这段代码定义了一个匿名函数,并立即执行它。这样可以创建一个新的作用域,不会污染全局命名空间。

总结

闭包是 JavaScript 中一个强大且有用的特性。它不仅可以用来封装变量,还可以控制变量的作用域,防止全局污染。然而,使用闭包时也需要注意性能影响,特别是在大量使用的情况下。

回到顶部