当前位置:  开发笔记 > 编程语言 > 正文

JavaScript私有方法

如何解决《JavaScript私有方法》经验,为你挑选了9个好方法。

要使用公共方法创建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的几次写法,但看起来似乎不能通过公共方法调用"私有"方法,并且可以在外部调用"特权"方法.



1> 17 of 26..:

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

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 - 是的,但只有当那些翻译有bug时:)
将封闭物藏在封闭物内并不能保证所有口译员的隐私.请参阅http://code.google.com/p/google-caja/wiki/EvalBreaksClosureEncapsulation
如果你把一个对象麦当劳()从餐厅(),如果你用这种方式宣告他们麦当劳不能重写私有方法继承.嗯,其实你可以,但它不会工作,如果其他方法调用私有,它会调用该方法的原始版本,你也不能调用父类的方法.到目前为止,我还没有找到一种很好的方法来声明适用于继承的私有方法.这和性能的影响使得这根本不是一个非常好的设计模式.我建议做一些前缀来表示私有方法,比如用下划线开始.
如果你想要私有方法(不是私有数据),那么简单地在原型之外声明它们并像这样调用它们:myFoo.apply(this,args).然后,您可以保持公共API小,同时仍然具有适用于您的本地对象的私有方法.
请考虑使用**自调用函数**+**调用**,如下所述:http://stackoverflow.com/a/25172901/1480391

2> georgebrock..:

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

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提出的模式,*所有*私有数据将在*all*restaurant对象之间共享.这类似于基于类的OOP中的静态私有变量和函数.我认为OP确实*不*想要这个.为了说明我的意思,我创建了[a jsFiddle](http://jsfiddle.net/feklee/zRLSh/1/).
他在问题中提到了这个链接.
这种方法的缺点是你不能让private_stuff()访问Restaurant中的其他私有数据,而其他Restaurant方法也不能调用private_stuff().好处是如果你不需要我提到的任何一个条件,你可以在原型中保留use_restroom().
我还建议Douglas Crockford的网站作为私人/公共方法的资源和成员http://javascript.crockford.com/private.html
这应该是解决方案和接受的答案,因为作者显然正在使用prototype属性.
@feklee:公共方法被定义为在许多实例之间共享的原型对象的属性.这里我们使用私有方法完全相同:所有实例都可以访问一个共享的`function`对象.你是正确的指出这种技术不适用于私人数据,但这不是问题所要求的.
@georgebrock:从OP接受的答案判断,他确实希望私有成员*每个对象*,而不是原型对象.他发布的用例也暗示了这一点.例如,洗手间的状态通常是每个餐馆.

3> Yves M...:
使用自调用功能和调用

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

维基百科报价:

与许多面向对象的语言不同,函数定义和方法定义之间没有区别.相反,区别发生在函数调用期间; 当一个函数作为一个对象的方法被调用时,该函数的local 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,因为您可以利用模块加载系统:

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

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

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

exports.MyObject = MyObject;

加载文件:

var MyObject = require('./MyObject').MyObject;

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


(实验)ES7与绑定操作员

绑定运算符::是ECMAScript 提议,在Babel(阶段0)中实现.

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

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

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

加载文件:

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function


这是最好的答案.私人方法的好处,没有垃圾.
@YvesM.问题的关键是选择模拟它的最佳模式.

4> 小智..:

在这种情况下,如果你有一个公共API,并且你想要私有和公共方法/属性,我总是使用模块模式.这个模式在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


但这不是一个类,所以你不能拥有不同状态的2个"实例".

5> Sarath..:

这是我创建的类,用于理解Douglas Crockford在其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

}


当函数绑定到不同的对象时,使用`that`代替`this`来避免作用域问题.在这里,你将`this`存储在`that`中,而不再使用它,这与完全没有使用它相同.你应该在`Constructor`内部函数(不是方法声明)中用`that`来改变`this`.如果`employee`是`apply`ed或`call`ed这些方法可能会抛出,因为`this`将被错误地绑定.
那个=这是一个非常普遍的模式,由前面提到的Crockford在他的书"Javascript:The good parts"中推广

6> 小智..:

我想到了这个:编辑:实际上,有人已经链接到相同的解决方案.咄!

var Car = function() {
}

Car.prototype = (function() {
    var hotWire = function() {
        // Private code *with* access to public properties through 'this'
        alert( this.drive() ); // Alerts 'Vroom!'
    }

    return {
        steal: function() {
            hotWire.call( this ); // Call a private method
        },
        drive: function() {
            return 'Vroom!';
        }
    };
})();

var getAwayVechile = new Car();

hotWire(); // Not allowed
getAwayVechile.hotWire(); // Not allowed
getAwayVechile.steal(); // Alerts 'Vroom!'



7> imos..:

我认为这些问题一次又一次地出现,因为对闭包缺乏了解.Сlosures是JS中最重要的事情.每个JS程序员都必须感受到它的本质.

1.首先我们需要独立的范围(关闭).

