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函数的基本概念缺乏认真的理解 . 请帮我理解这个 .
12 回答
这里需要注意的重要一点是,由于Javascript是一种dynamic语言,因此每个对象本质上只是一个美化的 hash-map (with a few exceptions) . 并且可以通过两种方式访问Javascript对象中的所有内容 - 括号表示法和点表示法 .
我将快速回顾你问题第一部分的两个符号,然后我将进入第二部分 .
括号表示法
此模式更类似于在其他编程语言中访问哈希映射和数组 . 您可以使用此语法访问任何组件(数据(包括其他对象)或函数) .
这正是您在示例中所做的 . 你有
'a'
,这是一个字符串( not 是一个字符文字,就像它用C语言一样) .使用括号表示法,可以访问其
toUpperCase
方法 . 但访问它仍然是不够的;例如,只需在Javascript中输入alert
,就不会只是一个简单的陈述 . 为了调用该函数,您需要添加括号:alert()
显示一个包含undefined
的简单对话框,因为它没有收到任何参数 . 我们现在可以使用这些知识来破译您的代码,这将成为:哪个更具可读性 .
实际上,更好地理解这一点的一个好方法是执行以下Javascript:
这通过传递一个函数对象
alert
来调用alert
,而不执行第二个警报 . 显示的内容(至少在Chrome 26中)如下:呼叫:
显示两个包含
undefined
的连续消息框 . 这很容易解释:内部alert()
先执行,显示undefined
(因为它没有任何参数)并且不返回任何内容 . 外部警报接收内部警报的返回值 - 这是什么都没有,并且还在消息框中显示undefined
.Try out all the cases on jsFiddle!
点符号
这是更标准的方法,它允许使用点(
.
)运算符访问对象的成员 . 这是您的代码在点表示法中的样子:更具可读性 . 那么我们何时应该使用点表示法,何时应该使用括号表示法?
比较
两种方法的主要区别在于语义 . 还有其他一些细节,但我最重要的是你真正想要做的事情 - 一个经验法则是你使用 dot notation 来 Build 一个对象所拥有的完善的字段和方法,并且当你实际使用_295559时使用您的对象作为哈希映射 .
这个规则如此重要的一个很好的例子可以在你的例子中显示 - 因为代码在点符号更加明智的地方使用括号表示法,这使得代码更难阅读 . 这是一件坏事,because code is read many more times than it is written .
在某些情况下,即使使用点符号更合理,也必须使用括号表示法:
如果对象的成员具有包含一个或多个空格或任何其他特殊字符的名称,则不能使用点表示法:
foo.some method()
不起作用,但foo["some method"]()
不起作用;如果你需要动态访问一个对象的成员,你也会使用括号表示法;
例:
最重要的是,在将对象用作哈希映射(
foods["burger"].eat()
)时使用括号语法,使用"actual"字段和方法(enemy.kill()
)时使用点语法 . 由于Javascript是一种动态语言,"actual"字段与对象方法之间的界限以及存储在其中的"other"数据可能会非常模糊 . 但只要你不以混乱的方式混合它们,你应该没事 .现在,问题的其余部分(最后!:P) .
你不能 . 试试吧 . 尝试在字符串上调用
derp
. 您将收到以下行中的错误:是的,在你的情况下,它必须是 . 否则你最终会得到错误I.上文提到的 . 但是,您不能 have 在
callMethod()
函数中使用return obj[method]();
. 您可以添加自己的功能,然后由 Map 功能使用 . 这是一个硬编码的方法,将所有字母转换为大写字母:您链接到的教程中的代码使用partial functions . 它们本身就是一个棘手的概念 . 阅读有关该主题的更多信息应该有助于使事情比我做得更清楚 .
注意:这是问题中代码source here使用的map函数的代码 .
您可以使用
.propertyName
表示法或["propertyName"]
表示法访问任何对象的成员 . 这是JavaScript语言的特性 . 要确保该成员在对象中,只需检查它是否已定义: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'属性,然后调用它() .打破它 .
.toUpperCase()
是String.prototype
的方法'a'
是原始值,但会转换为其Object表示形式我们有两种可能的符号来访问对象属性/方法,点和括号表示法
所以
是
toUpperCase
属性toUpperCase
上的括号表示法访问 . 由于此属性引用了一个方法,我们可以通过附加()
来调用它 .当
anyPropertyName
没有问题字符时,anyObject['anyPropertyName']
与anyObject.anyPropertyName
相同 .请参阅MDN中的Working with Objects .
toUpperCase
方法附加到String类型 . 当你在原始值上调用函数时,这里'a'
会自动提升为一个对象,这里是String:您可以通过记录
String.prototype.toUpperCase
来查看该功能 .如果您询问它是如何实际工作的,那就是我如何阅读它 . 好的,这是一个简单的数学函数 . 要理解它,您需要查看ascii表 . 其中为每个字母分配一个数值 . 为了转换它,竞争只是使用逻辑语句进行转换,例如If(ChcrValue> 80 && charValue <106)//这是一组小写字母,然后charValue = charValue - 38; //下部组和上部组之间的距离
就这么简单,我实际上并没有费心去查找正确的值,但这基本上是将所有小写字母转换为大写字母 .
没有 . 有人可以通过一个物体
没有名为
toUpperCase
的属性;要么有一个名为
toUpperCase
的属性,它不是一个函数在第一种情况下,将抛出错误,因为访问不存在的属性会返回
undefined
,并且我们无法将undefined
作为函数调用 .在第二种情况下,将抛出错误,因为我们不能再将非函数作为函数调用 .
请记住,JavaScript是一种非常松散的语言 . 很少或没有类型检查发生,除非并且直到它必须 . 您显示的代码在某些情况下有效,因为在这些情况下,传递的对象具有名为
toUpperCase
的属性,即属性 .事实上,
obj
论证根本不会麻烦JavaScript,可以这么说 . 它采用"wait and see"态度,并且在运行时发生实际问题之前不会抛出错误 .所以在Javascript中,
objects
是objects
. 那是他们的本性{}
. 可以使用以下任一方式设置对象属性: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
函数呢?每个JavaScript对象都是一个哈希表,因此您可以通过指定密钥来访问其成员 . 例如,如果变量是一个字符串,那么它应该具有toUpperCase函数 . 所以,你可以调用它
所以,通过内联str,你可以在下面
几乎javascript中的所有内容都可以视为一个对象 . 在您的情况下,字母表本身充当字符串对象,并且可以调用
toUpperCase
作为其方法 . 方括号只是访问对象属性的替代方法,因为toUpperCase
是一种方法,因此在['toUpperCase']
旁边需要simplebracket()
形成['toUpperCase']()
.'a'['toUpperCase']()
相当于'a'.toUpperCase()
foo.bar
和foo['bar']
相等,因此您发布的代码与之相同当使用
foo[bar]
(注意缺少引号)时,不要使用文字名bar
,而是使用变量bar
包含的任何值 . 因此,使用foo[]
表示法而不是foo.
允许您使用动态属性名称 .我们来看看
callMethod
:首先,它返回一个以
obj
为参数的函数 . 执行该函数时,它将在该对象上调用method
. 所以给定的方法只需要存在于obj
本身或其原型链的某个地方 .如果
toUpperCase
该方法来自String.prototype.toUpperCase
- 对于存在的每个字符串都有一个单独的方法副本是相当愚蠢的 .基本上,javascript将所有内容视为Object,或者更确切地说,每个对象都可以被视为字典/关联数组 . 并且函数/方法的定义方式与对象完全相同 - 作为此关联数组中的条目 .
基本上,你是一个'对象(在这种情况下是一个字符串类型) . 对象是一个对象(它是一个字符串类型) .
这是我头脑中的一些代码: