【问题标题】:How and why does 'a'['toUpperCase']() in JavaScript work?JavaScript 中的 'a'['toUpperCase']() 如何以及为什么工作?
【发布时间】:2013-03-17 14:10:34
【问题描述】:

JavaScript 一直让我感到惊讶,这是另一个例子。我刚刚遇到了一些我一开始不理解的代码。于是我调试了一下,得出了这个发现:

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

现在如果toUpperCase() 被定义为字符串类型的成员,这一定很明显,但最初对我来说没有意义。

不管怎样,

  • 因为toUpperCase 是“a”的成员,这是否有效?还是在幕后发生了其他事情?
  • 我正在阅读的code具有如下功能:

    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
    

    ANY 对象上调用 ANY 方法是一种通用函数。但这是否意味着指定的方法已经是指定对象的隐式成员?

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

【问题讨论】:

标签: javascript function


【解决方案1】:

分解它。

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

所以

'a'['toUpperCase'];

是通过 括号符号 对属性 toUpperCase 的访问,来自 String.prototype。由于这个属性引用了一个方法,我们可以通过附加()

来调用它
'a'['toUpperCase']();

【讨论】:

  • 这将是一个有趣的面试问题
【解决方案2】:

foo.barfoo['bar'] 是相等的,所以你发布的代码是一样的

alert('a'.toUpperCase())

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


我们来看看callMethod

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

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

