关于oop:JavaScript私有方法

JavaScript private methods

要用公共方法生成一个javascript类,我将执行如下操作:

1
2
3
4
5
6
7
8
9
function Restaurant() {}

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

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

这样我班的用户就可以:

1
2
3
var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();

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

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

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

但这不应该奏效:

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

我如何将private_stuff定义为私有方法,从而使这两种方法都成立?

我读过道格·克罗克福德的文章几次,但它似乎不像"私有"方法可以被公共方法调用,"特权"方法可以从外部调用。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
    }
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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


使用自调用函数和调用

JavaScript使用原型,并且没有类(或相关的方法)像面向对象的语言。一个javascript开发者需要在javascript中思考。

维基百科引言:

Unlike many object-oriented languages, there is no distinction between
a function definition and a method definition. Rather, the distinction
occurs during function calling; when a function is called as a method
of an object, the function's local this keyword is bound to that
object for that invocation.

使用自调用函数和调用函数调用私有"方法"的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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,因为您可以利用模块加载系统:

1
2
3
4
5
6
7
8
9
10
11
12
13
function MyObject (foo) {
    this._foo = foo;
}

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

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

exports.MyObject = MyObject;

加载文件:

1
2
3
4
5
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)中实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
export default class MyObject {
  constructor (foo) {
    this._foo = foo;
  }

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

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

加载文件:

1
2
3
4
5
import MyObject from './MyObject';

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


在这些情况下,当您有一个公共API,并且您想要私有和公共方法/属性时,我总是使用模块模式。此图案在Yui图书馆内非常流行,详情可在此处找到:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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


这是我创建的用来理解Douglas Crockford在其网站上用javascript的私有成员所建议的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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

}


我想起来了:编辑:事实上,有人链接到了一个相同的解决方案。啊!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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!'


我认为这样的问题一次又一次出现是因为对闭包缺乏理解。在JS中,C损耗是最重要的。每个JS程序员都必须感受到它的本质。

1。首先,我们需要做单独的范围(闭包)。

1
2
3
function () {

}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
    }
}

三。为了让全世界都知道我们的餐厅课程,我们必须把它从关闭处退回。

1
2
3
4
var Restaurant = (function () {
    // Restaurant definition
    return Restaurant
})()

4。最后,我们有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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。此外,这种方法还具有继承和模板化的潜力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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')

我希望这能帮助人们更好地理解这个问题


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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(); }
        ...
    };
})();

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

演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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+''
               + '
<ul>
'

               + '
<li>
Restroom count at '
+ this.name + ' : '+ this.getRestroomCount() + '
</li>
'

               + '
<li>
Food count at '
+ this.name + ' : ' + this.getFoodCount() + '
</li>
'

               + '
<li>
Total restroom count : '
+ this.getTotalRestroomCount() + '
</li>
'

               + '
<li>
Total food count : '
+ this.getTotalFoodCount() + '
</li>
'

               + '
</ul>
'
;
        }
    };

    return  { // Singleton public properties
        create : function(name) {
            return new Restaurant(name);
        },
        printStatus : function() {
          document.body.innerHTML
              += '<hr />'
              + 'Overview'
              + '
<ul>
'

              + '
<li>
Total restroom count : '
+ Restaurant.prototype.getTotalRestroomCount() + '
</li>
'

              + '
<li>
Total food count : '
+ Restaurant.prototype.getTotalFoodCount() + '
</li>
'

              + '
</ul>
'

              + '<hr />';
        }
    };
}();

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();

另见这把小提琴。


所有这些关闭都会让你付出代价。一定要测试速度影响,尤其是在IE中。你会发现你最好使用命名约定。仍然有很多企业网络用户被迫使用IE6…


采取任何符合克罗克福德私人或私人模式的解决方案。例如:

1
2
3
4
5
6
7
8
9
10
function Foo(x) {
    var y = 5;
    var bar = function() {
        return y * x;
    };

    this.public = function(z) {
        return bar() + x * z;
    };
}

在任何情况下,如果攻击者在JS上下文上没有"执行"权限,他就无法访问任何"公共"或"私有"字段或方法。如果攻击者具有该访问权限,他可以执行此一行程序:

