分配给 {}.toString 是什么意思?

What does assigning to {}.toString mean?

我正在学习 JavaScript,我发现了这个演示类型检测的片段:

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,我会得到一个错误:无法读取未定义的 属性 "call"。 为什么是这样?是因为toString是内置函数吗?

Is it because toString is a built-in function

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


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

如果省略 {} 它等同于 window.toString 幸运的是 window 对象本身有一个 toString 方法。所以一切都继续工作。

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

  • {} returns 一个类型为 Object (1).
  • 的新对象
  • toString() returns 表示对象的字符串 (2).
  • 对于 Object 类型的对象,toString() 应该 return 值 [object Object]
  • call() 使用给定的 this 值和单独提供的参数执行方法 (3).
  • 任何未显式分配给对象的方法的函数都隐式分配给 Window 对象。在这样的函数中使用 this 总是引用 Window 对象。

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

var toClass = {}.toString

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

这意味着 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);
}

这里发生了什么?嗯,这个函数只是提醒你方法中 this 值的 属性 age。默认情况下,这个 属性 是未知的,因为 this 值是 WindowWindow 对象没有 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

只是举这个例子让你明白。 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

哦,天哪……在这么短的时间内要回答的事情太多了……但是你还有很长的路要走JavaScript,但你会爱上它的……

1) 对象表示法。对于 JSON,你应该 google。

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

{ } in object notation means, an object... more specifically speaking, this is already an instance of an object.

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

new Object()

2) 函数首先是 class 公民。

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

{}.toString is a function. You would normally invoke it by doing {}.toString( )

You are instead, just copying the reference to the function in a variable. In this case you "store" the function in the variable "toClass"

3) 原型链。你应该google,嗯,原型链哈哈

原型链,简单来说就是"like"class继承。所以这意味着如果 class A 有一个方法 "blah" 并且 class B 是 A 的 "child" class,那么,这也将有方法"blah".

In JS world, the top of the prototype chain is "Object". And many functions are already defined in the prototype of Object. Including "toString"

4) 广义相对论问题...a.k.a。这个,打电话申请。

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

That means, you can choose on which this context you want the function to be invoked on.

默认情况下,当您调用对对象来说似乎是 "attached" 的函数时,该对象将成为该函数调用的 this 上下文:

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

但正如我所说,您可以只选择函数实际执行的位置。为此,方法 "call" 和 "apply" 存在。

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

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

5) 您环境中的全局对象。

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

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

的全局对象

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

So toString is equivalent to window.toString and window is descendent of Object, it will also get the method from the Object.prototype.

现在你的答案

getAge is not defined in Object.prototype, so you cannot invoke a non existing function.