【问题标题】:What does assigning to {}.toString mean?分配给 {}.toString 是什么意思?
【发布时间】:2015-09-16 14:54:52
【问题描述】:

我正在学习 JavaScript,遇到了这个 sn-p,它演示了类型检测:

var toClass = {}.toString // Copy a reference to toString for objects into toClass variable

alert( toClass.call( [1,2] ) ) // [object Array]
alert( toClass.call( new Date ) ) // [object Date]

我不明白第一行中的空大括号是什么意思,所以我这样删除它们:

var toClass = toString

而且代码仍然有效。 但是在下面的代码中,

function getAge () {
    alert(this.age);
}

var p1 = {
    age:1
};

var tellAge=getAge;
tellAge.call(p1); //1

如果我将var tellAge=getAge 更改为var tellAge={}.getAge,我会收到一个错误:无法读取未定义的属性“调用”。 为什么是这样?是不是因为toString是内置函数?

【问题讨论】:

  • 在下面查看我的答案,以逐行概述您的代码在做什么以及它是如何工作的。

标签: javascript function tostring


【解决方案1】:

是不是因为toString是内置函数

不完全是,这是因为 javascript 中的 Objectwindow 都有一个 toString 方法。


{}.toString 中的 {} 表示 javascript 中的一个新对象。 Object 有一个 toString 方法,这就是您要创建的引用。

如果您省略 {} 它相当于 window.toString 并且幸运的是 window 对象本身具有 toString 方法。所以一切都会继续工作。

当您执行{}.getAge 时,您是在告诉解释器从不存在的Object 获取getAge 方法,将tellAge 设置为等于undefined,这会导致错误cannot read property "call" of undefined

【讨论】:

  • 这可能是我一生中读过的最干净的答案——哇!我可以补充一点,JS 中的一切都是对象吗?
  • @SidneyLiebrand 谢谢你,这是一个很好的恭维。
  • 老实说,当之无愧,它是如此简单,以至于一想到它有多有效,我的大脑就会受伤哈哈:P
  • @Jamiec:谢谢!请问这两种toString 方法有区别吗?我试过toString.call(p1)p1.toString(),都给我[object Object]
  • @YiboYang :查看我下面的答案,查看line per line 对这段代码的作用及其工作原理的概述。
【解决方案2】:

哦,天哪...在这么短的时间内要回答很多问题...但是您在 JavaScript 方面还有很长的路要走,但您会喜欢上它的...

1) 对象符号。你应该用谷歌搜索 JSON。

对象表示法意味着,您可以使用与 JavaScript 相关的特定格式定义数据。

{ } 在对象表示法中的意思是,一个对象......更具体地说,这已经是一个对象的实例。

你也可以用普通的 javascript 写这个,它看起来像这样:

new Object()

2) 函数是一等公民。

嗯,函数确实是 JS 生态系统中非常有价值的资产。这意味着你基本上可以用它们做任何你想象的事情。这包括复制对“属于”另一个对象的函数的引用。

{}.toString 是一个函数。您通常会通过执行 {}.toString( )

来调用它

您只是将引用复制到变量中的函数。在这种情况下,您将函数“存储”在变量“toClass”中

3) 原型链。你应该谷歌,好吧,原型链哈哈。

原型链,简单来说就是“喜欢”类继承。所以这意味着如果一个类 A 有一个方法“blah”,而类 B 是 A 的一个“子”类,那么这个也将有一个方法“blah”。

在 JS 世界中,原型链的顶端是“Object”。 Object的prototype中已经定义了很多函数。包括“toString”

4) 广义相对论问题...又名 this、call 和 apply。

由于函数是一等公民,它们基本上可以存在,甚至不属于特定的对象实例。

这意味着,您可以选择要在哪个 this 上下文中调用该函数。

默认情况下,当您调用看似“附加”到对象的函数时,该对象将成为该函数调用的 this 上下文:

{}.toString()    // This executes toString in the context of {}

但正如我所说,您可以选择函数实际执行的位置。为此,存在“调用”和“应用”方法。

我们之前的例子可以翻译成:

Object.prototype.toString.call({})  // This executes toString in the context of {}

5) 环境中的全局对象。

这不是一个简单的话题,因为现在 JavaScript 不仅可以在浏览器上运行,还可以在服务器上运行……NodeJS 就是一个很好的例子。

假设您在浏览器中运行它...有一个名为 window

的全局对象

您基本上可以调用全局对象中的任何函数。

所以toString等价于window.toString,window是Object的后代,它也会从Object.prototype中获取方法。

现在你的答案

getAge 未在 Object.prototype 中定义,因此您无法调用不存在的函数。