1
2
3
eval("Foo =" + Foo.toString().replace(
    /{/,"{ this.eval = function(code) { return eval(code); };"
));

请注意,上述代码是所有构造函数类型隐私的通用代码。这里的一些解决方案会失败,但很明显,几乎所有基于闭包的解决方案都可以用不同的replace()参数像这样被破坏。

执行此操作后,使用new Foo()创建的任何对象都将具有一个eval方法,可以调用该方法来返回或更改构造函数闭包中定义的值或方法,例如:

1
2
3
4
f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");

我能看到的唯一问题是,在只有一个实例并且是在加载时创建的情况下,它将不起作用。但是,没有理由实际定义原型,在这种情况下,攻击者可以简单地重新创建对象而不是构造函数,只要他有传递相同参数的方法(例如,它们是常量或根据可用值计算)。

在我看来,这几乎使克罗克福德的解决方案无济于事。由于"隐私"很容易被打破,他的解决方案的缺点(可读性和可维护性降低、性能降低、内存增加)使得基于"不隐私"原型的方法成为更好的选择。

我通常使用前导下划线来标记__private_protected方法和字段(perl样式),但在javascript中拥有隐私权的想法表明它是一种被误解的语言。

因此,我不同意克罗克福德,除了他的第一句话。

那么,如何在JS中获得真正的隐私呢?在服务器端放置所有需要私有化的内容,并使用JS进行Ajax调用。


模块模式的神化:揭示模块模式

对一个非常健壮的模式的一个简单的小扩展。


如果您希望具有公共函数访问私有函数功能的公共和私有函数的全部范围,请使用如下对象的布局代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function MyObject(arg1, arg2, ...) {
  //constructor code using constructor arguments...
  //create/access public variables as
  // this.var1 = foo;

  //private variables

  var v1;
  var v2;

  //private functions
  function privateOne() {
  }

  function privateTwon() {
  }

  //public functions

  MyObject.prototype.publicOne = function () {
  };

  MyObject.prototype.publicTwo = function () {
  };
}


这个怎么样?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var Restaurant = (function() {

 var _id = 0;
 var privateVars = [];

 function Restaurant(name) {
     this.id = ++_id;
     this.name = name;
     privateVars[this.id] = {
         cooked: []
     };
 }

 Restaurant.prototype.cook = function (food) {
     privateVars[this.id].cooked.push(food);
 }

 return Restaurant;

})();

私有变量查找不可能超出立即函数的范围。没有重复的功能,节省了内存。

缺点是查找私有变量是笨拙的privateVars[this.id].cooked对于类型来说是荒谬的。还有一个额外的"id"变量。


模块模式在大多数情况下都是正确的。但是,如果您有数千个实例,那么类会保存内存。如果保存内存是一个问题,并且您的对象包含少量私有数据,但有很多公共函数,那么您将希望所有公共函数都位于.prototype中以保存内存。

这就是我想到的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var MyClass = (function () {
    var secret = {}; // You can only getPriv() if you know this
    function MyClass() {
        var that = this, priv = {
            foo: 0 // ... and other private values
        };
        that.getPriv = function (proof) {
            return (proof === secret) && priv;
        };
    }
    MyClass.prototype.inc = function () {
        var priv = this.getPriv(secret);
        priv.foo += 1;
        return priv.foo;
    };
    return MyClass;
}());
var x = new MyClass();
x.inc(); // 1
x.inc(); // 2

对象priv包含私有属性。它可以通过公共函数getPriv()访问,但此函数返回false,除非您将其传递给secret,并且这只在主闭包中知道。


以下是迄今为止我最喜欢的关于私有/公共方法/成员和JavaScript中的实例化的内容:

这是一篇文章:http://www.sefol.com/?P=1090

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var Person = (function () {

    //Immediately returns an anonymous function which builds our modules
    return function (name, location) {

        alert("createPerson called with" + name);

        var localPrivateVar = name;

        var localPublicVar ="A public variable";

        var localPublicFunction = function () {
            alert("PUBLIC Func called, private var is :" + localPrivateVar)
        };

        var localPrivateFunction = function () {
            alert("PRIVATE Func called")
        };

        var setName = function (name) {

            localPrivateVar = name;

        }

        return {

            publicVar: localPublicVar,

            location: location,

            publicFunction: localPublicFunction,

            setName: setName

        }

    }
})();


//Request a Person instance - should print"createPerson called with ben"
var x = Person("ben","germany");

//Request a Person instance - should print"createPerson called with candide"
var y = Person("candide","belgium");

//Prints"ben"
x.publicFunction();

//Prints"candide"
y.publicFunction();

//Now call a public function which sets the value of a private variable in the x instance
x.setName("Ben 2");

//Shouldn't have changed this : prints"candide"
y.publicFunction();

//Should have changed this : prints"Ben 2"
x.publicFunction();

jspiddle:http://jsfiddle.net/northkildonan/kopj3dt3/1/


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var TestClass = function( ) {

    var privateProperty = 42;

    function privateMethod( ) {
        alert("privateMethod," + privateProperty );
    }

    this.public = {
        constructor: TestClass,

        publicProperty: 88,
        publicMethod: function( ) {
            alert("publicMethod" );
            privateMethod( );
        }
    };
};
TestClass.prototype = new TestClass( ).public;


var myTestClass = new TestClass( );

alert( myTestClass.publicProperty );
myTestClass.publicMethod( );

alert( myTestClass.privateMethod ||"no privateMethod" );

类似于georebrock,但不太冗长(imho)这样做有什么问题吗?(我哪儿也没见过)

编辑:我意识到这有点无用,因为每个独立的实例化都有自己的公共方法副本,从而破坏了原型的使用。


用匿名函数包装所有代码:那么,所有函数都是私有的,只有附加到window对象的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function(w,nameSpacePrivate){
     w.Person=function(name){
         this.name=name;  
         return this;
     };

     w.Person.prototype.profilePublic=function(){
          return nameSpacePrivate.profile.call(this);
     };  

     nameSpacePrivate.profile=function(){
       return 'My name is '+this.name;
     };

})(window,{});

