属性 添加到 JS 对象作为原型被提升,而原型函数不是

Property added to a JS object as a prototype is hoisted, whereas a prototype function is not

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

考虑以下陈述:

A function declaration will be hoisted along with its body, whereas a function expression will not; only the var statement will be hoisted.

Function declarations and function variables are always moved (‘hoisted’) to the top of their JavaScript scope by the JavaScript interpreter” - 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");

说我想以原型函数的形式向已经定义的用户添加一个新的skill,就像这样(附加到代码):

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

... 现在,使用所说的 skill (在原型之前添加) :

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

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

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

我是否应该使用原型向 User 对象添加一个 属性,但在原型声明之前的代码中访问它,它将 起作用(吊装):

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

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

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

问题 2:如果有办法避免这种情况w/o将原型表达式移动到原型函数调用之上?

谢谢祝你玩得开心!

代码片段:

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.");

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

代码

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.

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

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

JavaScript 处理 undefined 变量的方式不同于 undefined 属性。

变量在声明时 提升,通过拆分声明的左侧。即 var Dan = new User("Danny"); 被分成两个语句,var Dan; Dan = new User("Danny");var Dan 然后 提升 到函数的顶部。但分配保持不变。

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

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

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

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

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

调用 Liz.uppercut(Dan) 会引发错误,因为您正试图将 Liz.uppercut(即 undefined)作为函数调用。这会导致“undefined is not a function”错误。