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

JavaScript一直让我惊讶,这是另一个例子。我只是遇到了一些代码,一开始我不理解。所以我对它进行了调试,得到了这样的发现:

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

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

不管怎么说,

  • 这是因为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
    

    任何对象上调用任何方法是一个泛型函数。但这是否意味着指定的方法已经是指定对象的隐式成员?< / p >

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

15321 次浏览

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

alert('a'.toUpperCase())

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


让我们来看看callMethod:

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

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

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

参见MDN中的使用对象

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

在上下文中,方法将被调用在一个基本字符串或 属性查找时,JavaScript会自动换行字符串 原语并调用该方法或执行属性查找

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

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

把它分解。

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

所以

'a'['toUpperCase'];

是通过括号String.prototype对属性toUpperCase的访问。由于此属性引用方法,因此可以通过附加()来调用它

'a'['toUpperCase']();

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

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

通过inline str,你可以有下面

"a"["toUpperCase"]()

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把所有东西都当作一个对象,或者说每个对象都可以被看作一个字典/关联数组。函数/方法的定义方式与对象完全相同——作为这个关联数组中的一个条目。

所以本质上,你引用/调用(注意'< em > () < / em >') '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']();

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

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

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

但是在我看来,更好的方法是将map函数写成

var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } ) < br > < br > 那么谁需要callMethod函数呢?< / p >

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

不。有人可以传入一个对象

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

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

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

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

可以这么说,obj参数不能保证具有正确类型的属性这一事实根本不影响JavaScript。它采用“等待和观察”的态度,直到在运行时出现实际问题时才抛出错误。

javascript中几乎所有的东西都可以被当作对象。在你的例子中,字母本身作为一个字符串对象,toUpperCase可以作为它的方法被调用。方括号只是访问对象属性的另一种方式,由于toUpperCase是一个方法,因此需要在['toUpperCase']旁边使用简单括号(),形成['toUpperCase']()

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

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

如果你问它是如何工作的,这是我读它的方式。这是一个简单的数学函数。要理解它,您需要查看ascii表。给每个字母赋一个数值。要隐藏它,竞争对手只需使用一个逻辑语句来隐藏,例如 If(ChcrValue > 80 &&charValue & lt;106) //这是一组小写字母 那么charValue = charValue - 38;//下层集合和上层集合之间的距离

. /p>

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

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

我会快速地过一遍这两个符号来回答你问题的第一部分,然后我会讲第二部分。

括号

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

这正是您在示例中所做的。你有'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

在jsFiddle上尝试所有的情况!< / >

点符号

这是更标准的方法,它允许使用点(.)操作符访问对象的成员。这是你的代码在点表示法下的样子:

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'
在任意对象上调用任意方法是一种泛型函数。但 这是否意味着指定的方法已经是隐式成员 指定对象的?< / p >

是的,在你的情况下,它必须是。否则就会出现我上面提到的错误。然而,你不能在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)

你链接到的教程中的代码使用部分功能。它们本身就是一个棘手的概念。多读一些这方面的书会让你更清楚一些。


注意:这是问题源在这里中代码使用的映射函数的代码。

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