关于javascript:这个代码做了什么?

What does this Code do?

我正在阅读jQuery的"插件/创作",虽然我已经写了一些jQuery-Plugins。 现在我看到jQuery有一种特殊的方法来确定方法并调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    

  };

})( jQuery );

我理解最终会发生什么的概念......但究竟是怎么回事? 这部分让我感到困惑:

1
2
3
4
5
6
    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    }

为什么Array.prototype.slide.call(argumetns, 1)? 变量"参数"从何而来? 任何简短或更深入的解释都非常感谢。 据说,这就是插件的编写方式......所以我想知道原因。

谢谢!


arguments

arguments是JavaScript语言的一部分。当我第一次碰到它的时候,我很困惑。这不仅仅是你。 :-)它是每个函数中的自动局部变量,并且是一个类似于数组的结构,为您提供所有参数(参见规范的第10.6节),例如:

1
2
3
4
5
6
7
8
function foo() {
    var index;

    for (index = 0; index < arguments.length; ++index) {
        alert(arguments[index]);
    }
}
foo("one","two"); // alerts"one", then alerts"two"

当我说arguments是阵列式时,我的意思是&mbsp;&mdash;它不是一个数组。它对参数的引用是实时的(和双向的)。例如:

1
2
3
4
5
6
7
8
function foo(namedArg, anotherNamedArg) {
    alert(namedArg === arguments[0]);        // alerts true, of course
    alert(anotherNamedArg === arguments[1]); // also alerts true
    namedArg ="foo";
    alert(arguments[0]);                     // alerts"foo"
    arguments[0] ="bar";
    alert(namedArg);                         // alerts"bar"
}

请注意,将值分配给namedArg时,结果将反映在arguments[0]中,反之亦然。

arguments非常酷,但只有在需要时才使用它&mdash;一些实现通过不挂钩来加速调用函数,直到/除非函数实际上首先尝试访问它,这可以减慢函数(非常轻微)。

arguments上还有一个名为callee的属性,它是对函数本身的引用:

1
2
3
function foo() {
    alert(foo === arguments.callee); // alerts true
}

但是,出于几个原因,最好避免使用arguments.callee。一个原因是在许多实现中,它确实很慢(我不知道为什么,但是为了给你一个想法,如果使用arguments.callee,函数调用开销可以增加一个数量级)。另一个原因是你无法在ECMAScript5的新"严格"模式下使用它。

(有些实现也有arguments.caller&nbsp;&mdash; shudder&nbsp;&mdash;但幸运的是它从未普及过,并且在任何地方都没有标准化[也可能不是]。)

slice调用和apply

关于

1
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));

正在做的是使用Array#slice方法将参数复制到数组中(减去第一个参数,即调用的方法),然后将结果数组传递给它调用的函数实例上的Function#apply函数。 Function#apply使用给定的this对象调用函数实例,并将参数作为数组提供。代码不只是使用arguments.slice,因为(再次)arguments实际上并不是一个数组,因此您不能依赖它具有所有数组函数,但规范明确指出(在第15.4.4.10节中)你可以将Array.prototype.slice函数应用于类似数组的任何东西,这就是他们正在做的事情。

Function#applyFunction#call也是JavaScript的内置部分(参见第15.3.4.3节和第15.3.4.4节)。以下是每个简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// A function to test with
function foo(msg, suffix) {
    alert(this.prefix +":" + msg + suffix);
}

// Calling the function without any `this` value will default `this`
// to the global object (`window` on web browsers)
foo("Hi there","!"); // Probably alerts"undefined: Hi there!" because the
                      // global object probably doesn't have a `prefix` property

// An object to use as `this`
var obj = {
    prefix:"Test"
};

// Calling `foo` with `this` = `obj`, using `call` which accepts the arguments
// to give `foo` as discrete arguments to `call`
foo.call(obj,"Hi there","!"); // alerts"Test: Hi there!"
      // ^----^-----------^---- Three discrete args, the first is for `this`,
      //                        the rest are the args to give `foo`

// Calling `foo` with `this` = `obj`, using `apply` which accepts the arguments
// to give `foo` as an array
foo.apply(obj, ["Hi there","!"]); // alerts"Test: Hi there!"
            // ^---------------^---- Note that these are in an array, `apply`
            //                       takes exactly two args (`this` and the
            //                       args array to use)


arguments是一个关键字,传递给函数的参数。但是你不想要所有这些,因为你知道的第一个,它是method,所以这是将每个参数超过第一个用于.apply() ...将这些参数传递给第一个指定的方法method论点。

如果它找不到method(意味着第一个参数不是'init''show''hide''update',那么它将转到else部分,并将所有参数传递给init方法(默认情况下,如果你愿意)。

例如:

  • .tooltip({ thing: value })会调用init({ thing: value }),因为这是默认值
  • .tooltip('show', var1, var2)会调用show(var1, var2)


arguments是一个特殊的JavaScript变量,表示"赋予此函数的所有参数"。它不是一个数组,但行为几乎像一个,所以不是说arguments.slice(1)它调用Array.prototype.slice.call(arguments, 1);除了列表中的第一个值之外它正在做什么 - 对于func(1, 2, 3, 4)它将是[2, 3, 4]

最终结果是,当你调用$.fn.tooltip('foo', 'bar', 'baz')时,它将尝试调用methods['foo']('bar', 'baz')


Array.prototype.slide.call(argumetns, 1)arguments伪数组转换为实数数组(跳过第一个元素)。

arguments包含您当前所使用的函数的所有参数。它由JavaScript自动提供。由于我们需要一个真正的数组(arguments不能被操作等),我们使用这个语句将它转换为一个。


变量arguments是javascript变量的定义。它存储在函数中调用的所有参数。例如 :

1
2
3
4
5
function variablesCount() {
    alert(arguments.length);
}

variablesCount(var1, var2); // alerts 2