【讨论】:

  • 你的格式很奇怪,当你没有引用任何人时,你的描述有一半是在块引号中。
【解决方案3】:
  • {} 返回一个 Object (1) 类型的新对象。
  • toString() 返回一个表示对象 (2) 的字符串。
  • 对于Object 类型的对象,toString() 应返回值[object Object]
  • call() 使用给定的 this 值和单独提供的参数 (3) 执行方法。
  • 任何未作为方法显式分配给对象的函数都被隐式分配给Window 对象。在这样的函数中使用 this 总是引用 Window 对象。

现在,让我们回到你的代码,好吗?!

var toClass = {}.toString

这首先创建了一个Object 类型的通用对象。然后它返回方法toString(方法本身,而不是返回值。

这意味着 toClass 现在是一个函数。它使用对象{} 作为this 值。所以如果你只是运行toClass(),你应该总是得到[object Object]的值。

现在,棘手的问题来了!

接受这个命令:

toClass.call( [1,2] )

这里发生了什么?嗯,这类似于调用toClass(),只是你的this 值不再是{}。相反,它被[1,2] (4) 取代。这就是你得到[object Array] 的原因!

现在执行这个命令:

toClass.call( new Date )

这里发生了什么?嗯,同样的事情,真的。这类似于调用toClass()toClass.call( [1,2] ),只是你的this 值被Date(5) 类型的对象替换。这就是你得到[object Date] 的原因!

现在,使用这个函数:

function getAge () {
    alert(this.age);
}

这里发生了什么?好吧,这个函数只是提醒属性age 你方法中的this 值。默认情况下,该属性是未知的,因为this 的值为Window,而Window 对象没有age 属性。

现在,执行以下命令:

var p1 = {
    age:1
};

在这里,您创建了一个只有一个属性的对象。该属性是age,值为1。

现在,执行以下命令:

var tellAge=getAge;

在这里,您将函数getAge 分配给变量tellAge。默认情况下,这两个函数使用相同的this 值,即Window 对象。

tellAge.call(p1);

这里发生了什么?好吧,这与调用tellAge()getAge() 几乎相同,只是您的this 值不再是Window。相反,它被p1 取代。你得到1 作为结果,因为对象p1 有一个属性age 并且该属性的值为1!

现在,让我们检查以下命令:

var tellAge={}.getAge

为什么会产生错误?您在这里尝试做的是创建Object 类型的通用对象。虽然默认情况下它具有toSting 方法(在其原型(6) 中定义),但它没有getAge 方法。这就是您收到错误消息的原因。

现在使用以下代码:

var p2 = {
    age : 18
};

var p3 = {
    age : 25
};

var FObject = {
    getage : function() {
        return this.age;
    }
};

var tellAge = FObject.getage;
alert( tellAge.call(p2) ); //18
alert( tellAge.call(p3) ); //25

那么这有什么作用呢?嗯:

  • 首先创建一个名为 p2 的对象,该对象的 age 属性值为 18。
  • 然后,您创建一个名为 p3 的对象,该对象的 age 属性值为 25。
  • 然后,您创建另一个名为 FObject 的对象对象,它有一个 getage 方法。
  • 然后,将方法FObject.getage 分配给变量tellAge
  • 然后,您拨打tellAge.call(p2)。这类似于调用tellAge(),只是你的this 值被对象p2 替换。这就是你得到18 的原因!
  • 最后,您致电tellAge.call(p3)。这类似于调用tellAge()tellAge.call(p2),只是你的this 值被对象p3 替换。这就是你得到25 的原因!

我相信最后一个示例很好地概述了您正在调查的行为。


参考资料:

  1. Object
  2. Object.prototype.toString()
  3. Function.prototype.call()
  4. Array
  5. Date
  6. Object.prototype

【讨论】:

  • 虽然都是真的,但我不确定这是在回答问题的哪一部分。
  • @Jamiec :我添加了很多额外的信息,每行都提供了代码正在做什么以及它是如何工作的概述。
【解决方案4】:

只是做了这个例子让你清楚。 Jsfiddle

var toClass = {}.toString; // type detection

function person(age){
    this.age = age;
    this.getAgePlusOne = function(){
        return this.age + 1;
    };
}

var you = new person(20); // create a new person object and set age propery to 20
console.log(you);
var yourAge = you.getAgePlusOne(); // call created person object's function getAgePlusOne() to retrieve value
console.log(yourAge);

console.log(toClass.call(you)); // object
console.log(toClass.call(you.getAgePlusOne)); //function

【讨论】:

    猜你喜欢
    • 2014-01-12
    • 1970-01-01
    • 2019-11-03
    • 2016-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多