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

时间:2012-03-06 11:01:51

标签: javascript

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

function clog(x){
    var text = x;
    return console.log(text );
}

我已经做了一些实例

var bla = new clog();

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

clog.prototype.alert = alert(text);

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

clog.alert = alert(text);

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

3 个答案:

答案 0 :(得分: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";

根据您的评论:

  

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

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

  

是不是prototype只是指向构造函数对象?

不,FooFoo.prototype是完全不同的对象。 Foo是一个函数对象,它与所有函数对象一样可以具有属性。 Foo的一个属性是prototype,这是一个非功能对象,除了constructor属性之外,它最初是空白的,指向{{1} }}。通过Foo创建的实例作为原型获得Foo.prototype,而非Foonew Foo唯一的作用是创建使用Foo作为原型的对象。 (实际上,在Foo.prototype的情况下,它只是初始化这些对象;它们由Foo运算符创建 使用像new这样的传统函数,Foo创建对象。如果此代码使用ES2015 + new语法,class将无法创建对象,它会将其留给new [如果Foo是基类构造函数]或Foo的最终基类[如果Foo是子类构造函数]。)< / p>

  

如果我Foo为什么Foo.newProp = "new addition"

(为避免混淆,我已将f.newProp => undefined更改为Foo.new = ...,因为Foo.newProp = ...是关键字。而可以像你在ES5那样使用它,它最好不要。)

因为new几乎与Foo.newProp无关。您可以<{1}} 找到它,因为ff.constructor.newProp

一些ASCII艺术:

鉴于此代码:

f.constructor

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

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

现在,如果我们这样做

Foo

我们有(粗体中的新内容):

        +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
        |                                          |
        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" |
                                                      +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

([[Prototype]]是一个对象的内部字段,指的是它的原型。这可以通过网络浏览器上的JavaScript引擎上的function Foo() { } Foo.prototype.bar = "I'm bar on Foo.prototype"; Foo.bar = "I'm bar on Foo"; [或var f = new Foo(); 访问,但不要在&#39 ; t使用Object.getPrototypeOf,它只是为了与旧的SpiderMonkey特定代码向后兼容。)

现在假设我们这样做:

__proto__

所有更改都是__proto__对象(粗体中的新内容):

        +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
        |                                          |
        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" |
                                  +−−−−−−−−−−−−−−−−−−−−+

f.charlie = "I'm charlie on f"; 现在有拥有属性,名为f。这意味着这两个陈述:

f

处理略有不同。

让我们先看看charlie。以下是引擎对console.log(f.charlie); // "I'm charlie on f" console.log(f.bar); // "I'm bar on Foo.prototype" 所做的事情:

  1. f.charlie是否有自己的属性f.charlie
  2. 是;使用该属性的值。
  3. 足够简单。现在让我们看一下引擎如何处理f

    1. "charlie"是否有自己的属性f.bar
    2. 否; f有原型吗?
    3. 是; "bar"的原型是否有一个名为f
    4. 的属性
    5. 是;使用该属性的值。
    6. f"bar"之间存在很大差异:f.charlie拥有属性名为f.bar,但是< strong> inherited 属性名为f。如果charlie的原型对象没有名为bar的属性,原型对象(在本例中为f)将被检查,依此类推,直到我们用完原型。

      您可以测试某个属性是否属于&#34;拥有&#34; property,btw,使用所有对象具有的bar函数:

      Object.prototype

      从评论中回答你的问题:

        

      我制作hasOwnProperty然后console.log(f.hasOwnProperty("charlie")); // true console.log(f.hasOwnProperty("bar")); // false 如何解析内部function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;}属性?

      var ilya = new Person('ilya', 'D') name表达式的Person部分调用中,new Person(...)指的是this将返回的新生成的对象表达。因此,当您执行new时,您直接在该对象上放置属性,与原型无关。

      换句话说,这两个示例导致完全相同的this.prop = "value";对象:

      p

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

      问题1:从构造函数返回内容:

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

      99.9999%的时间,你不想从构造函数中返回任何东西。 function clog(x){ var text = x; return console.log(text ); // <=== here } 操作的工作方式是:

      1. 创建一个新的空白对象。
      2. 从构造函数的new属性中分配一个原型。
      3. 调用构造函数,使prototype引用新对象。
      4. 如果构造函数没有返回任何内容,或者返回 object 以外的内容,则this表达式的结果是在步骤1中创建的对象。
      5. 如果构造函数返回一个对象,new操作的结果就是该对象。
      6. 因此,在您的情况下,由于new没有返回任何内容,您只需从代码中删除console.log关键字即可。但是如果你使用带有返回对象的函数的return构造,那么你就搞乱了构造函数。

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

        在此代码中:

        return xyz();

        调用 clog.prototype.alert = alert(text); 函数并将其结果分配给alert上名为alert的媒体资源。由于clog.prototype没有返回任何内容,因此它完全等同于:

        alert

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

        alert(text);
        clog.prototype.alert = undefined;
        

        我们创建一个函数并将其引用分配给原型上的clog.prototype.alert = function(text) { alert(text); }; 属性。调用该函数时,它将调用标准alert

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

        这只是样式,但它的压倒一切标准:构造函数(与alert一起使用的函数)应该以大写字母开头,所以{{ 1}}而不是new。但是,这只是风格。

答案 1 :(得分:3)

添加clog.alert函数只是将clog对象附加静态函数。它不会被继承,也无法访问在alert函数中使用new clog();创建的实例。

添加clog.prototype.alert会使您创建的new clog();对象继承该功能,您还可以使用this关键字访问该实例。

function John() {
    this.id = 1;
}

John.doe = function() {
  console.log(this);
  console.log(this.id); // undefined
}

John.prototype.doe = function() {
    console.log(this);
};

John.doe(); // the John object

var me = new John();
me.doe(); // the instance, inherited from prototype
console.log(me.id); // 1

答案 2 :(得分:2)

添加到构造函数的任何属性都将充当静态属性,只能通过引用构造函数对象(即函数)并且不使用它的任何实例对象来访问它。 它就像一个Class属性而不是实例属性。