为什么 NodeJS 不像浏览器那样执行构造函数

Why doesn't NodeJS execute constructors the way browsers do?

本文关键字:执行 构造函数 浏览器 NodeJS 为什么      更新时间:2023-09-26

我正在玩NodeJS,我注意到了一些奇怪的东西。

我只是通过下面的代码探索构造函数的使用。

// We declare the constructor.
function Personne(inName) {
    console.log("go!");
    if ('undefined' != typeof inName) {
        this.name = inName;
    }
}
// We declare the object's prototype (that will be used by the constructor).
var PersonnePrototype = {
    name: 'toto',
    setName: function(inName) {
        this.name = inName; // "this" refers to the new object being created.
    },
    getName: function() {
        return this.name; // "this" refers to the new object being created.
    }
};
Personne.prototype = PersonnePrototype;
var p = new Personne("Tom"); 
console.log("p.getName(): " + p.getName());
console.log(p);
console.log(Object.getPrototypeOf(p));

首先,我希望构造函数Personne(inName)中的代码在使用运算符 new 时执行。

显然,情况并非如此。下面,我给出执行的输出。

$ node loop-closure.js 
p.getName(): toto
{ prototype: { name: false, setName: [Function], getName: [Function] } }
{ name: 'toto', setName: [Function], getName: [Function] } 

您可以看到构造函数未执行...

但是,如果我在 Chrome 或 Firefox 上执行相同的代码,则执行构造函数!

火狐:

"go!"
"Given: Tom"
"p.getName(): Tom"
Object { name: "Tom" }
Object { name: "toto", setName: window.onload/PersonnePrototype.setName(inName), getName: window.onload/PersonnePrototype.getName() }

铬:

go!
Given: Tom
p.getName(): Tom
Personne {name: "Tom", setName: function, getName: function}
Object {name: "toto", setName: function, getName: function}

我认为 NodeJS 是 Chrome 使用的 JavaScript 解释器。如果这是正确的,那么为什么Chrome和NodeJS之间的解释不同?

更新

看到了评论,我尝试这样做:

我只是将代码复制/粘贴到一个文件中,然后在该文件上调用 NodeJs。

是的,你是对的:它按预期工作。

然后我找到了导致问题的原因。

我在执行的文件中有额外的代码。我在我给你的代码段之后放了一个 return 语句。下面,我给你完整的代码:

我正在使用 NodeJs 版本 0.10.35:

$ node -v
v0.10.35
// We declare the constructor.
function Personne(inName) {
    console.log("go!");
    if ('undefined' != typeof inName) {
        this.name = inName;
    }
}
// We declare the object's prototype (that will be used by the constructor).
var PersonnePrototype = {
    name: 'toto',
    setName: function(inName) {
        this.name = inName; // "this" refers to the new object being created.
    },
    getName: function() {
        return this.name; // "this" refers to the new object being created.
    }
};
Personne.prototype = PersonnePrototype;
var p = new Personne("Tom"); 
console.log("p.getName(): " + p.getName());
console.log(p);
console.log(Object.getPrototypeOf(p));

return;
console.log("Does the prototype has a constructor property ? " + Object.getPrototypeOf(p).hasOwnProperty('constructor'));
// Other way to say the same thing:
function Personne() {
    this.prototype = {
        name: false,
        setName: function(inName) {
            this.name = inName; // "this" refers to the new object being created.
        },
        getName: function() {
            return this.name; // "this" refers to the new object being created.
        }
    }
};
var p = new Personne(); // Object "p" has a parent. This parent has been created by the prototype (which is a function).
p.setName("Tom");
console.log("The name is " + p.getName() + " / " + this.name);
console.log("Does the prototype has a constructor property ? " + Object.getPrototypeOf(p).hasOwnProperty('constructor'));
// Ou encore :
var p = Object.create(Personne.prototype);
p.setName("Tom");
console.log("The name is " + p.getName() + " / " + this.name);
// We can see the difference between the prototype and the instanced object.
// Both are objects.
// However, as you can see, they do not present the same properties.
utils.dump(Object.getPrototypeOf(p));
Object.getPrototypeOf(p).name;
utils.dump(p);
if (Object.getPrototypeOf(p).getName() != p.getName()) {
    console.log("The prototype and the object have different properties.");
    console.log("Prototype: " + Object.getPrototypeOf(p).getName());
    console.log("Object: " + p.getName());
}
// ------------------------------------------------------------------------------------
// Heritage
// ------------------------------------------------------------------------------------
function Personne() {
    this.prototype = {
        name: false,
        setName: function(inName) {
            this.name = inName; // "this" refers to the new object being created.
        },
        getName: function() {
            return this.name; // "this" refers to the new object being created.
        }
    }
};
function student() {
    Personne.call(this);
}

