【问题标题】:What is the exact use of closure?闭包的具体用途是什么?
【发布时间】:2019-06-23 17:33:02
【问题描述】:

closure 在我的每次采访中都在伤害我。终于我了解了closure。但我的问题是下面这些sn-ps有什么区别?我们应该在哪些场景下使用闭包?

关闭:

function add(x) {
    return function(y) {
    return function(z) {
    return x + y + z;
    } 
    };
}

console.log(add(1)(2)(3))

除了闭包,简单地我们可以将三个参数传递给一个方法。

function add(x,y,z) {
   
    return x + y + z;
}

console.log(add(1,2,3))

那么为什么我需要使用闭包呢?

【问题讨论】:

  • 这与 闭包 关系不大。是的,它们被使用了,但是闭包并不一定意味着像第一个示例那样构建您的代码。您关注的主题是“currying”和/或“函数的部分应用”。

标签: javascript closures


【解决方案1】:

'add(1)(2)(3)' 和 'add(1,2,3)' 有相同的结果。但我可以用前者做很多事情。 举个例子吧。

function add(x) {
    return function(y) {
    return function(z) {
    return x + y + z;
    } 
    };
}

const WELCOME_POINT = 10;
const VIP_POINT = 20;
function myPointCaculator(currentPoint, predicate) {
  if(typeof predicate === 'number')
    return currentPoint + predicate;
  else if(typeof predicate === 'function') {
    return currentPoint + predicate(WELCOME_POINT);
  } else {
    return currentPoint;
  }
}
  
const eventPoint = add(WELCOME_POINT)(VIP_POINT);
myPointCaculator(WELCOME_POINT, eventPoint);
'add(1,2,3)' 将直接执行,但 'add(1)(?)(?)' 可以作为参数传递给函数。那是不同的。这是关于“柯里化”和“一阶函数”的 javascript 中的一阶函数可以使您使用函数式编程进行编码。

