关于功能:JavaScript中’a'[‘toUpperCase’]()的工作方式和原因是什么?

How and why does 'a'['toUpperCase']() in JavaScript work?

javascript让我惊讶,这是另一个例子。我刚遇到一些我一开始不理解的代码。所以我调试了它并得出了这个结论:

1
alert('a'['toUpperCase']());  //alerts 'A'

现在,如果toUpperCase()被定义为字符串类型的成员,这一定是显而易见的,但它最初对我没有意义。

不管怎样,

  • 这是因为toUpperCase是"a"的成员吗?或者在幕后还有别的事情发生?
  • 我正在阅读的代码具有以下功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function callMethod(method) {
        return function (obj) {
            return obj[method](); //**how can I be sure method will always be a member of obj**
        }
    }

    var caps2 = map(['a', 'b', 'c'], callMethod('toUpperCase')); // ['A','B','C']
    // ignoring details of map() function which essentially calls methods on every
    // element of the array and forms another array of result and returns it

    在任何对象上调用任何方法都是一个通用函数。但这是否意味着指定的方法将已经是指定对象的隐式成员?

我确信我对JavaScript函数的基本概念缺乏一些认真的理解。请帮助我理解这一点。


把它分解。

  • .toUpperCase()String.prototype的一种方法。
  • 'a'是一个原始值,但会转换为它的对象表示形式。
  • 我们有两个可能的符号来访问对象属性/方法,点和括号符号。

所以

1
'a'['toUpperCase'];

是从String.prototype通过括号在属性toUpperCase上的访问。因为这个属性引用了一个方法,所以我们可以通过附加()来调用它。

1
'a'['toUpperCase']();


foo.barfoo['bar']相等,因此您发布的代码与

1
alert('a'.toUpperCase())

当使用foo[bar]时(注意没有引号),您不使用文字名称bar,而是使用变量bar包含的任何值。因此,使用foo[]表示法而不是foo.可以使用动态属性名。

让我们来看看callMethod

首先,它返回一个以obj为参数的函数。当执行该函数时,它将在该对象上调用method。因此,给定的方法只需要存在于obj本身或其原型链的某个地方。

toUpperCase的情况下,该方法来自String.prototype.toUpperCase——对于存在的每一个字符串都有一个单独的方法副本是相当愚蠢的。


您可以使用.propertyName符号或["propertyName"]符号访问任何对象的成员。这就是JavaScript语言的特点。要确保成员在对象中,只需检查是否已定义:

1
2
3
4
5
6
function callMethod(method) {
    return function (obj) {
        if (typeof(obj[method]) == 'function') //in that case, check if it is a function
           return obj[method](); //and then invoke it
    }
}

基本上,JavaScript将所有内容都视为一个对象,或者更确切地说,每个对象都可以被视为一个字典/关联数组。对于对象,函数/方法的定义方式与此关联数组中的条目完全相同。

因此,本质上,您正在引用/调用(注意"()")A对象的"ToUpperCase"属性(在本例中是字符串类型)。

下面是我头顶上的一些代码:

1
2
3
4
5
6
7
8
9
10
11
function myObject(){
    this.msg ="hey there! ;-)";
    this.woop = function(){
        alert(this.msg); //do whatever with member data
    }
}

var obj = new myObject();
alert( obj.msg );
alert( obj['msg'] );
obj['woop']();

anyPropertyName没有问题字符时,anyObject['anyPropertyName']anyObject.anyPropertyName相同。

请参见使用MDN中的对象。

toUpperCase方法附加到类型字符串。当您对基元值(此处为'a')调用函数时,它将自动升级为对象,此处为字符串:

In contexts where a method is to be invoked on a primitive string or a
property lookup occurs, JavaScript will automatically wrap the string
primitive and call the method or perform the property lookup.

您可以通过记录String.prototype.toUpperCase来查看函数是否存在。


所以在javascript中,objectsobjects。这就是它们的性质。对象属性可以使用以下任何一种设置:a.greeting = 'hello';a['greeting'] = 'hello';。这两种方式都起作用。

检索的工作原理是一样的。a.greeting(不带引号)为'hello'a['greeting']'hello'。异常:如果属性是数字,则只有括号方法有效。dot方法没有。

因此,'a'是一个具有'toUpperCase'属性的对象,它实际上是一个函数。您可以检索该函数并随后以任何方式调用它:'a'.toUpperCase()'a'['toUpperCase']()

但在我看来,编写map函数的更好方法是var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )那么谁需要callMethod功能呢?


touppercase是标准的javascript方法:https://developer.mozilla.org/en-us/docs/javascript/reference/global_objects/string/touppercase

它与'a'['toUpperCase']()类似的原因是touppercase函数是字符串对象'a'的属性。可以使用object[property]object.property引用对象的属性。语法"a""touppercase"表示您正在引用"a"字符串对象的"touppercase"属性,然后调用它()。


每个javascript对象都是一个哈希表,因此您可以通过指定一个键来访问其成员。例如,如果变量是字符串,那么它应该具有touppercase函数。所以,你可以通过

4

所以,通过inline str,您可以在下面

1
"a"["toUpperCase"]()

几乎JavaScript中的所有内容都可以作为一个对象来处理。在您的示例中,字母表本身充当字符串对象,并且可以调用toUpperCase作为其方法。方括号只是访问对象属性的另一种方法,由于toUpperCase是一种方法,因此在['toUpperCase']形成['toUpperCase']()的旁边需要单方括号()

'a'['toUpperCase']()相当于'a'.toUpperCase()

1
2
'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A

But does that mean the specified method will already be an implicit member of the specified object?

