将ES6类getter设置为可枚举

lig*_*h05 25 javascript ecmascript-6 babeljs es2015

我有一个带有getter属性的ES6类(用babeljs反编译).我知道默认情况下这些属性不可枚举.但是,我不明白为什么我无法使用可枚举的属性Object.defineProperty

// Declare class
class Person {
  constructor(myName) {
    this.name = myName;
  }

  get greeting() {
    return `Hello, I'm ${this.name}`;
  }
}

// Make enumerable (doesn't work)
Object.defineProperty(Person, 'greeting', {enumerable: true});

// Create an instance and get enumerable properties
var person = new Person('Billy');
var enumerableProperties = Object.keys(person);
// => ['name']
Run Code Online (Sandbox Code Playgroud)

Plunker示例

Pau*_*aul 39

ES6风格的getter是在原型上定义的,而不是在每个人身上person.要将greeting属性设置为可枚举,您需要更改:

// Make enumerable (doesn't work)
Object.defineProperty(Person, 'greeting', {enumerable: true});
Run Code Online (Sandbox Code Playgroud)

至:

// Make enumerable
Object.defineProperty(Person.prototype, 'greeting', {enumerable: true});
Run Code Online (Sandbox Code Playgroud)

Object.keys只返回该对象拥有的可枚举属性,因此不返回原型上的属性.你会发现greeting在财产Object.keys( Object.getPrototypeOf( person ) ),或在对...的循环.更新了Plunker

相反,如果您希望Person的每个实例都有自己的实例,greeting您可以在构造函数中定义它:

class Person {
  constructor(myName) {
    this.name = myName;

    Object.defineProperty( this, 'greeting', {
      enumerable: true,
      get: function ( ) { return `Hello, I'm ${this.name}`; }
    } );
  }
}
Run Code Online (Sandbox Code Playgroud)

更新了Plunker

  • 这有效但哇,不直观,甚至不符合逻辑 (4认同)
  • @SeanVieira不,你不需要.`defineProperty`也可用于修改/配置现有属性.只要你没有指定`value`或新的`get`,设置enumerable就不会阻止旧的`get`工作.(请注意我在第一个关于我的答案的链接器中,'person [name]`正确地返回问候语). (3认同)

Кон*_*Ван 9

什么是班级?

类的非静态方法和访问器位于类的原型上,以便它的每个实例都继承它们。您可以通过实例访问它们,但它们不是实例自己的属性。静态方法和访问器位于类(它是一个函数)本身。

class Test {
	#private_field = "A private field.";
	public_field = "A public field.";
	static get static_getter() {
		return "A static getter.";
	}
	static static_method() {
		return "A static method.";
	}
	get getter() {
		return "A non-static getter.";
	}
	method() {
		return "A non-static method.";
	}
}

console.log(`Class ("${typeof Test}" type)`, Object.getOwnPropertyDescriptors(Test));
console.log("Its prototype", Object.getOwnPropertyDescriptors(Test.prototype));
console.log("Its instance", Object.getOwnPropertyDescriptors(new Test));
Run Code Online (Sandbox Code Playgroud)

Class ("function" type) {
    "length": {
        "value": 0,
        "writable": false,
        "enumerable": false,
        "configurable": true
    },
    "prototype": {
        "value": {……},
        "writable": false,
        "enumerable": false,
        "configurable": false
    },
    "static_getter": {
        "get": ƒ static_getter() {……},
        "set": undefined,
        "enumerable": false,
        "configurable": true
    },
    "static_method": {
        "value": ƒ static_method() {……},
        "writable": true,
        "enumerable": false,
        "configurable": true
    },
    "name": {
        "value": "Test",
        "writable": false,
        "enumerable": false,
        "configurable": true
    }
}
Run Code Online (Sandbox Code Playgroud)
Its prototype {
    "constructor": {
        "value": class Test {……},
        "writable": true,
        "enumerable": false,
        "configurable": true
    },
    "getter": {
        "get": ƒ getter() {……},
        "set": undefined,
        "enumerable": false,
        "configurable": true
    },
    "method": {
        "get": ƒ method() {……},
        "writable": true,
        "enumerable": false,
        "configurable": true
    }
}
Run Code Online (Sandbox Code Playgroud)
Its instance {
    "public_field": {
        "value": "A public field",
        "writable": true,
        "enumerable": true,
        "configurable": true
    }
}
Run Code Online (Sandbox Code Playgroud)

如何设置可枚举的属性

您可以使用可枚举的非静态访问器(原型上的属性)Object.defineProperty。

class Person {
    constructor(name) {
        this.name = name;
    }
    get greeting() {
        return `Hello from ${this.name}.`;
    }
}
for(const property of ["greeting"]) {
    Object.defineProperty(Person.prototype, property, {enumerable: true});
}
Run Code Online (Sandbox Code Playgroud)

不过这样一来它会大多是没有用的,因为大多数的有用的功能,如Object.keys,Object.values,Object.entries,JSON.stringify,等等,别只追求自己的对象的属性。


将原型上的属性归结为实例

您还可以将原型上的(复制)属性带到实例中。这样,它们不再从原型继承属性,而是将它们作为自己的属性。

Class ("function" type) {
    "length": {
        "value": 0,
        "writable": false,
        "enumerable": false,
        "configurable": true
    },
    "prototype": {
        "value": {……},
        "writable": false,
        "enumerable": false,
        "configurable": false
    },
    "static_getter": {
        "get": ƒ static_getter() {……},
        "set": undefined,
        "enumerable": false,
        "configurable": true
    },
    "static_method": {
        "value": ƒ static_method() {……},
        "writable": true,
        "enumerable": false,
        "configurable": true
    },
    "name": {
        "value": "Test",
        "writable": false,
        "enumerable": false,
        "configurable": true
    }
}
Run Code Online (Sandbox Code Playgroud)


将每个非静态 getter 归结为实例,枚举它们。

const prototype = Object.getPrototypeOf(this);
const prototype_property_descriptors = Object.getOwnPropertyDescriptors(prototype);
for(const [property, descriptor] of Object.entries(prototype_property_descriptors)) {
    const is_nonstatic_getter = (typeof descriptor.get === "function");
    if(is_nonstatic_getter) {
        descriptor.enumerable = true;
        Object.defineProperty(this, property, descriptor);
    }
}
Run Code Online (Sandbox Code Playgroud)