【讨论】:

    【解决方案2】:

    这是一个非常综合且几乎无用的示例。简而言之,您需要闭包,因为 Javascript 使用大量基于回调的代码,并且您希望在这些回调中保留对前一个范围的可访问性:

    function foo(bar) {
        setTimeout(function () {
            alert(bar);
        });
    }
    

    这里的闭包机制允许alert(bar) 仍然可以访问bar,即使foo 已经完成执行并且它的所有变量都应该超出范围。这就是 closure 的含义,它关闭范围并使其保持可用。

    【讨论】:

      【解决方案3】:

      此特定示例显示了启用partial application 的柯里化函数。它可能对单个函数调用似乎没有用,但也允许您编写

      function add(x) { return function(y) { return x + y; }; }
      
      const add2 = add(2);
      console.log(add2(1)); // 3
      console.log(add2(40)); // 42

      当然,如果 add 函数没有被柯里化,你可以使用

      function add2(y) {
        return add(2, y);
      }
      

      但这太冗长了。


      当然,这里只突出单个闭包用例,有many many more

      【讨论】:

        【解决方案4】:

        在此示例中,输出没有真正的区别。但是,让我们让它变得更简单:

        function add(a) {
          return function(b) {
            return a+b;
          }
        }
        
        
        console.log(add(1)(2));
        function add(a, b) {
          return a+b;
        }
        
        console.log(add(1, 2));

        为简单起见,该函数采用两个参数。一次你必须在调用时同时传递它们,另一次你必须一个一个地传递它们。让我们看看它有什么用处:

        function add(a) {
          return function(b) {
            return a+b;
          }
        }
        
        let numbers = [1, 2, 3, 4, 5, 6];
        
        //lets add the same number to all members of the array:
        const numberToAdd = 5;
        
        //traditionally
        const newArray = []; //make a new array
        
        for (let num of numbers) { //define a loop
          newArray.push(num + numberToAdd); //populate the array
        }
        
        console.log(newArray);
        
        //functional
        const mappedArray = numbers.map(num => num + numberToAdd); //using .map to handle everything
        console.log(mappedArray);
        
        //using the function we have
        const betterMappedArray = numbers.map(add(numberToAdd));
        console.log(betterMappedArray);

        因此,通过.map 的函数式方法更短更容易,但可以通过传递函数进一步改进。如果没有 add 函数,您仍然会传递一个函数,您定义一个新的添加函数每次您只想向数组中添加一些东西。如果你想添加可变数量,那么没有add,你必须创建两个基本上都做同样事情的新函数

        function add(a) {
          return function(b) {
            return a+b;
          }
        }
        
        let numbers = [1, 2, 3, 4, 5, 6];
        
        
        console.log("using .map() with new functions");
        console.log(
          numbers
            .map(num => num + 5)
            .map(num => num + 10)
        );
        
        console.log("using .map() with add()");
          
        console.log(
          numbers
            .map(add(5))
            .map(add(10))
        );

        如您所见,并排放置每次都创建新功能似乎很浪费。即使签名为add(a, b),因此它需要两个参数,这意味着您必须调用numbers.map(num => add(num, 5)),这不会改善任何事情。

        请记住,add 非常简单 - 您可能有一个更复杂的函数。尽管如此,还是用这个简单的例子,让我们重新编写这个例子来展示它是如何有用的:

        function add(a) {
          return function(b) {
            return a+b;
          }
        }
        
        const shoppingCart = [
          {name: "item1", price: 1},
          {name: "item2", price: 2},
          {name: "item3", price: 3},
          {name: "item4", price: 4},
          {name: "item5", price: 5},
          {name: "item6", price: 6},
        ];
        
        const applyHandlingTax = add(5);
        const applyShippingTax = add(10);
        
        const numbers = shoppingCart.map(item => item.price); //extract prices from the shopping cart
        
        const finalPrices = numbers
          .map(applyHandlingTax)
          .map(applyShippingTax);
        
        console.log(finalPrices);

        这是一个带有夸张数字的幼稚示例,但它只是用来说明我们可以在这里做什么。这在功能上与之前的 sn-p 相同,但正如您所见,我们现在的 业务逻辑 几乎没有任何变化。

        • 我们将applyHandlingTax 定义为添加5。我们还将applyShippingTax 定义为添加10
        • 这两个都是函数,因此我们可以通过.map 将它们直接应用到我们拥有的结果集上,从而减少我们必须编写的代码量。
        • 业务规则很容易理解,因为您正在编写非常易于阅读的代码 - 您只需很少的编程知识即可理解 numbers.map(applyHandlingTax).map(applyShippingTax) 正在对每个数字征收运费和手续费。作为开发人员,很明显您正在添加运费和手续费。
        • 如果我们确定add 可以正常工作,那么根据定义任何从它派生的东西也可以工作 - 无需测试applyShippingTax。事实上,没什么可测试的 - 没有函数体,我们没有为它编写任何逻辑,我们只为它使用函数的结果。
        • 我们编写的唯一有意义的代码是用于定义add 并从项目中提取价格。即便如此,后者也可以像我们对add 所做的那样轻松抽象,因此我们可以将其应用于任何类型的结果集

        function extract(name) {
          return function(item) {
            return item[name];
          }
        }
        
        const shoppingCart = [
          {name: "item1", price: 1},
          {name: "item2", price: 2},
          {name: "item3", price: 3},
          {name: "item4", price: 4},
          {name: "item5", price: 5},
          {name: "item6", price: 6},
        ];
        
        const people = [
          {name: "Alice",  drives: "Audi"},
          {name: "Bob",    drives: "BMW"},
          {name: "Carol",  drives: "Citroen"},
          {name: "David",  drives: "Dodge"},
          {name: "Esther", drives: "EDAG"},
          {name: "Fred",   drives: "Ford"},
        ];
        
        const prices = shoppingCart.map(extract('price'));
        console.log(prices);
        
        const names = people.map(extract('name'));
        const cars = people.map(extract('drives'));
        console.log(names);
        console.log(cars);

        即使是 add 的一个非常微不足道的例子,我们也可以走得很远。 add 的特殊编写方式称为 currying - 该函数不是一次采用 X 个参数,而是返回 另一个 函数,该函数采用 X - 1 直到满足为止。

        与其他函数一起使用的函数 - 生成它们(如 add 所做的那样)、使用它们或两者都称为 higher-order functions。如果我们始终使用这些并进行柯里化,我们就会进入functional programming 的领域。编写完整的函数式风格并不是从中受益的必要条件 - 正如我们在 add 中看到的那样,即使我们正在应用业务规则,您也可以编写一个简单的应用程序而无需使用大量代码。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-30
          相关资源
          最近更新 更多