JavaScript 私有方法

IT技术 javascript oop private-methods
2021-01-30 11:34:02

要使用公共方法创建 JavaScript 类,我会执行以下操作:

function Restaurant() {}

Restaurant.prototype.buy_food = function(){
   // something here
}

Restaurant.prototype.use_restroom = function(){
   // something here
}

这样,我班的用户可以:

var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();

如何创建一个可以被buy_fooduse_restroom方法调用但不能被类的用户从外部调用的私有方法

换句话说,我希望我的方法实现能够做到:

Restaurant.prototype.use_restroom = function() {
   this.private_stuff();
}

但这不应该起作用:

var r = new Restaurant();
r.private_stuff();

我如何定义private_stuff为私有方法,以便这两个都成立?

我已经读了几次Doug Crockford 的文章,但似乎“私有”方法不能被公共方法调用,而“特权”方法可以从外部调用。

6个回答

你可以这样做,但缺点是它不能成为原型的一部分:

function Restaurant() {
    var myPrivateVar;

    var private_stuff = function() {  // Only visible inside Restaurant()
        myPrivateVar = "I can set this here!";
    }

    this.use_restroom = function() {  // use_restroom is visible to all
        private_stuff();
    }

    this.buy_food = function() {   // buy_food is visible to all
        private_stuff();
    }
}
@mikesamuel - 是的,但仅当这些解释器中有错误时:)
2021-03-23 11:34:02
如果你让一个对象 McDonalds() 从 Restaurant() 继承,如果你以这种方式声明它们,麦当劳就不能覆盖私有方法。嗯,实际上你可以,但是如果其他方法调用私有方法,它将不起作用,它将调用该方法的原始版本,您也不能调用父方法。到目前为止,我还没有找到一种很好的方法来声明与继承配合得很好的私有方法。这和性能影响使得这根本不是一个很好的设计模式。我建议使用某种前缀来表示私有方法,例如以下划线开头。
2021-03-23 11:34:02
私有方法不应该被覆盖——它们是私有的。
2021-03-27 11:34:02
将薄件藏在封闭件内并不能保证所有口译员的隐私。请参阅code.google.com/p/google-caja/wiki/...
2021-04-05 11:34:02
这是一个私有方法好吧,但是会比通常的原型方法消耗更多的内存,特别是如果你要创建很多这样的对象。对于每个对象实例,它都会创建一个绑定到对象而不是类的单独函数。此外,在对象本身被销毁之前,这不会被垃圾收集。
2021-04-07 11:34:02

使用自调用函数并调用

JavaScript 使用原型并且没有像面向对象语言那样的类(或与此相关的方法)。JavaScript 开发人员需要在 JavaScript 中思考。

维基百科引用:

与许多面向对象的语言不同,函数定义和方法定义之间没有区别。相反,区别发生在函数调用期间;当函数作为对象的方法被调用时,该函数的本地 this 关键字将绑定到该对象以进行该调用。

使用自调用函数调用函数调用私有“方法”的解决方案:

var MyObject = (function () {
    
  // Constructor
  function MyObject(foo) {
    this._foo = foo;
  }

  function privateFun(prefix) {
    return prefix + this._foo;
  }
    
  MyObject.prototype.publicFun = function () {
    return privateFun.call(this, ">>");
  }
    
  return MyObject;

}());
var myObject = new MyObject("bar");
myObject.publicFun();      // Returns ">>bar"
myObject.privateFun(">>"); // ReferenceError: private is not defined

呼叫功能可以让我们调用私有函数与适当的上下文(this)。

使用 Node.js 更简单

如果您使用的是Node.js,则不需要IIFE,因为您可以利用module加载系统

function MyObject(foo) {
  this._foo = foo;
}
    
function privateFun(prefix) {
  return prefix + this._foo;
}

MyObject.prototype.publicFun = function () {
  return privateFun.call(this, ">>");
}
    
module.exports= MyObject;

加载文件:

var MyObject = require("./MyObject");
    
var myObject = new MyObject("bar");
myObject.publicFun();      // Returns ">>bar"
myObject.privateFun(">>"); // ReferenceError: private is not defined

(新!)未来 JavaScript 版本中的原生私有方法

TC39私有方法和 JavaScript 类的 getter/setter提案处于第 3 阶段。这意味着很快,JavaScript 将本地实现私有方法!

请注意,现代 JavaScript 版本中已经存在JavaScript 私有类字段

下面是一个如何使用它的例子:

class MyObject {

  // Private field
  #foo;
    
  constructor(foo) {
    this.#foo = foo;
  }

  #privateFun(prefix) {
   return prefix + this.#foo;
  }
    
  publicFun() {
    return this.#privateFun(">>");
  }

}

您可能需要JavaScript 转译器/编译器才能在旧的 JavaScript 引擎上运行此代码。

PS:如果您想知道为什么要使用#前缀,请阅读此.

(已弃用)带有绑定运算符的 ES7

警告:绑定运算符 TC39 命题已接近死亡https://github.com/tc39/proposal-bind-operator/issues/53#issuecomment-374271822

bind 操作符::是一个 ECMAScript提议在 Babel阶段 0)中实现。

export default class MyObject {
  constructor (foo) {
    this._foo = foo;
  }

  publicFun () {
    return this::privateFun(">>");
  }
}

function privateFun (prefix) {
  return prefix + this._foo;
}
@YvesM。问题的关键是选择模拟它的最佳模式。
2021-03-14 11:34:02
@janje 嗯?这就是问题的关键,f()javascript 中没有私有(目前没有)。
2021-03-20 11:34:02
@changed 拥有隐藏功能(不可从外部调用)、漂亮的语法(无.call)和小内存(实例上无功能)的最佳折衷是什么那真的存在吗?
2021-03-30 11:34:02
@TaylorMac 除了.call部分。
2021-04-02 11:34:02
这是最好的答案。私有方法的好处,没有垃圾。
2021-04-06 11:34:02