function () {

}

2.在这方面,我们可以做任何我们想做的.没有人会知道它.

function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
}

3.为了让全世界了解我们的餐厅类,我们必须从封闭处归还.

var Restaurant = (function () {
    // Restaurant definition
    return Restaurant
})()

4.最后,我们有:

var Restaurant = (function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
    return Restaurant
})()

5.此外,该方法对于继承和模板潜力

// Abstract class
function AbstractRestaurant(skills) {
    var name
    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return skills && name in skills ? skills[name]() : null
    }
    return Restaurant
}

// Concrete classes
SushiRestaurant = AbstractRestaurant({ 
    sushi: function() { return new Sushi() } 
})

PizzaRestaurant = AbstractRestaurant({ 
    pizza: function() { return new Pizza() } 
})

var r1 = new SushiRestaurant('Yo! Sushi'),
    r2 = new PizzaRestaurant('Dominos Pizza')

r1.getFood('sushi')
r2.getFood('pizza')

我希望这有助于人们更好地理解这个主题


它不起作用.这里的name变量就像静态变量一样,并由Restaurant的所有实例共享.这是jsbin:http://jsbin.com/oqewUWa/2/edit?js,output
你在第4点有什么了不起的!我认为这是所有这些中唯一的答案,你可以获得在原型上使用方法的性能/内存增益,这些公共方法可以完全访问私有成员.+1
在这里添加我的2c以重申这不起作用,看起来不错,但如上所述,"name"将作为静态变量,即在所有实例之间共享

8> John Slegers..:

就个人而言,我更喜欢以下模式在JavaScript中创建类:

var myClass = (function() {
    // Private class properties go here

    var blueprint = function() {
        // Private instance properties go here
        ...
    };

    blueprint.prototype = { 
        // Public class properties go here
        ...
    };

    return  {
         // Public class properties go here
        create : function() { return new blueprint(); }
        ...
    };
})();

如您所见,它允许您定义类属性和实例属性,每个属性都可以是公共属性和私有属性.


演示

var Restaurant = function() {
    var totalfoodcount = 0;        // Private class property
    var totalrestroomcount  = 0;   // Private class property
    
    var Restaurant = function(name){
        var foodcount = 0;         // Private instance property
        var restroomcount  = 0;    // Private instance property
        
        this.name = name
        
        this.incrementFoodCount = function() {
            foodcount++;
            totalfoodcount++;
            this.printStatus();
        };
        this.incrementRestroomCount = function() {
            restroomcount++;
            totalrestroomcount++;
            this.printStatus();
        };
        this.getRestroomCount = function() {
            return restroomcount;
        },
        this.getFoodCount = function() {
            return foodcount;
        }
    };
   
    Restaurant.prototype = {
        name : '',
        buy_food : function(){
           this.incrementFoodCount();
        },
        use_restroom : function(){
           this.incrementRestroomCount();
        },
        getTotalRestroomCount : function() {
            return totalrestroomcount;
        },
        getTotalFoodCount : function() {
            return totalfoodcount;
        },
        printStatus : function() {
           document.body.innerHTML
               += '

Buying food at '+this.name+'

' + '
    ' + '
  • Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '
  • ' + '
  • Food count at ' + this.name + ' : ' + this.getFoodCount() + '
  • ' + '
  • Total restroom count : '+ this.getTotalRestroomCount() + '
  • ' + '
  • Total food count : '+ this.getTotalFoodCount() + '
  • ' + '
'; } }; return { // Singleton public properties create : function(name) { return new Restaurant(name); }, printStatus : function() { document.body.innerHTML += '
' + '

Overview

' + '
    ' + '
  • Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '
  • ' + '
  • Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '
  • ' + '
' + '
'; } }; }(); var Wendys = Restaurant.create("Wendy's"); var McDonalds = Restaurant.create("McDonald's"); var KFC = Restaurant.create("KFC"); var BurgerKing = Restaurant.create("Burger King"); Restaurant.printStatus(); Wendys.buy_food(); Wendys.use_restroom(); KFC.use_restroom(); KFC.use_restroom(); Wendys.use_restroom(); McDonalds.buy_food(); BurgerKing.buy_food(); Restaurant.printStatus(); BurgerKing.buy_food(); Wendys.use_restroom(); McDonalds.buy_food(); KFC.buy_food(); Wendys.buy_food(); BurgerKing.buy_food(); McDonalds.buy_food(); Restaurant.printStatus();


9> 小智..:

所有这些关闭将花费你.确保测试速​​度影响,尤其是在IE中.您会发现最好使用命名约定.还有很多企业网站用户被迫使用IE6 ......


谁在乎呢?
仍然使用IE6的9%不关心速度,优化和所有现代HTML5功能.所以关闭不会花费任何东西.
现在是0.5%(2012年8月)http://www.w3schools.com/browsers/browsers_explorer.asp
@LorenzoPolidori w3schools用户!==企业网站用户;]
推荐阅读
贾志军
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有