为函数原型调用Object.defineProperty会发生什么?

yeg*_*gao 5 javascript getter prototype get defineproperty

var Foo = function(){};

Object.defineProperty(Foo.prototype,'x',{
    get(){
        return 3;
    }
});

var foo = new Foo();

console.dir(foo);
Run Code Online (Sandbox Code Playgroud)

我正在寻找的结果应该是

Foo {
    __proto__:{
        constructor: ƒ (),
        x: 3,
        __proto__: Object
    }
}
Run Code Online (Sandbox Code Playgroud)

但真正的结果是

Foo {
    x: 3,
    __proto__:{
        constructor: ƒ (),
        x: 3,
        __proto__: Object
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么x属性已经出现在最外层?

ymz*_*ymz -2

发生了什么(逐步)

var Foo = function(){};
Run Code Online (Sandbox Code Playgroud)

定义了一个新函数,名为 - Foo。如果我们使用console.dir(Foo)我们会看到Foo有 2 个特殊成员prototype并且__proto__

Object.defineProperty(Foo.prototype,'x',{
    get(){
        return 3;
    }
}); 
Run Code Online (Sandbox Code Playgroud)

Foo 的原型已更新。关于defineProperty(来自MDN):

静态方法 Object.defineProperty() 直接在对象上定义新属性,或修改对象上的现有属性,然后返回该对象。

因此,prototype现在使用名为 的新成员修改该对象x。这里充当 c'tor 并在创建prototype新实例时“启动”Foo

var foo = new Foo();
Run Code Online (Sandbox Code Playgroud)

创建了 Foo 的一个新实例。c'tor 调用使用 Foo 原型并x应用 getter

备择方案

扩展Foo prototype,从而确保它只会影响从Foo(作为类)创建的对象

Object.defineProperty(Foo.prototype, 'x',
{
    get: () => 3
}); 

console.dir(new Foo().x) // will show 3
console.dir(Foo.x) // undefined - protorype is for class objects
Run Code Online (Sandbox Code Playgroud)

或者扩展Foo __proto__,从而Foo作为函数进行更新,同时不影响从它创建的对象

Object.defineProperty(Foo.__proto__, 'x',
{
    get: () => 3
}); 

console.dir(new Foo().x) // undefined - x is not a member of Foo
console.dir(Foo.x) // 3
Run Code Online (Sandbox Code Playgroud)

奖励:一篇关于 JS 原型以及为什么会发生这种情况的非常好的文章

原答案

发生这种情况是因为在 JS 中function是一种声明函数和类的方法!

所以你的Foo函数也可以用作一个类

var Foo = function(){};
Foo(); // call Foo as a function
var obj = new Foo(); // initiate an instance from class Foo
Run Code Online (Sandbox Code Playgroud)

因为您使用Foo.prototype对象(作为函数),然后从您的Foo类创建一个新实例,所以您保证:

  1. 您的函数原型将使用新的 getter 进行修改
  2. 从类中继承的每个新对象也将使用新的 getter(在对象级别)

我真的认为你的代码应该是这样的:

function Foo ()
{
    Object.defineProperty(this,'x',{
       get(){
           return 3;
       }
    });
}
Run Code Online (Sandbox Code Playgroud)

  • `Foo.__proto__` 只是 `Function.prototype`。正如您所说,添加属性不会影响从 Foo 构造的对象,但您似乎忽略了它会影响每个函数。扩展 `Foo.__proto__` 后尝试 `console.log(function(){}.x);` (2认同)