简体   繁体   English

在没有.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 );
}

And I have made some instances of it 我已经做了一些实例

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);

Would that not be inherited by the objects that clog is their prototype? clog的对象是否会继承它们的原型?

Instances created by a constructor function ( clog in your case) inherit a reference to the clog.prototype object. 由构造函数创建的实例(在您的情况下为clog )继承对clog.prototype对象的引用。 So if you add a property to clog.prototype , it will show up on instances. 因此,如果向clog.prototype添加属性,它将显示在实例上。 If you add a property to clog itself, it will not show up on instances. 如果添加一个属性来clog自身,它将不会显示在实例上。

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";

From your comment below: 来自您的评论如下:

I don't understand why new properties added directly to Foo would be ignored by the prototype chain? 我不明白为什么原型链会忽略直接添加到Foo新属性?

Because it's Foo.prototype , not Foo , that is the prototype for objects created via new Foo() . 因为它是Foo.prototype ,而不是Foo ,这是通过new Foo()创建的对象的原型。

isn't prototype simply points to the constructor object? 是不是prototype只是指向构造函数对象?

No, Foo and Foo.prototype are completely distinct objects. 不, FooFoo.prototype是完全不同的对象。 Foo is a function object, which like all function objects can have properties. Foo是一个函数对象,它像所有函数对象一样可以拥有属性。 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 . Foo的一个属性是prototype ,它是一个非函数对象,除了指向Fooconstructor属性之外,它最初是空白的。 It's Foo.prototype , not Foo , that instances created via new Foo get as their prototype. 它是Foo.prototype ,而不是Foo ,通过new Foo创建的实例作为他们的原型。 Foo 's only role is to create objects that use Foo.prototype as their prototype. Foo的唯一作用是创建使用Foo.prototype作为原型的对象。 (Actually, in Foo 's case, it just initializes those objects; they're created by the new operator. With a traditional function like Foo , new creates the object. If this code were using ES2015+ class syntax, new wouldn't create the object, it would leave that to Foo [if Foo were a base class constructor] or Foo 's ultimate base class [if Foo were a subclass constructor].) (实际上,在Foo的情况下,它只是初始化那些对象;它们是由new运算符创建的。使用像Foo这样的传统函数, new会创建对象。如果此代码使用ES2015 + class语法,则new不会创建对象,它会留给Foo [如果Foo是基类构造函数]或Foo的最终基类[如果Foo是子类构造函数]。)

If I do Foo.newProp = "new addition" why is f.newProp => undefined ? 如果我做Foo.newProp = "new addition"为什么f.newProp => undefined

(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.new = ...改为Foo.newProp = ...上面,因为new是一个关键字。虽然你可以像使用ES5一样使用它,但最好不要这样做。)

Because Foo.newProp has virtually nothing to do with f . 因为Foo.newProp几乎与f无关。 You can find it, on f.constructor.newProp , since f.constructor is Foo . 可以f.constructor.newProp上找到它,因为f.constructorFoo

Some ASCII-art: 一些ASCII艺术:

Given this code: 鉴于此代码:

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

Now if we do 如果我们这样做

var f = new Foo();

we have (new stuff in bold ): 我们有( 粗体新内容):

+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
        |                                          |
        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]] is an object's internal field referring to its prototype. This is accessible via Object.getPrototypeOf [or __proto__ on JavaScript engines on web browsers, but don't use __proto__ , it's just for backward compatibility with old SpiderMonkey-specific code.) ([[Prototype]]是一个对象的内部字段,引用它的原型。这可以通过Web浏览器上的JavaScript引擎上的Object.getPrototypeOf [或__proto__访问,但不要使用__proto__ ,它只是为了向后兼容旧的SpiderMonkey特有的码。)

Now suppose we do this: 现在假设我们这样做:

f.charlie = "I'm charlie on f";

All that changes is the f object (new stuff in bold ): 所有改变都是f对象( 粗体的新东西):

+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
        |                                          |
        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 now has its own property, called charlie . f现在有自己的财产,叫做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"

Get processed slightly differently. 处理略有不同。

Let's look at f.charlie first. 我们先来看看f.charlie Here's what the engine does with f.charlie : 这是引擎对f.charlie

  1. Does f have its own property called "charlie" ? f有自己的属性叫"charlie"吗?
  2. Yes; 是; use the value of that property. 使用该属性的值。

