【问题标题】:Property added to a JS object as a prototype is hoisted, whereas a prototype function is not作为原型添加到 JS 对象的属性会被提升,而原型函数则不会
【发布时间】:2017-08-19 03:53:27
【问题描述】:

我(或者至少我认为我是)非常熟悉 JavaScript 中 Hoisting 的概念。

考虑以下陈述:

函数声明会连同它的主体一起被提升,而函数表达式不会; 只有 var 语句会被提升

函数声明函数变量总是被 JavaScript 解释器移动(‘提升’)到它们的 JavaScript 作用域的顶部” - Berry Cherry

现在考虑以下函数:

function User() {
    this.name = "";
    this.life = 100;
    this.heal = function heal(player) {
        player.life+=5;
        console.log("\nHey" + player.name + "! Player " + this.name + " healed you for 5.");
    } 
}

...以及以下用户:

var Dan = new User("Danny");
var Liz = new User("Lizzy");

假设我想以原型函数的形式向已经定义的用户添加一个新的技能,如下所示(附加到代码中):

User.prototype.uppercut = function uppercut(player) {
    player.life-=10;
    console.log("\nBaaam! " + player.name + ", player " + this.name + " uppercuted you for 10 damage.");
};

...现在,使用上述技能 (在原型之前添加)

Liz.uppercut(Dan);
Liz.uppercut(Dan);

...我将收到以下错误:

Liz.uppercut(Dan);
    ^
TypeError: Liz.uppercut is not a function

我是否应该使用原型向用户对象添加一个属性,但在原型声明之前在代码中访问它,它将工作(它被提升):

console.log("Hey there " + Dan.name + "! You have " + Dan.mana + " remaining mana.");
User.prototype.mana = 100;

这基本上向我证实了适用于函数和变量的相同提升原则也适用于原型。

问题 1:这个逻辑有效吗?如果有效,您能解释一下原因吗?

问题 2:如果没有将原型表达式移到原型函数调用之上,是否有办法避免这种情况?

谢谢,祝你愉快!

代码 sn-p:

function User(name) {
    this.name = name;
    this.life = 100;
    this.heal = function heal(player) {
        player.life+=5;
        console.log("Hey" + player.name + "! Player " + this.name + " healed you for 5.");
    }
}

var Dan = new User("Danny");
var Liz = new User("Lizzy");

Liz.uppercut(Dan);
Liz.uppercut(Dan);

console.log(Liz.name + " you know have " + Liz.life + " life.");

User.prototype.mana = 100;
User.prototype.uppercut = function (player) {
    player.life-=10;
    console.log("Baaam " + player.name + "! Player " + this.name + " uppercuted you for 10 damage.");
};

console.log("Hey there " + Dan.name + "! You have " + Dan.mana + " remaining mana.");
Dan.mana = 200;
console.log("Hey there " + Dan.name + "! You have " + Dan.mana + " remaining mana.");

【问题讨论】:

  • 你的代码工作得很好(做一个 sn-p 你会看到)。错误在其他地方。此外,“提升”仅适用于变量,不会以任何方式提升对象属性。
  • 感谢@georg 的回复!事实上,我忘了提到我在原型实现之前调用了 uppercut 方法。我编辑了帖子。另外,当您说对象属性不会以任何方式提升时,您到底指的是什么?我添加了 mana sn-p(之前忘记了)来证明通过原型添加的对象属性确实被提升了,而函数表达式没有。
  • 你能把你的例子转换成一个可运行的sn-p吗? (使用编辑器中的 sn-p 按钮)。你的问题很难理解。
  • console.log 不是说“你还有未定义的剩余法力值吗?”
  • @Thai 当然会说 undefined。但这证明对象属性确实被提升。之后您可以更改每个 User 实例的值。

标签: javascript variables prototype hoisting


【解决方案1】:

您在这里混淆了变量属性。变量被提升,属性没有。

在您的示例中,Dan 是一个变量,manaDan 的一个属性。

JavaScript 处理 undefined 变量与 undefined 属性不同。

变量在声明时被提升,方法是拆分声明的左侧。即var Dan = new User("Danny"); 被分成两个语句,var Dan; Dan = new User("Danny");。然后var Dan提升到函数的顶部。但任务仍然存在。

如果您的代码仅包含 Dan = new User("Danny");,您将收到 ReferenceError,因为您将尝试对未声明的变量进行赋值。缺少声明,因此变量从未被提升。

另一方面,属性的操作方式不同。属性访问器返回对父对象进行哈希查找的结果。在Dan.mana的情况下,定义了父对象,所以没有ReferenceError,但是mana不是Dan的属性,所以哈希查找返回undefined。没有发生提升,因为没有变量声明。

因此,原型不能与提升绑定,因为它们是严格的赋值操作。即使原型在函数开头被修改,它也不会受到提升的影响,因为提升影响变量的声明,而不是赋值(这发生在调用的右侧)。

【讨论】:

  • 这就是我要找的。非常感谢@Claies 的详细回答!
【解决方案2】:

不,分配(到原型属性或其他)没有提升。

代码

function User() { this.name = ""; … }
var Dan = new User("Danny");
console.log("Hey there " + Dan.name + "! You have " + Dan.mana + " remaining mana.");
User.prototype.mana = 100;

不起作用,它将记录Hey there ! You have undefined remaining mana.

【讨论】:

    【解决方案3】:

    访问未在对象上定义的属性将为您提供undefined。这里没有提升规则。

    • Liz.mana 会给你undefined
    • Liz.uppercut 也会给你undefined
    • Liz.agility 也会给你undefined

    调用Liz.uppercut(Dan) 会引发错误,因为您试图将Liz.uppercut(即undefined)作为函数调用。这会导致“未定义不是函数”错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-23
      • 2016-05-22
      • 1970-01-01
      • 2020-05-09
      • 2017-01-12
      • 2010-10-07
      • 1970-01-01
      相关资源
      最近更新 更多