不,有人可以通过一个物体

  • 没有名为toUpperCase的财产;或
  • 具有名为toUpperCase的属性,该属性不是函数
  • 在第一种情况下,会引发一个错误,因为访问不存在的属性会返回undefined,并且我们不能作为函数调用undefined

    在第二种情况下,将引发一个错误,因为同样,我们不能将非函数作为函数调用。

    记住,javascript是一种非常松散的类型语言。在必须进行类型检查之前,很少或没有进行类型检查。您显示的代码在某些情况下有效,因为在这些情况下,传递的对象有一个名为toUpperCase的属性,这是一个函数。

    可以说,obj参数不能保证具有正确的属性类型,这一事实根本不影响javascript。它采用"观望"的态度,在运行时出现实际问题之前不会抛出错误。


    这里需要注意的重要一点是,由于JavaScript是一种动态语言,所以每个对象本质上都只是一个美化的哈希图(除了少数例外)。javascript对象中的所有内容都可以通过两种方式访问——括号表示法和点表示法。好的。

    我会很快看一下回答你问题第一部分的两个符号,然后再看第二部分。好的。括号符号

    此模式更类似于使用其他编程语言访问哈希图和数组。您可以使用此语法访问任何组件(数据(包括其他对象)或函数)。好的。

    这正是您在示例中所做的。您有EDCOX1×0,它是一个字符串(而不是字符文字,就像它在一个语言中,比如C++)。好的。

    使用括号表示法,可以访问它的toUpperCase方法。但是访问它仍然是不够的;例如,在javascript中简单地键入alert并不能调用该方法。这只是一个简单的陈述。为了调用函数,需要添加括号:alert()显示了一个包含undefined的简单对话框,因为它没有收到参数。我们现在可以使用这些知识来破译您的代码,这将成为:好的。

    1
    alert('a'.toUpperCase());

    可读性更高。好的。

    实际上,更好地理解这一点的一个好方法是执行以下JavaScript:好的。

    1
    alert(alert)

    它通过传递一个函数对象(也叫alert)来调用alert,而不同时执行第二个警报。显示内容(至少在Chrome26中)如下:好的。

    1
    function alert() { [native code] }

    打电话:好的。

    1
    alert(alert())

    显示包含undefined的两个连续消息框。这很容易解释:首先执行内部alert(),显示undefined(因为它没有任何参数),什么也不返回。外部警报接收内部警报的返回值,该值为零,并在消息框中显示undefined。好的。

    在jsiddle上尝试所有案例!好的。点表示法

    这是一种更标准的方法,它允许使用dot(.操作符)访问对象的成员。这就是您的代码在点符号中的样子:好的。4

    可读性更高。那么我们什么时候应该使用点符号,什么时候应该使用括号符号?好的。比较

    这两种方法的主要区别在于语义。还有一些其他的细节,不过我马上就来。最重要的是你实际想要做的事情——经验法则是,你对一个对象拥有的已经建立好的字段和方法使用点表示法,当你实际使用你的对象作为散列图时使用括号表示法。好的。

    这个规则为什么如此重要的一个很好的例子可以在您的示例中显示出来——因为代码在一个点表示法更明智的地方使用了括号表示法,这使得代码更难阅读。这是一件坏事,因为代码被读取的次数比被写入的次数要多。好的。

    在某些情况下,即使使用点表示法更明智,也必须使用括号表示法:好的。

    • 如果对象成员的名称包含一个或多个空格或任何其他特殊字符,则不能使用点表示法:foo.some method()不起作用,但foo["some method"]()起作用;好的。

    • 如果需要动态访问对象的成员,还需要使用括号表示法;好的。

    例子:好的。

    1
    2
    3
    for(var i = 0; i < 10; ++i) {
       foo["method" + i]();
     }

    底线是,当使用对象作为散列映射(foods["burger"].eat()时,应使用括号语法;当使用"实际"字段和方法(enemy.kill()时,应使用点语法)。由于JavaScript是一种动态语言,对象的"实际"字段和方法与存储在其中的"其他"数据之间的界限可能会变得非常模糊。但只要你不把它们混在一起,你就应该没事了。好的。

    现在,继续剩下的问题(最后!P)。好的。

    how can I be sure method will always be a member of obj

    Ok.

    你不能。试试看。尝试在字符串上调用derp。您将在以下行中得到一个错误:好的。

    1
    Uncaught TypeError: Object a has no method 'derp'

    It is kinda generic function to call ANY methods on ANY object. But
    does that mean the specified method will already be an implicit member
    of the specified object?

    Ok.

    是的,在你的情况下,这是必须的。否则,你会得到我上面提到的错误。但是,在callMethod()函数中不必使用return obj[method]();。您可以添加自己的功能,然后由map函数使用。下面是一个硬编码方法,可以将所有字母转换为大写字母:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function makeCap()
    {
        return function(obj) {
            return obj.toUpperCase();
        }
    }

    var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C']
    console.log(caps2)

    链接到的教程中的代码使用部分函数。它们本身就是一个棘手的概念。多读一些关于那个主题的书应该有助于使事情比我所能做的更清楚。好的。

    注意:这是问题代码使用的map函数的代码,这里是源代码。好的。

    1
    2
    3
    4
    5
    function map(arr, iterator) {
      var narr = [];
      for (var i = 0; i < arr.length; i++) narr.push(iterator(arr[i], i));
      return narr;
    }

    好啊。


    如果你问它实际上是如何工作的,那就是我如何阅读它。这是一个简单的数学函数。要理解它,您需要查看ASCII表。为每个字母指定一个数值。为了隐藏它,竞争只是使用一个逻辑语句来隐藏,例如if(chcrvalue>80&;charvalue<106)//这是一组小写字母那么charvalue=charvalue-38;//下集和上集之间的距离

    这很简单,实际上我没有费心查找正确的值,但是这基本上是将所有小写字母都转换为大写值。