JavaScript中的'a'()是如何工作的?

JavaScript让我惊讶,这又是一个例子。 我刚刚遇到一些我一开始就不明白的代码。 所以我debugging了一下,发现了这个结果:

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

现在,如果toUpperCase()被定义为stringtypes的成员,那么这一点是显而易见的,但是最初对我来说没有任何意义。

无论如何,

  • 做这个工作是因为toUpperCase是'a'的成员? 或者幕后还有其他的事情呢?
  • 我正在阅读的代码有如下function:

     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方法都是一个通用的函数。 但这是否意味着指定的方法将已经是指定对象的隐式成员?

我相信我错过了对JavaScript函数基本概念的一些认真的理解。 请帮我理解这一点。

把它打破。

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

所以

 'a'['toUpperCase']; 

是通过来自String.prototype的属性toUpperCase 括号表示法进行访问。 由于这个属性引用了一个方法 ,我们可以通过attach ()

 'a'['toUpperCase'](); 

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

 alert('a'.toUpperCase()) 

当使用foo[bar] (注意缺less引号)时,不使用文字名称bar而是使用variablesbar包含的值。 所以使用foo[]符号而不是foo. 允许您使用dynamic属性名称。


我们来看看callMethod

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

toUpperCase情况下,该方法来自String.prototype.toUpperCase – 对于每个存在的单个string都有一个单独的方法副本是很愚蠢的。

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

基本上,javascript把所有东西看作一个对象,或者说每个对象都可以看作是一个字典/关联数组。 而函数/方法的定义与对象完全相同 – 就像这个关联数组中的一个入口一样。

所以本质上,你是引用/调用(注意' () ')'a'对象的'toUpperCase'属性(在这种情况下是stringtypes)。

这是我头顶的一些代码:

 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方法附加到Stringtypes。 当你在一个原始值上调用一个函数时,在这里'a'会自动提升到一个对象,这里是一个String :

在一个方法被调用的原始string或属性查找的上下文中,JavaScript将自动包装string原语并调用方法或执行属性查找。

你可以通过loggingString.prototype.toUpperCase来看到函数的存在。

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

检索工作是一样的。 a.greeting (不含引号)是'hello'a['greeting']'hello' 。 例外:如果该属性是一个数字,只有括号方法的作品。 点方法不。

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

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

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

谁需要callMethod函数呢?

每个JavaScript对象都是一个哈希表,因此您可以通过指定一个键来访问其成员。 例如,如果一个variables是一个string,那么它应该有toUpperCase函数。 所以,你可以通过调用它

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

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

 "a"["toUpperCase"]() 

toUpperCase是一个标准的JavaScript方法: https : //developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase

它的作用就像'a'['toUpperCase']()是toUpperCase函数是string对象'a'一个属性。 您可以使用object[property]object.property引用对象上的object[property] 。 语法'a'toUpperCase'表示你引用'a'string对象的'toUppercase'属性,然后调用它()。

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

没有人可以传递一个对象

  1. 没有名为toUpperCase的属性; 要么
  2. 有一个名为toUpperCase的属性,它不是一个函数

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

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

请记住,JavaScript是一种非常松散的语言。 很less或根本没有types检查,除非必要。 你显示的代码在某些情况下工作,因为在这种情况下,传递的对象有一个名为toUpperCase的属性,这是一个函数。

事实上, obj论点不能保证有正确的属性types,根本不打扰JavaScript。 它采取“观望”的态度,不会抛出错误,直到运行时出现实际问题。

几乎所有在JavaScript中可以被视为一个对象。 在你的情况下,字母表本身作为一个string对象,可以调用toUpperCase作为它的方法。 方括号只是访问对象属性的另一种方法,因为toUpperCase是一个方法,所以在['toUpperCase']成形['toUpperCase']()旁边需要simplebracket ['toUpperCase']()

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

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

这里要注意的重要一点是,由于Javascript是一种dynamic语言,每一个对象本质上都只是一个荣耀的散列图 ( 除了一些例外 )。 并且可以通过两种方式访问​​JavaScript对象中的所有内容 – 括号和点符号。

我会很快回过头来回答你的问题的第一部分,然后我会进入第二部分。

括号表示法

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

这正是你在你的例子中所做的。 你有'a' ,这是一个string(而不是一个字符文字,就像在一个语言,如C + +)。

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

 alert('a'.toUpperCase()); 

这是更可读性。

其实,理解这个更好一点的好方法是执行下面的Javascript:

 alert(alert) 

这通过传递一个函数对象来调用alert ,同时alert ,而不执行第二个警报。 显示的内容(至less在Chrome 26中)如下所示:

 function alert() { [native code] } 

呼叫:

 alert(alert()) 

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

试试jsFiddle的所有情况!

点符号

这是更标准的方法,它允许使用点( . )运算符访问对象的成员。 这是你的代码看起来像点符号:

 alert('a'.toUpperCase()) 

更可读。 那么我们何时应该使用点符号,什么时候应该使用括号符号?

对照

这两种方法的主要区别是语义。 还有一些其他的细节,但我会在一秒钟之内find。 最重要的是你真正想要做什么 – 一个经验法则是你使用点符号来build立一个对象所具有的良好的字段和方法,而当你实际使用你的对象作为哈希映射时, 括号表示法

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

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

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

  • 如果你需要dynamic地访问一个对象的成员,你也使用了括号表示;

例:

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

底线是,当使用对象作为哈希映射( foods["burger"].eat() )和使用“实际”字段和方法( enemy.kill() )时的点语法时,应该使用括号语法。 Javascript是一种dynamic语言,对象的“实际”字段和方法与存储在其中的“其他”数据之间的界限可能非常模糊。 但只要你不混淆他们,你应该没问题。


现在,到你的问题的其余部分(最后!:P)。

我怎么能确定方法将永远是obj的成员

你不能。 尝试一下。 尝试在一个string上调用derp 。 你会得到一个错误的行:

 Uncaught TypeError: Object a has no method 'derp' 

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

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

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

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


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

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

如果你问如何实际工作,我是如何阅读的。 好吧,这是一个简单的math函数。 要了解它,你需要看看ascii表。 它为每个字母分配一个数字值。 如果(ChcrValue> 80 && charValue <106)//这是一组小写字母,那么charValue = charValue – 38; //下方和上方之间的距离

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