Getter / Setter 和原型链

8
在一个当前使用ES6类语法和get/set语法的JavaScript项目中,我遇到了一个无法解释的行为。
首先,这里是一个正常工作的演示:
class A {
    constructor() {
        this.__value = null;
    }

    get value() {
        return this.__value;
    }

    set value(value) {
        this.__value = value;
    }
}

class B extends A { }

let b = new B();
b.value = 2;
console.log(b.value); // output: 2

设置和获取在A.prototype中定义的b.value是有效的。

现在考虑以下演示,我将setter从A移到B:

class A {
    constructor() {
        this.__value = null;
    }

    get value() {
        return this.__value;
    }
}

class B extends A {
    set value(value) {
        this.__value = value;
    }
}

let b = new B();
b.value = 2;          // b.__value is 2
console.log(b.value); // output: undefined

设置值是有效的(因为b.__value确实是2),但getter似乎不存在,尽管它仍然在A.prototype中定义。

这里的问题是什么?


在这种明确的情况下,那条评论一点也不有用。 - cbettinger
很抱歉没有帮到您,如果您愿意的话,可以继续在问题中说类似“B的原型A”的不正确的内容,我只是试图让您了解类和原型确实不是同一回事。 - Kaddath
那个不太准确,谢谢。我已经修复了。 - cbettinger
1个回答

4

当你试图检索一个属性时,如果该实例上不存在该属性,则引擎会沿着原型链查找第一个具有该属性描述符的对象。当找到该描述符时,如果它具有getter方法,则调用该getter方法。否则,如果没有getter方法,则将检索该属性的普通值(如果有)。

在第二种情况下,属性描述符位于B.prototype上。但是B.prototype没有value的getter方法(也没有普通值)! 因此,返回undefined

如果B.prototype具有value的getter方法,则会被调用:

'use strict';

class A {
    constructor() {
        this.__value = null;
    }

    get value() {
        return this.__value;
    }
}

class B extends A {
    set value(value) {
        this.__value = value;
    }
    get value() {
        console.log('trying to get value');
    }
}

let b = new B();
b.value = 2;
b.value;

但它并没有。引擎不会在原型链上不断查找getter,相反,它将停止并返回undefined,因为在原型链中第一个对象上没有找到具有hasOwnProperty('value')的getter(或纯值)。

如果你有一个getter,并且想要能够设置相同的属性,则setter必须与getter位于同一对象上,反之亦然。


所以这里的问题是,原型链中属性描述符的查找没有区分get和set。尽管我试图获取b.value,但查找停止在B.prototype(因为有一个set属性描述符),但预期的get属性描述符不在那里。我理解。奇怪的JS :) - cbettinger
@cbettinger,可能听起来有些奇怪,但你必须意识到ES6类语法只是大多数其他语言中类的模拟。我有时更喜欢直接使用原型,当你理解它时,它真的非常强大。 - Kaddath

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接