简体   繁体   English

Javascript函数声明选项

[英]Javascript Function Declaration Options

I've seen experts using below to declare a function: 我见过使用下面的专家来声明一个函数:

(function () {
  function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }
  //etc

}());

eg https://github.com/douglascrockford/JSON-js/blob/master/json.js 例如https://github.com/douglascrockford/JSON-js/blob/master/json.js

Could someone help me understand when should we use above pattern and how do we make use of it? 有人可以帮助我理解我们何时应该使用上述模式以及我们如何使用它?

Thanks. 谢谢。

Well, since ECMA6 hasn't arrived yet, functions are about the best way to create scopes in JS. 好吧,既然ECMA6还没有到来,那么函数就是在JS中创建范围的最佳方式。 If you wrap a variable declaration of sorts in an IIFE (Immediately Invoked Function Expression), that variable will not be created globally. 如果在IIFE(立即调用的函数表达式)中包装排序的变量声明,则不会全局创建该变量。 Same goes for function declarations. 功能声明也是如此。
If you're given the seemingly daunting task of clearing a script of all global variables, all you need to do is wrap the entire script in a simple (function(){/*script here*/}()); 如果给你一个看似令人生畏的清除所有全局变量脚本的艰巨任务,你需要做的就是将整个脚本包装成一个简单的(function(){/*script here*/}()); , and no globals are created, lest they are implied globals, but that's just a lazy fix. ,并且没有创建全局变量,以免它们是隐含的全局变量,但这只是一个懒惰的修复。 This pattern is sooo much more powerful. 这种模式非常强大。

I have explained the use of IIFE in more detail both here , here and here 我已经在这里这里这里更详细地解释了IIFE的使用

The basic JS function call live-cycle sort of works like this: 基本的JS函数调用live-cycle类似的工作如下:

f();//call function
 ||
 ====> inside function, some vars are created, along with the arguments object
        These reside in an internal scope object
     ==> function returns, scope object (all vars and args) are GC'ed

Like all objects in JS, an object is flagged for GC (Garbage Collection) as soon as that object is not referenced anymore. 与JS中的所有对象一样,只要该对象不再被引用,就会为GC(垃圾收集)标记一个对象。 But consider the following: 但请考虑以下因素:

var foo = (function()
{
    var localFoo = {bar:undefined};
    return function(get, set)
    {
        if (set === undefined)
        {
            return localFoo[get];
        }
        return (localFoo[get] = set);
    }
}());

