JavaScript中的'a'['toUpperCase']()如何以及为什么工作?


209

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

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

现在,如果toUpperCase()将其定义为字符串类型的成员,这一点必须很明显,但最初对我而言没有任何意义。

无论如何,

  • toUpperCase是因为它是'a'的成员吗?还是幕后发生了其他事情?
  • 我正在阅读的代码具有以下功能:

    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函数的基本概念缺少一些认真的了解。请帮助我理解这一点。


24
有两种方法可以访问对象的属性:点表示法和方括号表示法。稍微相关:stackoverflow.com/a/11922384/218196。您已经了解方括号表示法,因为在访问数组元素时总是使用它arr[5]。如果数字是有效标识符的名称,则可以使用点符号:arr.5
Felix Kling 2013年

2
与相同5['toString']()
0x499602D2 2013年


相关阅读:1)继承和原型链:developer.mozilla.org/en-US/docs/JavaScript/Guide/…2)JavaScript原语的秘密生活:javascriptweblog.wordpress.com/2010/09/27/…
Theraot

21
初读时我以为标题是“ JavaScript如何以及为什么起作用?” 呃,好吧。
2013年

Answers:


293

分解它。

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

所以

'a'['toUpperCase'];

是对来自属性的括号表示法的访问。由于此属性引用了一个方法,因此我们可以通过附加该方法来调用它toUpperCaseString.prototype()

'a'['toUpperCase']();

这将是一个有趣的面试问题
FluffyBeing

78

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

alert('a'.toUpperCase())

使用foo[bar](请注意,如果没有引号),则不要使用文字名称,bar而是使用变量bar包含的任何值。因此,使用foo[]符号代替foo.允许您使用动态属性名称。


让我们来看看callMethod

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

如果toUpperCase该方法来自String.prototype.toUpperCase-对于每个存在的单个字符串都单独拥有该方法的副本,那将是非常愚蠢的。


25

您可以使用.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
    }
}

17

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

因此,从本质上讲,您是在引用/调用(请注意' () ”)“ a”对象(在这种情况下为字符串类型)的“ toUpperCase”属性。

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

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

10

anyObject['anyPropertyName']anyObject.anyPropertyName什么时候一样anyPropertyName没有问题的字符。

请参见使用对象 MDN中的。

toUpperCase方法附加到String类型。当您在原始值(此处)上调用函数时,该函数'a'会自动提升为对象(此处是String)

在要在原始字符串上调用方法或发生属性查找的情况下,JavaScript将自动包装字符串原始并调用该方法或执行属性查找。

您可以通过登录看到该功能存在String.prototype.toUpperCase


9

因此在Javascript中,objectsobjects。那是他们的本性{}。可以使用以下任意一项来设置对象属性:a.greeting = 'hello';a['greeting'] = 'hello';。两种方式都可以。

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

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

但是,imo编写map函数的更好方法是:

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

谁需要callMethod函数?


精美的讲解:-)
Elisha Senoo

6

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

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

因此,通过内联str,您可以在下面

"a"["toUpperCase"]()


4

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

否。有人可以传递一个物体

  1. 没有名为的属性toUpperCase; 要么
  2. 具有一个名为toUpperCase非函数的属性

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

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

请记住,JavaScript是一种非常宽松的语言。除非有必要,否则几乎不会进行类型检查。您显示的代码在某些情况下有效,因为在这种情况下,传递的对象具有名为toUpperCase,即函数。

可以这么说,obj不能保证参数具有正确类型的属性这一事实根本不会打扰JavaScript。它采用“等待和观察”的态度,并且在运行时发生实际问题之前不会抛出错误。


4

javascript中的几乎所有内容都可以视为对象。在您的情况下,字母本身充当字符串对象,并且toUpperCase可以作为其方法调用。方括号是访问对象的属性的只是替代方式和因为toUpperCase是因此simplebracket的方法()接下来需要['toUpperCase']形成['toUpperCase']()

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

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

3

这里要注意的重要一点是,由于Javascript是一种动态语言,因此每个对象从本质上来说只是一个美化的哈希表有一些例外)。Javascript对象中的所有内容都可以通过两种方式访问​​-括号表示法和点表示法。

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

括号符号

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

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

使用方括号符号,可以访问其toUpperCase方法。但是访问它仍然是不够的。alert例如,仅在Java语言中键入,就不会调用该方法。这只是一个简单的声明。为了调用该函数,您需要添加括号:alert()显示一个包含的简单对话框undefined,因为该对话框未接收任何参数。现在,我们可以使用此知识来解密您的代码,该代码将变为:

alert('a'.toUpperCase());

更具可读性。

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

alert(alert)

alert通过向其传递一个函数对象(也称为)来调用alert,而不执行第二个警报。显示的内容(至少在Chrome 26中为):

function alert() { [native code] } 

致电:

alert(alert())

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

在jsFiddle上尝试所有案例!

点表示法

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

alert('a'.toUpperCase())

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

比较方式

两种方法之间的主要区别是语义。还有其他一些细节,我将在稍后讨论。最重要的是您实际想要做的事情-一条经验法则是,将点表示法用于对象具有的完善字段和方法,以及将括号实际用作对象作为哈希图时的括号表示法

在您的示例中可以看到一个很好的例子说明了为什么此规则如此重要的原因-由于代码在点符号会更敏感的地方使用括号符号,因此使代码更难阅读。这是一件坏事,因为代码被读取的次数比编写的次数多

在某些情况下,即使使用点符号更明智,也必须使用括号符号

  • 如果对象的成员的名称包含一个或多个空格或任何其他特殊字符,则不能使用点表示法: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' 

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

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

function makeCap()
{
    return function(obj) {
        return obj.toUpperCase();
    }
}

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

您链接到的教程中的代码使用部分函数。它们本身就是一个棘手的概念。阅读更多有关该主题的内容应该可以使事情比我以前所知道的更清晰。


注意:这是问题代码所使用的map函数的代码,请参见此处

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

2

如果您问它实际上是如何工作的,那是我的阅读方式。好的,这是一个简单的数学函数。要了解它,您需要查看ascii表。为每个字母分配一个数值。为了隐蔽,竞争者仅使用逻辑语句进行隐秘,因此例如If(ChcrValue> 80 && charValue <106)//这是小写字母的集合,然后charValue = charValue-38; //下组与上组之间的距离

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


这与问题有什么关系?
Quasdunk

2
@glh,他问'a'['toUpperCase']()工作方式和原因。但是,如果有人不读问题,这种误解是可以理解的。
LarsH 2013年

gr8回答,由于未text-transform: uppercase添加任何样式,因此我对JS如何更改大小写,清除疑惑并学到一两个东西不屑一顾。非常感谢。
dkjain
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.