如何使用Object.create在JavaScript中继承私有变量

3

我有这个:

function Book (){
  this.width = 7;
  this.height = 10;
  var pages = 100;

  this.tear_page = function(){
    return --pages;
  }
}
function TechBook () {
  var pages = 50;
  this.open_book = function(){ return "Opened in page "+(pages/2); };
  return this;
}
var bBook = Object.create(new Book(), new TechBook());


console.log(bBook);
console.log(bBook.tear_page());
console.log(bBook.open_book());

我无法让它正常工作。我已经成功让TechBook从Book继承了访问本地/私有变量的权限,但只能从Book函数中访问。如果我添加新方法或覆盖它们,它们就无法再访问那些变量了。我想知道是否有一种方法可以仍然从子类的方法中访问这些变量,并在子类中创建新的私有变量。

如果这不可能,那就意味着如果你想要继承,就不能拥有私有变量,反之亦然。顺便说一句,我知道chrome现在(多亏了ES6)可以自然地实现类:class TechBook extends Book(){},像许多其他语言一样,但是由于支持限制目前仅适用于最新版本的chrome...我想知道是否有其他解决此问题的方法。


5
你试图将古典概念应用于JavaScript,并且它们在这里并不适用。在JavaScript中,不存在"public"或"private"的概念,只有闭包。 - Paul S.
1
虽然闭包可以用来模拟“隐私”,但据我所知,没有办法拥有“受保护”的成员。这确实是你想要的。私有成员永远不会被继承,在Java/C#或任何其他面向对象的语言中都不起作用。最后一件事,ES6类也没有受保护的成员。 - Wiktor Zychla
你的 bBook = Object.create(new Book(), new TechBook()) 代码完全混乱了。请参见这里正确地进行继承(然后使用 bBook = new TechBook(); 进行实例化)。 - Bergi
@Vandervals:你已经覆盖了访问私有变量的公共方法... - Bergi
带有继承的编程语言永远不会授予您对私有变量的访问权限。私有意味着在它们所处的上下文中是私有的,没有人可以在该上下文之外访问它。 - ecarrizo
3个回答

5

在任何语言中,私有属性都不能被继承,只能继承受保护或公共的属性。

Javascript 中不存在这个概念,但是当创建一个对象时,可以模拟它(属性为公共的,范围为私有的)。

解决方法可以是添加一个属性,执行返回对象作用域的私有变量/函数的函数。

如果公开了一个返回私有对象的方法,则可以修改它,因为您具有返回的引用。

我喜欢这样做:

var SomeObject = function() {
    //private var one, can't access it outside of this scope
    var one = 1;

    /*private object anotherObject, can't access it directly
     but if you return it with a public function you can modify it.*/
    var anotherObject = {
        a : 'somevalue'
    };

    //public prop two, can access it outside of this scope.
    this.two = 2;

    //public method getOne, you can access it.
    this.getOne = function() {
       return one;
    };

    /* return private var anotherObject */
    this.getAnotherObject = function() {
       return anotherObject;
    };
};

var someObject = new SomeObject();
console.log(someObject.two); // print in console 2
console.log(someObject.getOne()); // print in console 1

var referencedObject = someObject.getAnotherObject();
console.log(referencedObject);
referencedObject.a = 'anotherValue';

console.log(someObject.getAnotherObject());

Fiddle


如果您使用一个_Object_来保存one并传递该_Object_,那么您也可以在外部修改它。 - Paul S.
这很有趣,但我的问题是关于继承,为什么子类可以访问私有变量,但只能从“父类”的方法中访问。您是否愿意扩展您的答案来考虑这一点,然后我再接受一个答案? - Vandervals

2
这是一个关于如何通过知道“密钥”传递数据的示例。
function Book(secret) {
    secret = secret || {};
    var env = {}; // `env` to hold data requiring `secret`
    this.width = 7;
    this.height = 10;
    env.pages = 100;
    this.getEnv = function (s) { // give access to `env` if you know the secret
        if (s === secret) return env;
    };
    this.tear_page = function () {
        return --env.pages;
    };
}

function TechBook(secret) {
    secret = secret || {};
    Book.call(this, secret); // construct from Book
    var env = this.getEnv(secret); // get references via secret

    this.open_book = function () {
        return "Opened in page " + (env.pages/2);
    };
}

TechBook.prototype = Object.create(Book.prototype); // set up inheritance

使用对象引用作为密钥比使用原始类型更安全,因为您需要原始引用才能访问。

现在您有

var bBook = new TechBook();
console.log(bBook); // instance of TechBook
console.log(bBook.tear_page()); // 99
console.log(bBook.open_book()); // Opened in page 49.5

1

涉及原型继承基础知识Object.create属性参数。

根据您的示例实现。

function Book (){
  this.width = 7;
  this.height = 10;
  this.pages = 100;

  this.tear_page = function(){
    return --this.pages;
  }
  this.init = function() {
    return this
  }
}

Book.prototype = {
  open_book: function(){ return "Opened in page "+(this.pages/2) }
}


var bBook = Object.create(new Book(), {pages: { value: 50 } }).init();


console.log( new Book())              // { width: 7, height: 10, pages: 100, tear_page: [Function], init: [Function] }
console.log( bBook )                  //{}
console.log( bBook.width )            //->7              
console.log( bBook.height )           //-> 10             
console.log( bBook.pages )            // -> 50             
console.log( bBook.tear_page())       //-> 49
console.log(bBook.open_book())       //-> Opened in page 25

问题在于当您创建bBook时,您并没有创建一个新类,而是自动创建了一个新实例。是否可能创建一个TechBook类,在实例创建时返回Object.create(new Book(), {pages: {value:50} });? - Vandervals
在Book函数中添加一个init方法,并在创建继承对象时返回该实例。我已经更新了我的答案中的代码。 - aarti

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