作为原型添加到 JS 对象的属性会被提升,而原型函数则不会 [英] Property added to a JS object as a prototype is hoisted, whereas a prototype function is not

查看:35
本文介绍了作为原型添加到 JS 对象的属性会被提升,而原型函数则不会的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我(或者至少我认为我)非常熟悉 JavaScript 中的提升.

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

考虑以下陈述:

函数声明将与其主体一起提升,而函数表达式则不会;只会提升 var 语句.

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

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

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

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

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

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

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?

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

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

谢谢祝你好运!

代码片段:

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

推荐答案

你在这里混淆了变量属性.变量被提升,属性不是.

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

在你的例子中,Dan 是一个变量,manaDan 的一个属性.

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

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

JavaScript handles undefined variables differently from undefined properties.

变量在声明时被提升,方法是将声明的左侧分开.即 var Dan = new User("Danny"); 被拆分成两条语句, var Dan;Dan = new User("Danny");.var Dan 然后被提升到函数的顶部.但分配保持不变.

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.

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

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.

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

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

这篇关于作为原型添加到 JS 对象的属性会被提升,而原型函数则不会的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