在没有.prototype的情况下向构造函数添加新属性 [英] Adding new properties to constructor function without .prototype
问题描述
当我有函数时我想用作构造函数,比如说:
when I have function I want to use as a constructor, say:
function clog(x){
var text = x;
return console.log(text );
}
我已经做了一些实例
var bla = new clog();
现在我想添加新功能,所以我会使用
Now I want to add new functionality it, so I would use
clog.prototype.alert = alert(text);
如果我这样做会有什么不同:
what would be the difference if I simply do:
clog.alert = alert(text);
不会被 clog $ c $的对象继承c>是他们的原型吗?
Would that not be inherited by the objects that clog
is their prototype?
推荐答案
构造函数创建的实例( clog
在你的情况下)继承对 clog.prototype
对象的引用。因此,如果向 clog.prototype
添加属性,它将显示在实例上。如果您将属性添加到 clog
本身,它将不会显示在实例上。
Instances created by a constructor function (clog
in your case) inherit a reference to the clog.prototype
object. So if you add a property to clog.prototype
, it will show up on instances. If you add a property to clog
itself, it will not show up on instances.
有一些问题你引用的代码,让我们看一个抽象的例子:
There are some issues with your quoted code, so let's look at an abstract example:
function Foo() {
}
Foo.prototype.bar = "I'm bar on Foo.prototype";
Foo.bar = "I'm bar on Foo";
var f = new Foo();
console.log(f.bar); // "I'm bar on Foo.prototype"
// E.g., `f` inherits from `Foo.prototype`, not `Foo`
// And this link is live, so:
Foo.prototype.charlie = "I'm charlie on Foo.prototype";
console.log(f.charlie); // "I'm charlie on Foo.prototype";
从下面的评论中:
我不明白为什么原型链会忽略直接添加到
Foo
的新属性?
因为它是 Foo.prototype
,而不是 Foo
,这是通过 new Foo()
创建的对象的原型。
Because it's Foo.prototype
, not Foo
, that is the prototype for objects created via new Foo()
.
不是
prototype
只是指向构造函数对象?
isn't
prototype
simply points to the constructor object?
不, Foo
和 Foo.prototype
是完全不同的对象。 Foo
是一个函数对象,它与所有函数对象一样可以具有属性。 Foo
的一个属性是 prototype
,这是一个非函数对象,除了<$之外最初是空白的c $ c> constructor 指向 Foo
的属性。它是 Foo.prototype
,而不是 Foo
,它参与了JavScript的原型继承。 Foo
唯一的作用是创建对象(实际上,初始化对象; new
运算符创建它们)使用 Foo.prototype
作为原型。
No, Foo
and Foo.prototype
are completely distinct objects. Foo
is a function object, which like all function objects can have properties. One of Foo
's properties is prototype
, which is a non-function object that's initially blank other than a constructor
property that points back to Foo
. It's Foo.prototype
, not Foo
, that participates in JavScript's prototypical inheritance. Foo
's only role is to create objects (well, actually, to initialize objects; the new
operator creates them) that use Foo.prototype
as their prototype.
如果我
Foo.newProp =new addition
为什么f.newProp => undefined
?
(为避免混淆,我改变了 Foo.new = ...
到 Foo.newProp = ...
以上,因为 new
是一个关键字。虽然可以像在ES5那样使用它,但最好不要这样做。)
(To avoid confusion, I've changed Foo.new = ...
to Foo.newProp = ...
above, since new
is a keyword. While you can use it like you did as of ES5, it's best not to.)
因为 Foo.newProp
几乎与 f
无关。你可以在 f.constructor.newProp
上找到它,因为 f.constructor
是 Foo
。
Because Foo.newProp
has virtually nothing to do with f
. You can find it, on f.constructor.newProp
, since f.constructor
is Foo
.
一些ASCII艺术:
鉴于此代码:
function Foo() {
}
Foo.prototype.bar = "I'm bar on Foo.prototype";
Foo.bar = "I'm bar on Foo";
我们有这些具有这些属性的对象(为清楚起见,省略了一些):
we have these objects with these properties (some omitted for clarity):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| |
V +−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−−−−−+ +−−>| [String] | |
| Foo [Function] | | +−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−−−−−+ | | "I'm bar on Foo" | |
| bar |−−−−+ +−−−−−−−−−−−−−−−−−−+ |
| prototype |−−−−+ |
+−−−−−−−−−−−−−−−−+ | |
+−−−−−−−−−−+ |
| |
V |
+−−−−−−−−−−−−−+ |
| [Object] | |
+−−−−−−−−−−−−−+ |
| constructor |−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| bar |−−−−−>| [String] |
+−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| "I'm bar on Foo.prototype" |
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
现在,如果我们这样做
var f = new Foo();
我们有(粗体的新内容):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| |
V +−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−−−−−+ +−−>| [String] | |
| Foo [Function] | | +−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−−−−−+ | | "I'm bar on Foo" | |
| bar |−−−−+ +−−−−−−−−−−−−−−−−−−+ |
| prototype |−−−−+ |
+−−−−−−−−−−−−−−−−+ | |
+−−−−−−−−−−+ |
| |
V |
+−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ |
| f [Object] | +−−−−−>| [Object] | |
+−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ |
| __proto__ |−−−−−−−−−−+ | constructor |−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
+−−−−−−−−−−−−+ | bar |−−−−−>| [String] |
+−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| "I'm bar on Foo.prototype" |
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
__ proto __
不是字面上的属性名称(至少在大多数引擎上都没有),但它是对象与其原型对象的内部链接。
__proto__
isn't literally a property name (at least not on most engines), but it's the object's internal link to its prototype object.
现在假设我们这样做:
f.charlie = "I'm charlie on f";
所有更改都是 f
对象( 粗体中的新内容:
All that changes is the f
object (new stuff in bold):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| |
V +−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−−−−−+ +−−>| [String] | |
| Foo [Function] | | +−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−−−−−+ | | "I'm bar on Foo" | |
| bar |−−−−+ +−−−−−−−−−−−−−−−−−−+ |
| prototype |−−−−+ |
+−−−−−−−−−−−−−−−−+ | |
+−−−−−−−−−−+ |
| |
V |
+−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ |
| f [Object] | +−−−−−>| [Object] | |
+−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ |
| __proto__ |−−−−−−−−−−+ | constructor |−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| charlie |−−−−−−−−−−+ | bar |−−−−−>| [String] |
+−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
| | "I'm bar on Foo.prototype" |
| +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
|
| +−−−−−−−−−−−−−−−−−−−−+
+−−−−−>| [String] |
+−−−−−−−−−−−−−−−−−−−−+
| "I'm charlie on f" |
+−−−−−−−−−−−−−−−−−−−−+
f
现在有拥有属性,名为 charlie
。这意味着这两个语句:
f
now has its own property, called charlie
. This means that these two statements:
console.log(f.charlie); // "I'm charlie on f"
console.log(f.bar); // "I'm bar on Foo.prototype"
处理略有不同。
让我们先看看 f.charlie
。这是引擎用 f.charlie
做的事情:
Let's look at f.charlie
first. Here's what the engine does with f.charlie
:
-
f
有自己的属性叫charlie
? - 是;使用该属性的值。
- Does
f
have its own property called"charlie"
? - Yes; use the value of that property.
足够简单。现在让我们看看引擎如何处理 f.bar
:
Simple enough. Now let's look at how the engine handles f.bar
:
-
f
有自己的属性名为bar
? - 否;
f
有原型吗? - 是;
f
的原型是否有一个名为bar的属性
? - 是;使用该属性的值。
- Does
f
have its own property called"bar"
? - No; does
f
have a prototype? - Yes; does
f
's prototype have a property called"bar"
? - Yes; use the value of that property.
因此 f.charlie $ c $之间存在很大差异c>和
f.bar
: f
的拥有属性名为 charlie
,但继承的属性名为 bar
。如果 f
的原型对象没有名为 bar
的属性,其将检查原型对象(在本例中为 Object.prototype
),依此类推,直到我们用完原型。
So there's a big difference between f.charlie
and f.bar
: f
has its own property called charlie
, but an inherited property called bar
. And if f
's prototype object hadn't had a property called bar
, its prototype object (in this case, Object.prototype
) would be checked, and so on up the chain until we run out of prototypes.
您可以使用所有对象具有的 hasOwnProperty
函数来测试属性是否为自己的属性btw:
You can test whether a property is an "own" property, btw, using the hasOwnProperty
function that all objects have:
console.log(f.hasOwnProperty("charlie")); // true
console.log(f.hasOwnProperty("bar")); // false
从评论中回答你的问题:
Answering your question from the comments:
我使
函数Person(first_name,last_name){this.first_name = first_name; this.last_name = last_name;}
然后var ilya = new Person('ilya','D')
它如何解析内部name
properties?
I make
function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;}
and thenvar ilya = new Person('ilya', 'D')
how does it resolves the innername
properties?
在 Person
这是新人(...)
表达式的一部分,此
是指新的生成的对象,将由 new
表达式返回。因此当你执行 this.prop =value;
时,你将一个属性直接放在该对象上,与原型无关。
Within the call to Person
that's part of the new Person(...)
expression, this
refers to the newly-generated object that will be returned by the new
expression. So when you do this.prop = "value";
, you're putting a property directly on that object, nothing to do with the prototype.
换句话说,这两个例子导致完全相同的 p
对象:
Putting that another way, these two examples result in exactly the same p
object:
// Example 1:
function Person(name) {
this.name = name;
}
var p = new Person("Fred");
// Example 2:
function Person() {
}
var p = new Person();
p.name = "Fred";
以下是我提到的引用代码的问题:
Here are the issues with the quoted code I mentioned:
问题1:从构造函数返回一些东西:
Issue 1: Returning something from a constructor function:
function clog(x){
var text = x;
return console.log(text ); // <=== here
}
99.9999%的时间,你不要我想从构造函数中返回任何东西。 new
操作的工作方式是:
99.9999% of the time, you don't want to return anything out of a constructor function. The way the new
operation works is:
- 创建一个新的空白对象。
- 从构造函数的
原型
属性中分配一个原型。 - 调用构造函数这样这个引用新对象。
- 如果构造函数没有返回任何内容,或者返回 object ,
new
表达式的结果是在步骤1中创建的对象。 - 如果构造函数返回一个对象,
new
操作的结果是该对象。
- A new blank object is created.
- It gets assigned a prototype from the constructor's
prototype
property. - The constructor is called such that
this
refers to the new object. - If the constructor doesn't return anything, or returns something other than an object, the result of the
new
expression is the object created in step 1. - If the constructor function returns an object, the result of the
new
operation is that object instead.
因此,在您的情况下,由于 console.log
不返回任何内容,您只需从代码中删除 return
关键字。但如果您使用返回对象的函数返回xyz();
构造,则会破坏构造函数。
So in your case, since console.log
doesn't return anything, you just remove the return
keyword from your code. But if you used that return xyz();
construct with a function that returned an object, you'd mess up your constructor function.
问题2:调用函数而不是引用它们
Issue 2: Calling functions rather than referring to them
在此代码中:
clog.prototype.alert = alert(text);
您致电 提醒
函数并将其结果分配给 clog.prototype
上名为 alert
的属性。由于 alert
不会返回任何内容,因此它完全等同于:
you're calling the alert
function and assigning the result of it to a property called alert
on clog.prototype
. Since alert
doesn't return anything, it's exactly equivalent to:
alert(text);
clog.prototype.alert = undefined;
...这可能不是你的意思。也许:
...which probably isn't what you meant. Perhaps:
clog.prototype.alert = function(text) {
alert(text);
};
我们创建一个函数并将其引用分配给<原型上的code> alert 属性。调用该函数时,它将调用标准警报
。
There we create a function and assign a reference to it to the alert
property on the prototype. When the function is called, it will call the standard alert
.
问题3:构造函数应该初始上限
Issue 3: Constructor functions should be initially capped
这只是样式,但它是压倒性地标准:构造函数(函数意味着与<$一起使用) c $ c> new )应以大写字母开头,所以 Clog
而不是 clog
。不过,这只是风格。
This is just style, but it's overwhelmingly standard: Constructor functions (functions meant to be used with new
) should start with an upper case letter, so Clog
rather than clog
. Again, though, this is just style.
这篇关于在没有.prototype的情况下向构造函数添加新属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!