Nodejs 原型中定义成员的疑问?
Nodejs 原型中定义成员的疑问?
有书《Node.js开发指南》这书的看一下162页,没书的,我给出代码和相关语句。
以下为书中原话: 尽量在构造函数内定义一般成员,尤其是对象或数组,因为用原型定义的成员是多个 实例共享的。
经我测试(下面是测试用例),就算在原型中定义的成员,并不是共享的,而是各有一份,这个怎么说?
function Foo () {
var innerVar = 'hello';
this.prop1 = 'BYVoid';
this.func1 = function() {
innerVar = '';
};
}
Foo.prototype.prop2 = ‘Car’;
Foo.prototype.func2 = function() {
console.log(this.prop2);
};
var foo1 = new Foo();
var foo2 = new Foo();
console.log(foo1.func1 == foo2.func1); //false
console.log(foo1.func2 == foo2.func2); //true
foo1.prop2 = ‘Mobile’;
foo1.func2(); //Mobile
foo2.func2(); //Car
Nodejs 原型中定义成员的疑问?
问题背景
在《Node.js开发指南》第162页提到,尽量在构造函数内定义一般成员,尤其是对象或数组,因为用原型定义的成员是多个实例共享的。但我在实际测试中发现,即使在原型中定义的成员,每个实例似乎都有独立的一份,这与书中描述的情况不符。
测试用例
让我们通过一个简单的例子来理解这个问题:
function Foo () {
var innerVar = 'hello';
this.prop1 = 'BYVoid';
this.func1 = function() {
innerVar = '';
};
}
Foo.prototype.prop2 = 'Car';
Foo.prototype.func2 = function() {
console.log(this.prop2);
};
var foo1 = new Foo();
var foo2 = new Foo();
console.log(foo1.func1 == foo2.func1); // false
console.log(foo1.func2 == foo2.func2); // true
foo1.prop2 = 'Mobile';
foo1.func2(); // Mobile
foo2.func2(); // Car
分析
-
this.prop1
和this.func1
prop1
是在构造函数内部定义的实例属性。func1
是在构造函数内部定义的方法。每次创建新实例时,都会重新生成一个新的函数实例,因此foo1.func1
和foo2.func1
是不同的函数对象,所以比较结果为false
。
-
Foo.prototype.prop2
和Foo.prototype.func2
prop2
是在原型上定义的属性。所有实例共享同一个原型对象,因此foo1.prop2
和foo2.prop2
指向的是同一个属性。当你修改foo1.prop2
时,不会影响到foo2.prop2
,因为 JavaScript 的原型链机制会优先查找实例本身的属性。func2
是在原型上定义的方法。所有实例共享同一个方法对象,因此foo1.func2
和foo2.func2
是相同的函数对象,所以比较结果为true
。
总结
- 在构造函数内部定义的属性和方法,每次实例化时都会生成新的属性和方法对象,因此这些属性和方法是不共享的。
- 在原型上定义的属性和方法,所有实例共享同一个属性和方法对象,因此这些属性和方法是共享的。
通过以上分析,可以清楚地看到为什么在原型上定义的属性和方法在不同实例间的行为差异。
console.log(foo1.func2 == foo2.func2);
输出 true
了这还不能证明么?
呃, 你是想说
foo1.prop2 = 'Mobile';
这句有问题么? 你可以试试
console.log(foo1); // { prop1: 'BYVoid', func1: [Function], prop2: 'Mobile' }
console.log(foo2); // { prop1: 'BYVoid', func1: [Function] } 没有 prop2!
其实 foo2
自身是没有 prop2
这个属性的, 但因为之前那一句 foo1
有了. js 解释器发现某个对象自身没这个属性时, 会往它的构造方法原型上找
console.log(foo2.constructor.prototype); // { prop2: 'Car', func2: [Function] }
如果能找到, 这个就作为属性值.
所以实际上是 this.prop2
这个表达式求值在两个对象上结果不一导致的.
说得有道理。 还有一个疑问就是为什么foo1.prop2 不直接使用Foo原型中的prop2,而会去新生成一个prop2属性呢?
因为不能通过实例重写prototype的属性
foo1.prop2 = 'Mobile';
只是屏蔽了prototype的prop2属性。你可以通过__proto__访问原型链上的prop2属性:
foo1.__proto__.prop2 == "Car"
谢谢大家,终于明白了。
正解. 一个是原型的属性值,一个是对象自身的属性(动态添加的),造成了使用this求值的区别.
//假想的new操作符 function New (f) { var n = { ‘proto’: f.prototype }; return function () { f.apply(n, arguments); return n; }; } // 顺序:先使用prototype创建对象,再使用构造函数处理创建的对象
这段代码展示了在Node.js中如何使用构造函数和原型来定义成员。让我们逐条解析书中提到的观点以及测试结果。
构造函数内部定义成员
在构造函数内部定义的成员(如this.prop1
)是每个实例独有的,不会被其他实例共享。因此,每次创建新的实例时,都会初始化这些成员。
function Foo () {
var innerVar = 'hello'; // 这是一个局部变量,不属于实例或原型
this.prop1 = 'BYVoid'; // 每个实例都有一个prop1属性,值为'BYVoid'
}
构造函数内部定义方法
构造函数内部定义的方法(如this.func1
)也是每个实例独有的。因此,每次创建新实例时,都会重新定义这些方法,即使它们的实现相同。
this.func1 = function() { // 每个实例都有一个func1方法
innerVar = '';
};
原型上定义成员
在原型上定义的成员(如Foo.prototype.prop2
)确实是所有实例共享的。但是,在测试代码中,我们修改了foo1.prop2
的值,这并不会影响到foo2
中的prop2
,因为prop2
在这里被赋值为foo1
的新值,而不是从原型中继承来的。
Foo.prototype.prop2 = 'Car'; // 所有Foo的实例都共享这个prop2
Foo.prototype.func2 = function() { // 所有Foo的实例都共享这个func2方法
console.log(this.prop2);
};
测试结果解释
foo1.func1 == foo2.func1
返回false
,因为这两个方法是在每个实例创建时重新定义的。foo1.func2 == foo2.func2
返回true
,因为这两个方法是从原型继承来的,指向同一个内存地址。
修改prop2
的结果
当对foo1.prop2
进行赋值时,实际上是在foo1
对象上添加了一个新的属性prop2
,而没有改变原型上的prop2
。所以foo2
仍然保留了原型上的prop2
值。
foo1.prop2 = 'Mobile'; // 在foo1上添加一个新的prop2属性,值为'Mobile'
foo1.func2(); // 输出'Mobile'
foo2.func2(); // 输出'Car',因为foo2使用的是原型上的prop2
希望这能帮助你理解Node.js中构造函数和原型的工作方式。