使用此:

1
2
  var abdennour=new Person('Abdennour');
  abdennour.profilePublic();

小提琴


我更喜欢将私有数据存储在关联的WeakMap中。这允许您将公共方法保留在它们所属的原型上。对于大量的对象来说,这似乎是处理这个问题的最有效的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const data = new WeakMap();

function Foo(value) {
    data.set(this, {value});
}

// public method accessing private value
Foo.prototype.accessValue = function() {
    return data.get(this).value;
}

// private 'method' accessing private value
function accessValue(foo) {
    return data.get(foo).value;
}

export {Foo};

请参阅此答案,以获得一个干净简单的"类"解决方案,该解决方案具有私有和公共接口,并支持组合。


关于这个问题已经有很多答案了,但没有一个符合我的需要。所以我提出了自己的解决方案,我希望它对某些人有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function calledPrivate(){
    var stack = new Error().stack.toString().split("
"
);
    function getClass(line){
        var i = line.indexOf("");
        var i2 = line.indexOf(".");
        return line.substring(i,i2);
    }
    return getClass(stack[2])==getClass(stack[3]);
}

class Obj{
    privateMethode(){
        if(calledPrivate()){
            console.log("your code goes here");
        }
    }
    publicMethode(){
        this.privateMethode();
    }
}

var obj = new Obj();
obj.publicMethode(); //logs"your code goes here"
obj.privateMethode(); //does nothing

正如您所看到的,当在JavaScript中使用此类类时,该系统工作正常。据我所知,上述方法都没有。


这就是我的想法:

需要一类你可以在这里找到的糖代码。还支持受保护的、继承的、虚拟的、静态的东西…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
;( function class_Restaurant( namespace )
{
    'use strict';

    if( namespace["Restaurant" ] ) return    // protect against double inclusions

        namespace.Restaurant = Restaurant
    var Static               = TidBits.OoJs.setupClass( namespace,"Restaurant" )


    // constructor
    //
    function Restaurant()
    {
        this.toilets = 3

        this.Private( private_stuff )

        return this.Public( buy_food, use_restroom )
    }

    function private_stuff(){ console.log("There are", this.toilets,"toilets available") }

    function buy_food     (){ return"food"        }
    function use_restroom (){ this.private_stuff() }

})( window )


var chinese = new Restaurant

console.log( chinese.buy_food()      );  // output: food
console.log( chinese.use_restroom()  );  // output: There are 3 toilets available
console.log( chinese.toilets         );  // output: undefined
console.log( chinese.private_stuff() );  // output: undefined

// and throws: TypeError: Object #<Restaurant> has no method 'private_stuff'

私有函数无法使用模块模式访问公共变量


别那么冗长。它是javascript。使用命名约定。