When the IIFE returns, foo is assigned its return value, which is another function. 当IIFE返回时,foo被赋予其返回值,这是另一个函数。 Now localFoo was declared in the scope of the IIFE, and there is no way to get to that object directly. 现在localFoolocalFoo的范围内被声明,并且没有办法直接到达该对象。 At first glance you might expect localFoo to be GC'ed. 乍一看,您可能希望localFoo成为localFoo
But hold on, the function that is being returned (and assigned to foo still references that object, so it can't be gc'ed. In other words: the scope object outlives the function call, and a closure is created. 但是,继续,返回的函数(并分配给foo仍然引用该对象,因此它不能被gc'ed。换句话说:scope对象比函数调用更长,并且创建了一个闭包。

The localFoo object, then, will not be GC'ed until the variable foo either goes out of scope or is reassigned another value and all references to the returned function are lost. 然后,在变量foo超出范围或重新分配另一个值并且返回的函数的所有引用都丢失之前, localFoo对象将不会被localFoo

Take a look at one of the linked answers (the one with the diagrams), In that answer there's a link to an article, from where I stole the images I used. 看看其中一个链接的答案(带有图表的答案),在那个答案中有一个文章的链接,从那里我偷了我用过的图像。 That should clear things up for you, if this hasn't already. 如果还没有,那应该为你清理。

An IIFE can return nothing, but expose its scope regardless: IIFE可以不返回任何内容,但无论如何都要暴露其范围:

var foo = {};
(function(obj)
{
    //obj references foo here
    var localFoo = {};
    obj.property = 'I am set in a different scope';
    obj.getLocal = function()
    {
        return localFoo;
    };
}(foo));

This IIFE returns nothing (implied undefined ), yet console.log(foo.getLocal()) will log the empty object literal. 此IIFE不返回任何内容(暗示undefined ),但console.log(foo.getLocal())将记录空对象文字。 foo itself will also be assigned property . foo本身也将被赋予property But wait, I can do you one better. 但是等等,我可以做得更好。 Assume foo has been passed through the code above once over: 假设foo已经通过上面的代码一次:

var bar = foo.getLocal();
bar.newProperty = 'I was added using the bar reference';
bar.getLocal = function()
{
    return this;
};
console.log(foo.getLocal().newProperty === bar.newProperty);
console.log(bar ==== foo.getLocal());
console.log(bar.getLocal() === foo.getLocal().getLocal());
//and so on

What will this log? 这个日志会是什么? Indeed, it'll log true time and time again. 事实上,它会记录true ,一次又一次。 Objects are never copied in JS, their references are copied, but the object is always the same. 对象永远不会在JS中复制,它们的引用会被复制,但对象始终是相同的。 Change it once in some scope, and those changes will be shared across all references (logically). 在某个范围内更改一次,这些更改将在所有引用中共享(逻辑上)。
This is just to show you that closures can be difficult to get your head round at first, but this also shows how powerful they can be: you can pass an object through various IIFE's, each time setting a new method that has access to its own, unique scope that other methdods can't get to. 这只是告诉你,封锁可能很难在第一次得到你的头一轮,但是这也说明他们强大功能:您可以通过各种IIFE的传递对象,每段时间设定可以访问自己的新方法,其他方法无法达到的独特范围。

Note 注意
Closers aren't all that easy for the JS engines to Garbage Collect, but lately, that's not that big of an issue anymore . 对于JS引擎而言,更接近Garbage Collect并不是那么容易,但最近,这不再是一个大问题了
Also take your time to google these terms: 还花些时间来谷歌这些条款:

IIFE's can be named functions, too, but then the only place where you can reference that function is inside that function's scope: IIFE也可以命名为函数,但是你可以引用该函数的唯一地方是函数的范围:

(function init (obj)
{
    //obj references foo here
    var localFoo = {};
    obj.property = 'I am set in a different scope';
    obj.getLocal = function()
    {
        return localFoo;
    };
    if (!this.wrap)
    {//only assign wrap if wrap/init wasn't called from a wrapped object (IE foo)
        obj.wrap = init;
    }
}(foo));
var fooLocal = foo.getLocal();
//assign all but factory methods to fooLocal:
foo.wrap(fooLocal);
console.log(fooLocal.getLocal());//circular reference, though
console.log(init);//undefined, the function name is not global, because it's an expression

This is just a basic example of how you can usre closures to create wrapper objects... 这只是一个基本的例子,说明如何使用闭包来创建包装器对象......

Well the above pattern is called the immediate function. 以上模式称为立即函数。 This function do 3 things:- 这个功能做3件事: -

The result of this code is an expression that does all of the following in a single statement: 此代码的结果是一个表达式,它在单个语句中执行以下所有操作:

  1. Creates a function instance 创建一个函数实例
  2. Executes the function 执行功能
  3. Discards the function (as there are no longer any references to it after the statement has ended) 丢弃该函数(因为在语句结束后不再有任何引用)

This is used by the JS developers for creating a variables and functions without polluting the global space as it creates it's own private scope for vars and functions. JS开发人员使用它来创建变量和函数,而不会污染全局空间,因为它为变量和函数创建了自己的私有范围。

In the above example the function f(){} is in the private scope of the immediate function, you can't invoke this function at global or window scope. 在上面的示例中,函数f(){}位于immediate函数的私有范围内,您无法在全局或窗口范围内调用此函数。

Browser-based JavaScript only has two scopes available: Global and Function. 基于浏览器的JavaScript只有两个可用范围:全局和函数。 This means that any variables you create are in the global scope or confined to the scope of the function that you are currently in. 这意味着您创建的任何变量都在全局范围内或仅限于您当前所在函数的范围。

Sometimes, often during initialization, you need a bunch of variables that you only need once. 有时,通常在初始化期间,您需要一堆只需要一次的变量。 Putting them in the global scope isn't appropriate bit you don't want a special function to do it. 将它们放在全局范围内是不合适的,你不需要特殊的功能来完成它。

Enter, the immediate function. 输入,即时功能。 This is a function that is defined and then immediately called. 这是一个定义然后立即调用的函数。 That's what you are seeing in Crockford's (and others') code. 这就是你在Crockford(和其他人的)代码中看到的。 It can be anonymous or named, without defeating the purpose of avoiding polluting the global scope because the name of the function will be local to the function body. 它可以是匿名的或命名的,但不会破坏避免污染全局范围的目的,因为函数的名称将是函数体的本地名称。

It provides a scope for containing your variables without leaving a function lying around. 它提供了一个包含变量的范围,而不会留下一个函数。 Keeps things clean. 保持清洁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM