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

梦想的初衷 提交于 2019-12-23 20:39:25

问题


I am (or at least I thought I was) pretty much familiar with the concept of Hoisting in JavaScript.

Consider the following statements:

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

Now consider the following function:

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

... and the following users:

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

Say I want to add a new skill in the form of a prototype function to already defined users, like so (appended to the code):

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

... and now, use said skill (add this before prototype):

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

... I will receive the following error:

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

Should I add a property to the User objects using a prototype, but accessing it in the code before the prototype declaration, it will work (it is hoisted):

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

Basically this confirmed to me that the same hoisting principles which apply to functions & variables, also apply to prototypes.

Question no.1: Is this logic valid and if so, can you explain why?

Question no.2: If there a way to avoid this w/o moving the prototype expression above the prototype function call?

Thanks and have a good one!

Code snippet:

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

回答1:


You are confusing variables and properties here. Variables are hoisted, properties are not.

In your example, Dan is a variable, mana is a property of Dan.

JavaScript handles undefined variables differently from undefined properties.

Variables are hoisted when they are declared, by splitting off the left hand side of the declaration. i.e. var Dan = new User("Danny"); is split into two statements, var Dan; Dan = new User("Danny");. the var Dan is then hoisted to the top of the function. but the assignment stays in place.

If your code contatined only Dan = new User("Danny");, you would receive a ReferenceError, because you would be trying to make an assignment to a variable that isn't declared. The declaration is missing, thus the variable was never hoisted.

Properties, on the other hand, operate differently. Property accessors return the result of a hash lookup on the parent object. In the case of Dan.mana, the parent object is defined, so no ReferenceError, but mana is not a property of Dan, so the hash lookup returns undefined. No hoisting has occurred, because there is no variable declaration.

Therefore, prototypes cannot be tied to hoisting, because they are strictly assignment operations. Even if the prototype is modified at the beginning of the function, it wouldn't be affected by hoisting, since hoisting only affects the declaration of the variable, not the assignment (which happens on the right side of the call).




回答2:


No, assignments (to prototype properties or other) are not hoisted.

The code

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;

does not work, it will log Hey there ! You have undefined remaining mana..




回答3:


Accessing a property that is not defined on the object will give you undefined. There is no hoisting rule in play here.

  • Liz.mana will give you undefined.
  • Liz.uppercut will also give you undefined.
  • Liz.agility will also give you undefined.

Calling Liz.uppercut(Dan) throws the error because you are trying to call Liz.uppercut, which is undefined, as a function. That leads to the “undefined is not a function” error.



来源:https://stackoverflow.com/questions/43029490/property-added-to-a-js-object-as-a-prototype-is-hoisted-whereas-a-prototype-fun

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!