我虽然返回语句后的代码没有干扰。显然,确实如此。

NodeJs 在执行之前编译所有代码。因此,如果我稍后在代码中重新定义构造函数,那么它将修改它的第一个匹配项。

好的,但是:

var v = 1;
console.log("v = " + v);
var v = 2;
console.log("v = " + v);

输出:

$ node test1.js 
v = 1
v = 2

和:

var v = 1;
console.log("v = " + v);
return;
var v = 2;
console.log("v = " + v);

输出:

$ node test1.js 
v = 1

并且(可能有参考资料):

var v = { a: 1 };
console.log("v.a = " + v.a);
return;
var v = { a: 2 };
console.log("v.a = " + v.a);

输出:

$ node test1.js 
v.a = 1

这里没有什么不寻常的...return 语句后面的代码似乎不会更改 return 语句之前的代码。

@Alexey十

感谢您的提示。

测试1.js

function Construct() { this.name = "Tom"; }
var v = new Construct();
console.log("v.name = " + v.name);
return;
function Construct() { this.name = "Joe"; }

测试2.js

var Construct = function() { this.name = "Tom"; }
var v = new Construct();
console.log("v.name = " + v.name);
return;
var Construct = function() { this.name = "Joe"; }

测试 1 的结果为:v.name = 乔

测试 1 的结果是:v.name = 汤姆

事实上,这篇文章不是关于 NodeJs,而是关于提升的(感谢 Alexey Ten)

很好的解释:

http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html

在使用过C,C++,Java,PHP,Perl,Tcl,Python或GO等语言之后,我希望JavaScript在变量声明方面以相同的方式运行。如果在使用变量之前未声明变量,则根据语言:

  • 你最终会得到一个错误。
  • 变量的值未定义。

在 JavaScript 中,结果取决于变量的声明方式。请注意,变量的声明方式决定了其范围。提升意味着 JavaScript 会将所有变量的声明放在变量范围的开头。

var x;  // Declaration
x = 10; // Initialization: this is the first time a value is affected to the variable.  
x = 20; // Affectation.

换句话说:

(function() {
    console.log("x = " + x + " f = " + f); // => x = undefined f = undefined
    // Declaration + initialization
    var x = 1; 
    var f = function() {};
})();

相当于:

(function() {
    // Declaration
    var x, f;
    console.log("x = " + x + " f = " + f); // => x = undefined f = undefined
    // initialization
    x = 2;
    f = function() {};
})();

这并不等同于:

(function() {
    try {
        console.log("x = " + x + " f = " + f);
        // initialization
        x = 2;
        f = function() {};
    } catch(e) {
        console.log("ERROR: " + e.message); // => ERROR: x is not defined (and f is not defined either).
    }
})();

但是,要小心!有一些微妙的问题:

(function() {
    try {
        console.log("x = " + x + " f = " + f);
        x = 1;
        f = function() {};
    } catch (e) {
        console.log("ERROR: " + e.message); // => ERROR: x is not defined (and f is not defined either).
    }
})();
// x and f should be defined ???
console.log("x = " + x + " f = " + f); // => ReferenceError: x is not defined.

应该定义 x 和 f 吗?事实上,定义 x 和 f 的代码不会执行,因为 en 异常是在之前引发的。如果你试试这个:

(function() { 
    try {
        x = 1;
        f = function() {};
    } catch (e) {
        console.log("ERROR: " + e.message);
    }
})();
console.log("x = " + x + " f = " + f); // => x = 1 f = function () {} 

或:

(function() {
    try {
        console.log("x = " + x + " f = " + f); // => x = undefined f = undefined
        x = 1;
        f = function() {};
    } catch (e) {
        console.log("ERROR: " + e.message);
    }
})();
console.log("x = " + x + " f = " + f); // => x = 1 f = function () {}
var x = 10, f;