Simple enough. 很简单。 Now let's look at how the engine handles f.bar : 现在让我们看一下引擎如何处理f.bar

  1. Does f have its own property called "bar" ? f有自己的属性叫做"bar"吗?
  2. No; 没有; does f have a prototype? f有原型吗?
  3. Yes; 是; does f 's prototype have a property called "bar" ? f的原型是否有一个名为"bar"的属性?
  4. Yes; 是; use the value of that property. 使用该属性的值。

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 . 所以f.charlief.bar之间有很大的区别: f自己的属性叫charlie ,但是有一个名为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. 如果f的原型对象没有名为bar的属性, Object.prototype检查原型对象(在本例中为Object.prototype ),依此类推,直到我们用完原型。

You can test whether a property is an "own" property, btw, using the hasOwnProperty function that all objects have: 您可以使用所有对象具有的hasOwnProperty函数来测试属性是否为“自己的”属性btw:

console.log(f.hasOwnProperty("charlie")); // true
console.log(f.hasOwnProperty("bar"));     // false

Answering your question from the comments: 从评论中回答你的问题:

I make function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;} 我创建function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;} function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;} and then var ilya = new Person('ilya', 'D') how does it resolves the inner name properties? function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;}然后var ilya = new Person('ilya', 'D')它如何解析内部name属性?

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. Person的调用中,它是new Person(...)表达式的一部分, this指的是新生成的对象,它将由new表达式返回。 So when you do this.prop = "value"; 所以当你这样做时this.prop = "value"; , you're putting a property directly on that object, nothing to do with the prototype. ,你把一个属性直接放在那个对象上,与原型无关。

Putting that another way, these two examples result in exactly the same p object: 换句话说,这两个例子产生完全相同p对象:

// 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: 以下是我提到的引用代码的问题:

Issue 1: Returning something from a constructor function: 问题1:从构造函数返回一些东西:

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

99.9999% of the time, you don't want to return anything out of a constructor function. 在99.9999%的时间内,您不希望从构造函数中返回任何内容。 The way the new operation works is: new操作的工作方式是:

  1. A new blank object is created. 创建一个新的空白对象。
  2. It gets assigned a prototype from the constructor's prototype property. 它从构造函数的prototype属性中分配了一个原型。
  3. The constructor is called such that this refers to the new object. 构造函数被调用,使得this指的是新的对象。
  4. 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. 如果构造函数未返回任何内容,或返回除对象之外的其他内容,则new表达式的结果是在步骤1中创建的对象。
  5. If the constructor function returns an object, the result of the new operation is that object instead. 如果构造函数返回一个对象,则new操作的结果是该对象。

So in your case, since console.log doesn't return anything, you just remove the return keyword from your code. 因此,在您的情况下,由于console.log不返回任何内容,您只需从代码中删除return关键字。 But if you used that return xyz(); 但是如果你使用那个return xyz(); construct with a function that returned an object, you'd mess up your constructor function. 使用返回对象的函数构造,你会搞乱你的构造函数。

Issue 2: Calling functions rather than referring to them 问题2:调用函数而不是引用函数

In this code: 在这段代码中:

clog.prototype.alert = alert(text);

you're calling the alert function and assigning the result of it to a property called alert on clog.prototype . 您正在调用 alert功能并将其结果分配给clog.prototype上名为alert的属性。 Since alert doesn't return anything, it's exactly equivalent to: 由于alert不会返回任何内容,因此它完全等同于:

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

...which probably isn't what you meant. ......这可能不是你的意思。 Perhaps: 也许:

clog.prototype.alert = function(text) {
    alert(text);
};

There we create a function and assign a reference to it to the alert property on the prototype. 在那里,我们创建一个函数并将其引用分配给原型上的alert属性。 When the function is called, it will call the standard alert . 调用该函数时,它将调用标准alert

Issue 3: Constructor functions should be initially capped 问题3:构造函数应该初始上限

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 . 这只是样式,但它绝对是标准的:构造函数(函数意味着与new一起使用)应该以大写字母开头,所以Clog而不是clog Again, though, this is just style. 不过,这只是风格。

Adding a clog.alert function would simply a attach static function to the clog object. 添加clog.alert函数只是将一个静态函数附加到clog对象。 It will not be inherited and will not have access to the instance created with new clog(); 它不会被继承,也不会访问使用new clog();创建的实例new clog(); in the alert function. 在警报功能中。

Adding clog.prototype.alert will make the new clog(); 添加clog.prototype.alert将使new clog(); object you create inherit the function, and you will also have access to the instance inside using the this keyword. 您创建的对象继承了该函数,您还可以使用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

Any property added to the constructor will act as a static property that can only be accessed by referring to the constructor object (ie, function) and not using any instance object of it. 添加到构造函数的任何属性都将充当静态属性,只能通过引用构造函数对象(即函数)并且不使用它的任何实例对象来访问它。 Its like a Class property and not an instance property. 它类似于Class属性而不是实例属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM