在没有.prototype的情况下向构造函数添加新属性

ily*_*lyo 16 javascript

当我有函数我想用作构造函数时,说:

function clog(x){
    var text = x;
    return console.log(text );
}
Run Code Online (Sandbox Code Playgroud)

我已经做了一些实例

var bla = new clog();
Run Code Online (Sandbox Code Playgroud)

现在我想添加新的功能,所以我会用

clog.prototype.alert = alert(text);
Run Code Online (Sandbox Code Playgroud)

如果我这样做会有什么不同:

clog.alert = alert(text);
Run Code Online (Sandbox Code Playgroud)

它不会被clog原型的对象继承吗?

T.J*_*der 45

由构造函数(clog在您的情况下)创建的实例继承对该clog.prototype对象的引用.因此,如果添加属性clog.prototype,它将显示在实例上.如果向clog自身添加属性,它将不会显示在实例上.

引用代码存在一些问题,让我们看一个抽象的例子:

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";
Run Code Online (Sandbox Code Playgroud)

来自您的评论如下:

我不明白为什么Foo原型链会忽略直接添加的新属性?

因为它Foo.prototype不是Foo,这是通过创建的对象的原型new Foo().

是不是prototype简单地指向构造函数对象?

不,Foo并且Foo.prototype是完全不同的对象.Foo是一个函数对象,它像所有函数对象一样可以拥有属性.其中一个Foo属性是prototype,它是一个非功能对象,除了constructor指向的属性之外,它最初是空白的Foo.它Foo.prototype,不Foo,是参与JavScript的原型继承.new Foo唯一的作用是创建用作原型的对象(实际上,初始化对象; Foo操作符创建它们)Foo.prototype.

如果我Foo为什么这样做new

(为了避免混淆,我已经改变Foonew上面,因为class是关键字.虽然你可以像你这样的ES5的使用它,最好不要.)

因为new几乎与之无关Foo.你可以找到它Foo,因为FooFoo.

一些ASCII艺术:

鉴于此代码:

function Foo() {
}
Foo.prototype.bar = "I'm bar on Foo.prototype";
Foo.bar = "I'm bar on Foo";
Run Code Online (Sandbox Code Playgroud)

我们有这些具有这些属性的对象(为清楚起见,省略了一些):

        +???????????????????????????????????????+
        |                                       |
        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();
Run Code Online (Sandbox Code Playgroud)

我们有(粗体新内容):

        +??????????????????????????????????????????+
        |                                          |
        V                 +??????????????????+     |
+????????????????+    +??>| [String]         |     |
| Foo [Function] |    |   +??????????????????+     |
+????????????????+    |   | "I'm bar on Foo" |     |
| bar            |????+   +??????????????????+     |
| prototype      |????+                            |
+????????????????+    |                            |
                      +?????????????+              |
                                    |              |
                                    V              |
+???????????????+                 +?????????????+  |
| f [Object]    |          +?????>| [Object]    |  |
+???????????????+          |      +?????????????+  |
| [[Prototype]] |??????????+      | constructor |??+  +????????????????????????????+
+???????????????+                 | bar         |????>| [String]                   |
                                  +?????????????+     +????????????????????????????+
                                                      | "I'm bar on Foo.prototype" |
                                                      +????????????????????????????+

Foo.newProp = "new addition" 并不是字面上的属性名称(至少在大多数引擎上都没有),但它是对象与其原型对象的内部链接.

现在假设我们这样做:

f.charlie = "I'm charlie on f";
Run Code Online (Sandbox Code Playgroud)

所有改变都是f.newProp => undefined对象(粗体的新东西):

        +??????????????????????????????????????????+
        |                                          |
        V                 +??????????????????+     |
+????????????????+    +??>| [String]         |     |
| Foo [Function] |    |   +??????????????????+     |
+????????????????+    |   | "I'm bar on Foo" |     |
| bar            |????+   +??????????????????+     |
| prototype      |????+                            |
+????????????????+    |                            |
                      +?????????????+              |
                                    |              |
                                    V              |
+???????????????+                 +?????????????+  |
| f [Object]    |          +?????>| [Object]    |  |
+???????????????+          |      +?????????????+  |
| [[Prototype]] |??????????+      | constructor |??+  +????????????????????????????+
| charlie       |??????????+      | bar        |?????>| [String]                   |
+???????????????+          |      +?????????????+     +????????????????????????????+
                           |                          | "I'm bar on Foo.prototype" |
                           |                          +????????????????????????????+
                           |
                           |      +????????????????????+
                           +?????>| [String]           |
                                  +????????????????????+
                                  | "I'm charlie on f" |
                                  +????????????????????+

Foo.new = ...现在有自己的财产,叫做Foo.newProp = ....这意味着这两个陈述:

console.log(f.charlie); // "I'm charlie on f"
console.log(f.bar);     // "I'm bar on Foo.prototype"
Run Code Online (Sandbox Code Playgroud)

处理略有不同.

我们new先来看看吧.这是引擎的用途Foo.newProp:

  1. f没有自己的属性叫f.constructor.newProp
  2. 是; 使用该属性的值.

很简单.现在让我们看一下引擎的处理方式f.constructor:

  1. Foo没有自己的属性叫Object.getPrototypeOf
  2. 没有; 确实__proto__有原型?
  3. 是; 那个__proto__原型有一个名为f?的属性?
  4. 是; 使用该属性的值.

所以f和之间有一个很大的区别charlie:f.charlie自己的属性调用f.charlie,但是一个被称为的继承属性f.如果"charlie"原型对象没有调用属性f.bar,那么它的原型对象(在这种情况下f)将被检查,依此类推,直到我们用完原型.

您可以使用"bar"所有对象具有的函数来测试属性是否为"自己的"属性(btw):

console.log(f.hasOwnProperty("charlie")); // true
console.log(f.hasOwnProperty("bar"));     // false
Run Code Online (Sandbox Code Playgroud)

从评论中回答你的问题:

我制作f然后f如何解决内在"bar"属性?

在对表达式f.charlie的一部分的调用中f.bar,f引用将由charlie表达式返回的新生成的对象.所以,当你这样做时bar,你将一个属性直接放在该对象上,与原型无关.

换句话说,这两个例子产生完全相同f对象:

// Example 1:
function Person(name) {
    this.name = name;
}
var p = new Person("Fred");

// Example 2:
function Person() {
}
var p = new Person();
p.name = "Fred";
Run Code Online (Sandbox Code Playgroud)

以下是我提到的引用代码的问题:

问题1:从构造函数返回一些东西:

function clog(x){
    var text = x;
    return console.log(text ); // <=== here
}
Run Code Online (Sandbox Code Playgroud)

在99.9999%的时间内,您不希望从构造函数中返回任何内容.该bar操作的工作方式是:

  1. 创建一个新的空白对象.
  2. 它从构造函数的Object.prototype属性中分配了一个原型.
  3. 调用构造函数,以hasOwnProperty引用新对象.
  4. 如果构造函数未返回任何内容,或返回除对象之外的其他内容,则function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;}表达式的结果是在步骤1中创建的对象.
  5. 如果构造函数返回一个对象,则var ilya = new Person('ilya', 'D')操作的结果是该对象.

因此,在您的情况下,由于name不返回任何内容,您只需Person从代码中删除关键字即可.但是如果你使用new Person(...)带有返回对象的函数的那个构造,你就会弄乱你的构造函数.

问题2:调用函数而不是引用函数

在这段代码中:

clog.prototype.alert = alert(text);
Run Code Online (Sandbox Code Playgroud)

你正在调用this函数并将其结果分配给一个名为newon 的属性this.prop = "value";.因为p不返回任何东西,它完全等同于:

alert(text);
clog.prototype.alert = undefined;
Run Code Online (Sandbox Code Playgroud)

......这可能不是你的意思.也许:

clog.prototype.alert = function(text) {
    alert(text);
};
Run Code Online (Sandbox Code Playgroud)

在那里,我们创建一个函数并将其引用分配给new原型上的属性.调用该函数时,它将调用该标准prototype.

问题3:构造函数应该初始上限

这只是样式,但它是绝对标准的:构造函数(与之一起使用的函数this)应该以大写字母开头,new而不是new.不过,这只是风格.

  • @IlyaD:*"但所有`Foo.prototype`都指向'Foo`!"*不,这就是你犯的错误.重复我上面所说的:**它们是完全不同的东西**.我在答案中添加了一些ASCII艺术,试图提供帮助.*"或者在这之间做Foo.bar ="foobar"然后创建新实例并访问它?"*所有`Foo.bar ="foobar"`做的是向`foo`对象添加一个名为`bar的属性`.没有创建实例,并且对原型链没有影响. (2认同)
  • @IlyaD:不用担心,很高兴这有帮助.我已经更新了答案,以解决上面的问题. (2认同)