您可以像这样模拟私有方法:

function Restaurant() {
}

Restaurant.prototype = (function() {
    var private_stuff = function() {
        // Private code here
    };

    return {

        constructor:Restaurant,

        use_restroom:function() {
            private_stuff();
        }

    };
})();

var r = new Restaurant();

// This will work:
r.use_restroom();

// This will cause an error:
r.private_stuff();

有关此技术的更多信息,请访问:http : //webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html

使用@georgebrock 提出的模式,所有私人数据将在所有餐厅对象之间共享这类似于基于类的 OOP 中的静态私有变量和函数。我假设OP不会想要这个。为了说明我的意思,我创建了一个 jsFiddle
2021-03-21 11:34:02
我还建议 Douglas Crockford 的网站作为私有/公共方法和成员的资源javascript.crockford.com/private.html
2021-03-29 11:34:02
这应该是解决方案和公认的答案,因为作者显然在使用原型属性。
2021-03-31 11:34:02
此方法的缺点是您不能让 private_stuff() 访问 Restaurant 中的其他私有数据,并且其他 Restaurant 方法无法调用 private_stuff()。好处是,如果您不需要我刚刚提到的任何一个条件,您可以将 use_restroom() 保留在原型中。
2021-04-01 11:34:02
他在问题中提到了该链接。
2021-04-08 11:34:02

在这些情况下,当您拥有公共 API,并且想要私有和公共方法/属性时,我总是使用module模式。这种模式在 YUI 库中很流行,详细信息可以在这里找到:

http://yuiblog.com/blog/2007/06/12/module-pattern/

这真的很简单,其他开发人员也很容易理解。举个简单的例子:

var MYLIB = function() {  
    var aPrivateProperty = true;
    var aPrivateMethod = function() {
        // some code here...
    };
    return {
        aPublicMethod : function() {
            aPrivateMethod(); // okay
            // some code here...
        },
        aPublicProperty : true
    };  
}();

MYLIB.aPrivateMethod() // not okay
MYLIB.aPublicMethod() // okay
删除 () 部分,您就有了一个“类”。至少在那里您可以实例化具有不同状态的不同实例。module模式是相当内存密集型的,虽然......
2021-03-14 11:34:02
但这不是一个类,所以你不能有两个不同状态的“实例”。
2021-03-21 11:34:02
像这样从私有函数调用公共变量有什么缺点aPrivateMethod = function() { MYLIB.aPublicProperty}吗?
2021-03-30 11:34:02
@DevAntoine 查看 26 个回答中 17 个的评论。在 JavaScript 中,可扩展类和私有方法并不容易齐头并进。在这种情况下,我的建议是使用组合而不是继承。使用与封闭的具体对象相同的方法创建可扩展原型。然后在原型内部,您可以决定何时调用具体对象上的方法。
2021-03-31 11:34:02
IDE 的自动完成功能不会检测到这种事情:(
2021-04-01 11:34:02

这是我创建的类,以了解 Douglas Crockford 在他的网站Private Members in JavaScript 中的建议

function Employee(id, name) { //Constructor
    //Public member variables
    this.id = id;
    this.name = name;
    //Private member variables
    var fName;
    var lName;
    var that = this;
    //By convention, we create a private variable 'that'. This is used to     
    //make the object available to the private methods. 

    //Private function
    function setFName(pfname) {
        fName = pfname;
        alert('setFName called');
    }
    //Privileged function
    this.setLName = function (plName, pfname) {
        lName = plName;  //Has access to private variables
        setFName(pfname); //Has access to private function
        alert('setLName called ' + this.id); //Has access to member variables
    }
    //Another privileged member has access to both member variables and private variables
    //Note access of this.dataOfBirth created by public member setDateOfBirth
    this.toString = function () {
        return 'toString called ' + this.id + ' ' + this.name + ' ' + fName + ' ' + lName + ' ' + this.dataOfBirth; 
    }
}
//Public function has access to member variable and can create on too but does not have access to private variable
Employee.prototype.setDateOfBirth = function (dob) {
    alert('setDateOfBirth called ' + this.id);
    this.dataOfBirth = dob;   //Creates new public member note this is accessed by toString
    //alert(fName); //Does not have access to private member
}
$(document).ready()
{
    var employee = new Employee(5, 'Shyam'); //Create a new object and initialize it with constructor
    employee.setLName('Bhaskar', 'Ram');  //Call privileged function
    employee.setDateOfBirth('1/1/2000');  //Call public function
    employee.id = 9;                     //Set up member value
    //employee.setFName('Ram');  //can not call Private Privileged method
    alert(employee.toString());  //See the changed object

}
此外,每个实例都会有私有函数的完整副本,效率低下。最重要的是公共方法无法访问私有类变量,这让我想切换到 dart。不幸的是,angulardart 是超级测试版。
2021-03-17 11:34:02
that=this 是一种很常见的模式,由前面提到的 Crockford 在他的着作“Javascript:好的部分”中推广
2021-03-20 11:34:02
thatthis当函数绑定到不同的对象时,使用它而不是避免作用域问题。在这里,您存储thisthat永不再使用它这是一样的,在所有没有这样做。你应该改变thisthat所有跨Constructor内部函数(而不是方法声明)。如果以某种方式employeeapplyed 或called 这些方法可能会抛出,因为它们this会被错误地绑定。
2021-03-20 11:34:02