【讨论】:

    【解决方案3】:

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

    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
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      所以本质上,您是在引用/调用(注意 '()')'a' 对象(它是字符串类型,在这种情况下)。

      这是我脑海中的一些代码:

      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']();
      

      【讨论】:

        【解决方案5】:

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

        请参阅来自 MDN 的 Working with Objects

        toUpperCase 方法附加到 String 类型。当你在原始值上调用函数时,这里是'a',它会自动提升为一个对象,这里是String

        在要在原始字符串或原始字符串上调用方法的上下文中 发生属性查找时,JavaScript 会自动换行字符串 原语并调用方法或执行属性查找。

        通过记录String.prototype.toUpperCase可以看到函数存在。

        【讨论】:

          【解决方案6】:

          所以在 Javascript 中,objectsobjects。那就是它们具有这种性质{}。可以使用以下任一设置对象属性:a.greeting = 'hello';a['greeting'] = 'hello';。两种方式都有效。

          检索的工作原理相同。 a.greeting(不带引号)是'hello'a['greeting']'hello'。例外:如果属性是数字,则只有括号方法有效。点法没有。

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

          但 imo 编写地图函数的更好方法是

          var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )

          那么谁需要callMethod 函数呢?

          【讨论】:

          • 解释得很漂亮:-)
          【解决方案7】:

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

          调用它
          var str = "a"
          str['toUpperCase'](). // you get the method by its name as a key and invoke it.
          

          所以,通过内联 str,你可以有下面

          "a"["toUpperCase"]()
          

          【讨论】:

            【解决方案8】:

            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' 属性,然后调用它 ()。

            【讨论】:

              【解决方案9】:

              但这是否意味着指定的方法已经是指定对象的隐式成员?

              没有。有人可以传入一个对象,

              1. 没有名为toUpperCase的属性;或
              2. 有一个名为 toUpperCase 的属性不是函数

              在第一种情况下,会抛出错误,因为访问不存在的属性会返回undefined,我们不能将undefined 作为函数调用。

              在第二种情况下,将抛出错误,因为我们不能将非函数调用为函数。

              请记住,JavaScript 是一种类型非常松散的语言。除非必须这样做,否则很少或根本不会进行类型检查。您展示的代码在某些情况下有效,因为在这些情况下,传递的对象有一个名为 toUpperCase 的属性,它是一个函数。

              obj 参数不能保证具有正确的属性类型这一事实根本不会困扰 JavaScript,可以这么说。它采取“观望”的态度,直到运行时出现实际问题才抛出错误。

              【讨论】:

                【解决方案10】:

                javascript 中几乎所有的东西都可以被视为一个对象。在您的情况下,字母表本身充当字符串对象,toUpperCase 可以作为其方法调用。方括号只是访问对象属性的替代方式,因为toUpperCase 是一种方法,因此在['toUpperCase'] 旁边需要简单的括号() 形成['toUpperCase']()

                'a'['toUpperCase']() 等价于'a'.toUpperCase()

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

                【讨论】:

                  【解决方案11】:

                  这里要注意的重要一点是,由于 Javascript 是一种dynamic 语言,因此每个对象本质上都只是一个美化的 hash-map (with a few exceptions)。 Javascript 对象中的所有内容都可以通过两种方式访问​​ - 括号表示法和点表示法。

                  我将快速复习回答您问题第一部分的两个符号,然后我将进入第二部分。

                  括号表示法

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

                  这正是您在示例中所做的。你有'a',它是一个字符串(并且不是一个字符文字,就像在 C++ 等语言中一样)。

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

                  alert('a'.toUpperCase());
                  

                  这更具可读性。

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

                  alert(alert)
                  

                  这会调用alert,方法是向它传递一个函数对象,也就是alert,而不执行第二个警报。显示的内容(至少在 Chrome 26 中)如下:

                  function alert() { [native code] } 
                  

                  调用:

                  alert(alert())
                  

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

                  Try out all the cases on jsFiddle!

                  点符号

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

                  alert('a'.toUpperCase())
                  

                  更具可读性。那么什么时候应该用点表示法,什么时候应该用括号表示法呢?

                  比较

                  这两种方法的主要区别在于语义。还有一些其他细节,但我会在稍后讨论。最重要的是您真正想要做什么 - 经验法则是您使用 dot notation 来表示对象具有的完善字段和方法,以及 bracket-notation 当你实际使用你的对象作为哈希映射时。

                  这个规则如此重要的一个很好的例子可以在您的示例中显示 - 因为代码在点表示法更合理的地方使用括号表示法,它使代码更难阅读。这是一件坏事,because code is read many more times than it is written

                  在某些情况下,您必须使用 括号表示法,即使使用 点表示法 更合理:

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

                  • 如果你需要动态访问一个对象的成员,你也会被卡住使用括号表示法;

                  例子:

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

                  最重要的是,在将对象用作哈希映射 (foods["burger"].eat()) 时应使用括号语法,在使用“实际”字段和方法时应使用点语法 (enemy.kill())。由于 Javascript 是一种动态语言,对象的“实际”字段和方法与存储在其中的“其他”数据之间的界限会变得非常模糊。但只要你不以令人困惑的方式混合它们,你应该没问题。


                  现在,你的问题的其余部分(终于!:P)。

                  我如何确定方法永远是 obj 的成员

                  你不能。试试吧。尝试在字符串上调用derp。您将在以下行中收到错误:

                  Uncaught TypeError: Object a has no method 'derp' 
                  

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

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

                  function makeCap()
                  {
                      return function(obj) {
                          return obj.toUpperCase();
                      }
                  }
                  
                  var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C'] 
                  console.log(caps2)
                  

                  您链接到的教程中的代码使用partial functions。它们本身就是一个棘手的概念。阅读更多关于该主题的内容应该有助于使事情变得比我所能做的更清楚。


                  注意:这是问题中代码source here使用的map函数的代码。

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

                  【讨论】:

                    【解决方案12】:

                    如果你问它实际上是如何工作的,我就是这么读的。好的,这是一个简单的数学函数。要理解它,您需要查看 ascii 表。它为每个字母分配一个数值。为了掩饰它,竞争者只需使用逻辑语句来掩饰,例如 if(ChcrValue > 80 && charValue

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

                    【讨论】:

                    • 这与问题有什么关系?
                    • @glh,他问'a'['toUpperCase']() 如何以及为什么工作。但如果有人没有读过这个问题,这种误解是可以理解的。
                    • gr8 答案,由于没有添加样式text-transform: uppercase,我对 JS 如何设法改变情况、消除疑问并学到一两件事感到束手无策。非常感谢。
                    猜你喜欢
                    • 2019-01-17
                    • 2011-03-20
                    • 1970-01-01
                    • 2013-12-19
                    • 1970-01-01
                    • 2018-04-10
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-06-26
                    相关资源
                    最近更新 更多