在ES6课程中工作了多年之后,我最近开始了ES5项目的工作(使用已经非常冗长的RequireJS)。我已经反复讨论过这里提到的所有策略,基本上都归结为使用命名约定:

  • javascript没有像private这样的范围关键字。其他输入javascript的开发人员会预先知道这一点。因此,一个简单的命名约定就足够了。用下划线前缀的简单命名约定解决了私有属性和私有方法的问题。
  • 出于速度方面的考虑,让我们利用原型,但不要变得过于冗长。让我们试着让ES5"类"与其他后端语言中的期望值保持一致(并将每个文件视为类,即使我们不需要返回实例)。
  • 让我们用更实际的模块情况来演示(我们将使用旧的ES5和旧的RequireJ)。
  • MY-ToLTIPS.JS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
        define([
            'tooltip'
        ],
        function(
            tooltip
        ){

            function MyTooltip() {
                // Later, if needed, we can remove the underscore on some
                // of these (make public) and allow clients of our class
                // to set them.
                this._selector ="#my-tooltip"
                this._template = 'Hello from inside my tooltip!';
                this._initTooltip();
            }

            MyTooltip.prototype = {
                constructor: MyTooltip,

                _initTooltip: function () {
                    new tooltip.tooltip(this._selector, {
                        content: this._template,
                        closeOnClick: true,
                        closeButton: true
                    });
                }
            }

            return {
                init: function init() {
                   new MyTooltip();  // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
                }

                // You could instead return a new instantiation,
                // if later you do more with this class.
                /*
                create: function create() {
                   return new MyTooltip();
                }
                */

            }
        });


    既然每个人都在这里发布自己的代码,我也会这么做…

    我喜欢Crockford,因为他在JavaScript中引入了真正的面向对象模式。但他也提出了一个新的误会,"那个"误会。

    那他为什么用"那个=这个"?它完全与私有功能无关。它与内部功能有关!

    因为根据Crockford的说法,这是车码:

    1
    2
    3
    4
    5
    6
    Function Foo( ) {
        this.bar = 0;
        var foobar=function( ) {
            alert(this.bar);
        }
    }

    所以他建议这样做:

    1
    2
    3
    4
    5
    6
    7
    Function Foo( ) {
        this.bar = 0;
        that = this;
        var foobar=function( ) {
            alert(that.bar);
        }
    }

    所以,正如我所说的,我很确定克罗克福德是错的,他对这个和这个的解释是错误的(但他的代码肯定是正确的)。或者他只是在愚弄javascript世界,想知道谁在复制他的代码?我不知道……我不是浏览器怪人;D

    编辑

    啊,这就是为什么:在javascript中,"var that=this";是什么意思?

    所以克罗基的解释是错的……但他的代码是对的,所以他仍然是一个伟大的人。:)


    一般来说,我将私有对象临时添加到对象中。您必须在方法的"Power构造函数"中公开隐私。如果从原型调用该方法,您将能够覆盖原型方法

    • 在"power constructor"中使公共方法可访问:(ctx是对象上下文)

      1
      ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
    • 现在我有了这个开放的隐私:

      1
      2
      3
      4
      5
      6
      7
      8
      GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
          return function() {
              ctx._ = _;
              var res = clss[func].apply(ctx, arguments);
              ctx._ = null;
              return res;
          };
      };

    您必须在实际的构造函数函数周围放置一个闭包,在那里您可以定义您的私有方法。要通过这些私有方法更改实例的数据,您必须将这些实例作为函数参数或通过使用.apply调用此函数来"this"(this):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    var Restaurant = (function(){
        var private_buy_food = function(that){
            that.data.soldFood = true;
        }
        var private_take_a_shit = function(){
            this.data.isdirty = true;  
        }
        // New Closure
        function restaurant()
        {
            this.data = {
                isdirty : false,
                soldFood: false,
            };
        }

        restaurant.prototype.buy_food = function()
        {
           private_buy_food(this);
        }
        restaurant.prototype.use_restroom = function()
        {
           private_take_a_shit.call(this);
        }
        return restaurant;
    })()

    // TEST:

    var McDonalds = new Restaurant();
    McDonalds.buy_food();
    McDonalds.use_restroom();
    console.log(McDonalds);
    console.log(McDonalds.__proto__);


    我创建了一个新的工具,允许您在原型上使用真正的私有方法https://github.com/tremaynechrist/protectjs

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    var MyObject = (function () {

      // Create the object
      function MyObject() {}

      // Add methods to the prototype
      MyObject.prototype = {

        // This is our public method
        public: function () {
          console.log('PUBLIC method has been called');
        },

        // This is our private method, using (_)
        _private: function () {
          console.log('PRIVATE method has been called');
        }
      }

      return protect(MyObject);

    })();

    // Create an instance of the object
    var mo = new MyObject();

    // Call its methods
    mo.public(); // Pass
    mo._private(); // Fail


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    Class({  
        Namespace:ABC,  
        Name:"ClassL2",  
        Bases:[ABC.ClassTop],  
        Private:{  
            m_var:2  
        },  
        Protected:{  
            proval:2,  
            fight:Property(function(){  
                this.m_var--;  
                console.log("ClassL2::fight (m_var)" +this.m_var);  
            },[Property.Type.Virtual])  
        },  
        Public:{  
            Fight:function(){  
                console.log("ClassL2::Fight (m_var)"+this.m_var);  
                this.fight();  
            }  
        }  
    });

    https://github.com/nooning/jclass


    我知道有点晚了,但是这个怎么样?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var obj = function(){
        var pr ="private";
        var prt = Object.getPrototypeOf(this);
        if(!prt.hasOwnProperty("showPrivate")){
            prt.showPrivate = function(){
                console.log(pr);
            }
        }    
    }

    var i = new obj();
    i.showPrivate();
    console.log(i.hasOwnProperty("pr"));