자바 스크립트 개인 메소드


482

공용 메소드를 사용하여 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_foodand use_restroom메소드 는 호출 할 수 있지만 클래스 사용자는 외부 적 으로 호출 할 수없는 개인용 메소드를 작성하려면 어떻게해야 합니까?

즉, 메소드 구현이 가능할 수 있기를 원합니다.

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

그러나 이것은 작동하지 않아야합니다.

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

private_stuff개인 메소드로 정의 하여 두 가지 모두를 충족 시키려면 어떻게해야 합니까?

Doug Crockford의 글 을 몇 번 읽었 지만 "비공개"메서드는 공용 메서드로 호출 할 수 있고 "비공개"메서드는 외부에서 호출 할 수있는 것처럼 보이지 않습니다.

답변:


403

할 수 있지만 단점은 프로토 타입의 일부가 될 수 없다는 것입니다.

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

9
클로저 내부에 얇은 부분을 숨겨도 모든 통역사의 프라이버시가 보장되는 것은 아닙니다. 참조 code.google.com/p/google-caja/wiki/...
마이크 사무엘에게

51
@mikesamuel-사실, 그러나 그 해석자들이 버그를 가지고있을 때만 :)
jvenema

133
이것은 개인적인 방법은 괜찮지 만 일반적인 프로토 타입 방법보다 더 많은 메모리를 사용하는 경향이 있습니다. 특히 이러한 객체를 많이 만드는 경우에는 더욱 그렇습니다. 모든 객체 인스턴스에 대해 클래스가 아닌 객체에 바인딩 된 별도의 함수를 만듭니다. 또한 객체 자체가 파괴 될 때까지 가비지 수집되지 않습니다.
Arindam

4
McDonalds () 객체를 Restaurant ()에서 상속 받게하는 경우 McDonalds는 이러한 방식으로 선언하면 개인용 메서드를 재정의 할 수 없습니다. 글쎄, 실제로는 할 수 있지만 다른 메소드가 private을 호출하면 작동하지 않으며 원래 버전의 메소드를 호출하며 부모 메소드를 호출 할 수도 없습니다. 지금까지 상속과 잘 작동하는 개인 메서드를 선언하는 좋은 방법을 찾지 못했습니다. 이로 인해 성능에 영향을 미치므로 디자인 패턴이 그리 좋지 않습니다. 밑줄로 시작하는 것과 같은 개인 메서드를 나타내는 일종의 접두사를 사용하는 것이 좋습니다.
Hoffmann

68
개인 메서드는 재정의되지 않아야합니다. 개인 메서드입니다.
17 of 26

163

자체 호출 기능 및 호출 사용

JavaScript는 프로토 타입을 사용하며 객체 지향 언어와 같은 클래스 (또는 해당 문제에 대한 메소드)가 없습니다. JavaScript 개발자는 JavaScript로 생각해야합니다.

Wikipedia 인용문 :

많은 객체 지향 언어와 달리 함수 정의와 메소드 정의는 구별되지 않습니다. 오히려, 함수 호출 중에 구별이 발생합니다. 함수가 객체의 메소드로 호출되면 함수의 로컬 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

34
이것이 가장 좋은 대답입니다. 개인 방법의 장점, 정크 없음.
TaylorMac

1
@TaylorMac .call부분을 제외하고 .
pishpish

1
@janje Huh? 그것은 질문의 요점입니다. f()자바 스크립트 에는 비공개가 없습니다 (현재는 아닙니다 ).
Yves M.

2
@YvesM. 문제의 핵심은 그것을 시뮬레이션하는 가장 좋은 패턴을 선택하는 것입니다.
pishpish

1
@ 숨겨진 함수 (외부에서 호출 할 수 없음 .call) , 멋진 sintax (아니오 ) 및 작은 메모리 (인스턴스에 함수 없음 ) 를 갖는 가장 좋은 타협은 무엇 입니까? 심지어 존재합니까?
Ciprian Tomoiagă

161

다음과 같은 개인용 메소드를 시뮬레이션 할 수 있습니다.

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)를 참조 하십시오.


7
또한 Douglas Crockford의 사이트를 개인 / 공용 메소드 및 멤버에 대한 리소스로 제안합니다. javascript.crockford.com/private.html
Jared

10
그는 질문에서 그 링크를 언급했습니다.
Gulzar Nazim

8
이 방법의 단점은 private_stuff ()가 Restaurant의 다른 개인 데이터에 액세스 할 수없고 다른 Restaurant 메서드는 private_stuff ()를 호출 할 수 없다는 것입니다. 단점은 방금 언급 한 조건 중 하나가 필요하지 않은 경우 프로토 타입에 use_restroom ()을 유지할 수 있다는 것입니다.
26 개 중 26

6
저자가 프로토 타입 속성을 명확하게 사용하고 있기 때문에 이것이 해결책이자 대답이되어야합니다.
가브리엘 라마

23
@georgebrock이 제안한 패턴으로 모든 개인 데이터는 모든 식당 개체 간에 공유 됩니다. 이는 클래스 기반 OOP의 정적 개인 변수 및 함수와 유사합니다. OP가 이것을 원하지 않는다고 가정합니다 . 무슨 뜻인지 설명하기 위해 jsFiddle을 만들었습니다 .
feklee

35

공개 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

이런 종류의 것은 IDE의 자동 완성으로 감지되지 않습니다 :(
Click Upvote

19
그러나 이것은 클래스가 아니므로 다른 상태의 2 가지 "인스턴스"를 가질 수 없습니다.
DevAntoine

() 부분을 제거하면 "클래스"가 있습니다. 적어도 다른 상태로 다른 인스턴스를 인스턴스화 할 수있는 곳. 모듈 패턴은 상당히 메모리 집약적이지만 ...
oligofren

@DevAntoine 답변 26 개 중 17 개에 대한 주석을보십시오. JavaScript에서 확장 가능한 클래스와 개인 메서드는 쉽게 사용할 수 없습니다. 이 경우의 제안은 상속에 대한 구성과 함께하는 것입니다. 동봉 된 콘크리트 객체와 동일한 방법으로 확장 가능한 프로토 타입을 만듭니다. 그런 다음 프로토 타입 내부에서 콘크리트 오브젝트에서 메소드를 호출 할시기를 결정할 수 있습니다.

개인 함수에서 공용 변수를 호출하는 데 단점이 aPrivateMethod = function() { MYLIB.aPublicProperty}있습니까?
Hanna

21

다음은 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

}

5
그 = 이것은 아주 일반적인 패턴으로, 앞서 언급 한 Crockford가 그의 저서 "자바 스크립트 : 좋은 부분"에서 대중화 한 패턴입니다.
oligofren

8
thatthis함수가 다른 오브젝트에 바인드 될 때 범위 지정 문제를 피하기 위해 대신 사용됩니다 . 여기에서 저장하는 thisthat결코 전혀 그 일을하지 동일하다 다시 사용하지 않는다. 당신은 변경해야 thisthat전체의 모든 Constructor내부 기능 (안 방법 선언). 하면 employee된다 apply에드 또는 call어떤 방법으로 에드 이러한 방법은 이후 던질 수있는 this잘못을 준수 할 것입니다.
Maroshii

또한 모든 인스턴스에는 개인 기능의 전체 사본이 비효율적입니다. 공개 메소드가 개인 클래스 변수에 액세스 할 수 없다는 사실 외에도 다트로 전환하고 싶습니다. 불행히도 angulardart는 슈퍼 베타입니다.
Ray Foss

이것에서 "생성자"방법은 어디에 있습니까? 인스턴스화 될 때 클래스의 생성자 메서드에서 일반적으로 실행되는 논리를 어디에 배치합니까?
BadHorsie

13

나는 이것을 혼란스럽게했다 : 편집 : 실제로 누군가가 동일한 솔루션에 연결했습니다. 어이!

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!'

1
이것은 좋은 기술이지만 생성자에서 매개 변수를 어떻게 허용합니까? 예를 들어 속도는 var getAwayVehicle = new Car(100);어디 100이며 속도를 경고하려고합니다. 감사!
Jason

1
알아낼 수 var Car = function(speed) { this.speed = speed; }있고`return {constructor : Car, ...`
Jason

11

클로저에 대한 이해 부족으로 인해 이러한 질문이 반복해서 발생한다고 생각합니다. С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')

이것이 누군가 가이 주제를 더 잘 이해하는 데 도움이되기를 바랍니다.


2
당신이 포인트 4에서 가지고있는 것은 놀라운 것입니다! 프로토 타입에서 메소드를 사용하면 성능 / 메모리 이점을 얻을 수 있으며이 공용 메소드는 개인 멤버에게 완전히 액세스 할 수있는 모든 답변에서 유일한 대답이라고 생각합니다. +1
Hudon

7
작동하지 않습니다. 여기서 이름 변수는 정적 변수처럼 작동하며 모든 Restaurant 인스턴스에서 공유됩니다. 여기 jsbin이 있습니다 : jsbin.com/oqewUWa/2/edit?js,output
Shital Shah

좋은 시도이지만 Shital이 지적했듯이 이름 변수는 버그가 있습니다.
oligofren

2
내 2c를 여기에 추가하면 이것이 작동하지 않는다는 것을 재확인 할 수 있지만 멋지게 보이지만 "name"위에서 지적한 것처럼 정적 변수, 즉 모든 인스턴스에서 공유되는 정적 변수로 사용됩니다.
Paul Carroll

10

개인적으로 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
               += '<h3>Buying food at '+this.name+'</h3>'
               + '<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 />'
              + '<h3>Overview</h3>'
              + '<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();

이 바이올린을 참조하십시오 .


이 날 ES6 클래스를 사용하고 너무 transpiles 무엇을보고 싶어한다
sheriffderek

9

이 폐쇄는 모두 비용이 듭니다. 특히 IE에서 속도 영향을 테스트해야합니다. 명명 규칙을 사용하면 더 나을 것입니다. IE6를 사용해야하는 회사 웹 사용자가 여전히 많이 있습니다 ...


34
누가 진심으로 신경 쓰나요?
nowayyy

17
여전히 IE6을 사용하는 9 %는 속도, 최적화 및 모든 최신 HTML5 기능에 신경 쓰지 않습니다. 폐쇄는 비용이 들지 않습니다.
Gabriel Llamas


7
@LorenzoPolidori w3schools 사용자! == 회사 웹 사용자;]
WynandB

명명 규칙 (예 : 밑줄 앞에 추가)을 사용하는 것이 좋습니다. 코드는 유지 관리가 쉬우 며 메소드는 여전히 프로토 타입에 정의되어 있습니다. 요즘은 ... 나는 단지 타입 스크립트에서 메소드를 private으로 표시합니다.
David Sherret

5

너무 장황하지 마십시오. 자바 스크립트입니다. 명명 규칙을 사용하십시오 .

es6 수업에서 수년간 일한 후 최근에 es5 프로젝트 작업을 시작했습니다 (이미 매우 장황한 requireJS 사용). 나는 여기에 언급 된 모든 전략을 계속 반복 해 왔으며 기본적으로 명명 규칙사용하도록 정리되었습니다 .

  1. 자바 스크립트에는 다음과 같은 범위 키워드가 없습니다. private . Javascript를 입력하는 다른 개발자는이를 미리 알게됩니다. 따라서 간단한 명명 규칙으로 충분합니다. 밑줄로 접두어를 붙이는 간단한 명명 규칙은 개인 속성과 개인 메서드의 문제를 해결합니다.
  2. 속도 때문에 프로토 타입을 활용 해 봅시다. 그러나 그보다 더 자세한 정보는 얻지 않겠습니다. es5 "클래스"를 다른 백엔드 언어에서 기대할 수있는 것과 비슷하게 유지하도록 노력하십시오 (그리고 인스턴스를 반환 할 필요가없는 경우에도 모든 파일을 클래스로 취급).
  3. 보다 현실적인 모듈 상황 (이전 es5 및 이전 requireJ 사용)으로 시연합시다.

my-tooltip.js

    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();
            }
            */
        }
    });

2
Javascript 언어 나 일반적인 브라우저 호스트는 개인 상태를 "숨기기"위해 명명 규칙에 의존하는 객체를 정의하지 않으므로 개발자가 개념을 파악하는 것이 옳을 수도 있지만 여전히 OO 프로그래밍에 대한 OO 접근법.
rich remer

그렇게 할 때 좋은 참조를 부탁해도 될까요? 이 예에는 나에게 새로운 부분이 있습니다. defineconstructor그 구조 자체. 나는 대부분 대답에 동의하지만, 나는 많은 OOP 영향으로 JS 작업을 시작했으며 C #에 대한 이전 경험이 있기 때문에 TS에 너무 일찍 뛰어 들었습니다. 나는 이것들을 배우고 프로토 타입 / 프로 시저 패러다임을 이해해야한다고 생각합니다. (upvoted, btw)
Cold Cerberus

1
@ColdCerberus이 코드는 es5를 사용하고 있습니다. 이 접근 방식의 전체 그림은 gist.github.com/jonnyreeves/2474026 에서 확인할 수 있습니다 . 그러나이 방법을 사용하여 es6 클래스를 사용하여 업데이트 할 수 있습니다. googlechrome.github.io/samples/classes-es6 및 es6 모듈 (가져 오기 / 내보내기 구문) : hackernoon.com/…
prograhammer

5

es10 개인 메소드를 사용 하여이 작업을 수행 할 수 있습니다 . #메소드 이름 앞에 a를 추가하면 됩니다.

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
    return #privateMethod();
  }
}

2
3 단계이지만 공식적으로는 언어의 일부가 아닙니다.
misterhtmlcss 2018

3

Crockford의 개인 또는 특권 패턴 을 따르는 솔루션을 선택하십시오 . 예를 들면 다음과 같습니다.

function Foo(x) {
    var y = 5;
    var bar = function() {
        return y * x;
    };

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

공격자가 JS 컨텍스트에서 "실행"권한이없는 경우 "공개"또는 "개인"필드 또는 방법에 액세스 할 수있는 방법이 없습니다. 공격자가 액세스 권한을 가지고있는 경우이 단일 라이너를 실행할 수 있습니다.

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

위의 코드는 모든 생성자 유형 개인 정보에 일반적입니다. 여기에 일부 솔루션이 실패하지만 클로저 기반 솔루션은 거의 모두 이와 같이 끊어 질 수 있음이 분명해야합니다.replace() 매개 변수로 .

이것이 실행 new Foo()되면 생성 된 객체 eval는 생성자의 클로저에 정의 된 값이나 메소드를 반환하거나 변경하기 위해 호출 할 수 있는 메소드 를 갖게 됩니다.

f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");

이것으로 볼 수있는 유일한 문제는 인스턴스가 하나만 있고로드시 생성되는 경우에는 작동하지 않는다는 것입니다. 그러나 실제로 프로토 타입을 정의 할 이유가 없으며,이 경우 공격자는 동일한 매개 변수를 전달하는 방법이있는 한 생성자 대신 오브젝트를 간단히 다시 작성할 수 있습니다 (예 : 상수 또는 사용 가능한 값에서 계산 됨).

제 생각에 이것은 Crockford의 솔루션을 거의 쓸모 없게 만듭니다."프라이버시"는 그의 솔루션의 단점 (쉽게 가독성 및 유지 보수성 감소, 성능 저하, 메모리 증가)을 쉽게 깨뜨 리므로 "프라이버시가없는"프로토 타입 기반 방법이 더 나은 선택입니다.

필자는 일반적으로 밑줄을 사용하여 표시 __private하고 _protected메서드와 필드 (Perl 스타일)를 사용하지만 JavaScript에서 개인 정보를 보호한다는 아이디어는 언어를 잘못 이해하는 방법을 보여줍니다.

그러므로 나는 그의 첫 문장을 제외하고 Crockford에 동의하지 않습니다 .

그렇다면 JS에서 실제 개인 정보를 어떻게 얻습니까? 서버 측에 개인용으로 필요한 모든 것을 넣고 JS를 사용하여 AJAX 호출을 수행하십시오.


이것은 더 잘 알려진 심각한 문제입니다. 이 공격에 대한 '방어'가 있습니까?
제임스

@James 내가 아는 사람은 없다. 나는 그것이 짐승의 본질이라고 생각한다. 내가 지적했듯이 보호 환경에서 실행되는 서버로 기능을 옮길 수 있습니다. 그래도 Crockford의 솔루션은 도움이되지 않고 불필요하게 코드를 복잡하게 만들고 그것에 대해 무언가를 할 필요성을 숨기고 있습니다.
Fozi

사용자가 비밀 암호를 입력하면 서버 측에서이 작업을 수행 할 수 없습니다. 어떤 시점에서 암호는 'private'var에 있습니다. 공격자가 읽을 수 있습니까? 나는 내 코드를 신뢰하고 어쨌든 내 집 표준은 eval ()을 허용하지 않습니다. 침입자는 내가 제대로 확인하지 않은 악의적 인 타사 JavaScript 플러그인 또는 라이브러리 일 수 있습니다. 따라서 확인해야합니다. 공격자는 내 코드와 상호 작용해서는 안되는 광고와 같은 것일 수도 있습니다. 모든 코드를 익명으로 감싸서 (function () {allMyStuff}());전역에 아무것도 노출시키지 않도록 보호 합니다.
제임스

@ 제임스 OT를 받고 있습니다. 계속하려면 새 질문을여십시오. 예, 공격자는 암호를 읽을 수 있습니다. "비공개"변수에서. 또는 DOM에서. 또는 AJAX API를 교체 할 수 있습니다. 또는 페이지를 다른 것으로 바꿉니다. 위의 작업을 수행 할 수없는 경우 JS 변수를 읽을 수 없으므로 JS "개인 정보"가 필요하지 않습니다. 요점은 모든 사람들이 현재 사용하고있는 Crockford의 "솔루션"이이 문제를 해결하지 못한다는 것입니다.
Fozi

필자는 의사 난수 코드 난독 화가이 공격에 대한 약한 방어책이라고 생각합니다. 이름이 고정 된 함수에 의존 할 수없는 경우 함수 본문을 수정하기가 더 어렵습니다. f.eval('nameOfVariable')당신이 무엇인지 모르는 경우에 더 어려운 'nameOfVariable'...
Gershom


2

퍼블릭 함수가 프라이빗 함수에 액세스 할 수있는 퍼블릭 및 프라이빗 함수의 전체 범위를 원하는 경우 다음과 같은 객체의 레이아웃 코드 :

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

왜 이것이 투표를 거부했는지 말해 줄 수 있습니까? 나에게 좋아 보인다.
thomasrutter

10
를 수행 할 때마다 new MyObject의 프로토 타입이 MyObject동일한 값으로 바뀝니다.
bpierre

2
-1. .prototype생성자 내부에 절대로 할당하지 마십시오 .
Bergi

2
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" );

georgebrock과 비슷하지만 조금 덜 장황한 (IMHO)이 방법으로 문제가 있습니까? (어디서도 보지 못했습니다)

편집 : 모든 독립 인스턴스화에는 자체 공개 메소드 사본이 있으므로 프로토 타입의 사용을 손상시키기 때문에 이것이 쓸모가 없다는 것을 깨달았습니다.


2

다음은 개인 / 공용 메소드 / 멤버 및 자바 스크립트의 인스턴스화와 관련하여 내가 지금까지 가장 즐겼던 것입니다.

여기 기사가 있습니다 : http://www.sefol.com/?p=1090

그리고 여기 예제가 있습니다 :

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

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


이 방법에는 하나의 중요한 단점이 있습니다. 만약 당신이 2 개의 객체를 생성했다면, 메모리에 2 개의 동일한 방법이있을 것입니다 (예를 들어 PublicFunction) 1000 개의 객체는 모든 메모리를 먹을 것입니다.
Artem G

2

대부분의 경우 모듈 패턴이 옳습니다. 그러나 수천 개의 인스턴스가있는 경우 클래스는 메모리를 절약합니다. 메모리 저장이 중요하고 객체에 적은 양의 개인 데이터가 포함되어 있지만 많은 공용 함수가있는 경우 모든 공용 함수가 .prototype에 있어야 메모리를 절약 할 수 있습니다.

이것이 내가 생각해 낸 것입니다.

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에 개인 속성이 포함되어 있습니다. public 함수를 통해 액세스 할 수 getPriv()있지만이 함수는 false을 전달하지 않으면 이 함수를 반환 secret하며 이는 메인 클로저 내부에서만 알려져 있습니다.


이는 보호 된 멤버를 시뮬레이트하며, 상속 된 유형은 보호 된 멤버에도 액세스 할 수 있습니다. 개인 패턴
보다이

2

이건 어때?

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"변수도 있습니다.


익명 함수에서 아무것도 반환하지 않기 때문에 그대로 유지 Restaurant됩니다 undefined.
user4815162342

어디서 어떻게? 작성된 Restaurant에 대한 참조가 유실되면 privateVars는 해당 소유자에 대한 참조를 갖지 않습니다. 참조 그래프는 비 주기적입니다. 내가 무엇을 놓치고 있습니까?
Evan Leis

실제로 이것은 메소드 외에도 개인 속성을 지원하는 유일한 답변입니다. 답에 이미 두 가지 문제 만 언급되어 있습니다.
pishpish

메모리 누수를 봅니다. 인스턴스 Restaurant가 가비지 수집 된 경우 해당 값은 내에 유지됩니다 privateVars. 이 경우 A를 WeakMap대체 할 수 있습니다 Array.
Gershom

2

익명 함수로 모든 코드를 래핑하십시오. 그러면 모든 함수는 비공개이며 window객체에 연결된 함수 만 있습니다 .

(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,{});

이것을 사용하십시오 :

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

깡깡이


1

개인 데이터를 연결된에 저장하는 것을 선호합니다 WeakMap. 이를 통해 공개 메소드가 속한 프로토 타입에 공개 메소드를 유지할 수 있습니다. 이것은 많은 수의 객체 에서이 문제를 처리하는 가장 효율적인 방법 인 것 같습니다.

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};

0

개인 함수는 모듈 패턴을 사용하여 공용 변수에 액세스 할 수 없습니다


0

모두가 여기에 자신의 코드를 게시했기 때문에 나도 그렇게 할 것입니다 ...

Crockford는 Javascript에서 실제 객체 지향 패턴을 도입했기 때문에 좋아합니다. 그러나 그는 새로운 오해, 즉 "그것"도 생각해 냈습니다.

왜 그는 "that = this"를 사용합니까? 개인 기능과는 전혀 관련이 없습니다. 내부 기능과 관련이 있습니다!

Crockford에 따르면 이것은 버그가있는 코드입니다.

Function Foo( ) {
    this.bar = 0; 
    var foobar=function( ) {
        alert(this.bar);
    }
} 

그래서 그는 이것을 제안했습니다.

Function Foo( ) {
    this.bar = 0;
    that = this; 
    var foobar=function( ) {
        alert(that.bar);
    }
}

내가 말했듯이, Crockford가 그것에 대한 그의 설명과 이것에 대한 잘못된 설명이라고 확신합니다 (그러나 그의 코드는 확실합니다). 아니면 누가 자신의 코드를 복사하는지 알기 위해 Javascript 세계를 속이고 있었습니까? 나는 몰라 ... 나는 브라우저 괴짜 아니에요; D

편집하다

아, 그게 전부입니다. 'var that = this;' JavaScript에서 의미 하는가?

그래서 Crockie는 그의 설명이 잘못되었습니다. ...하지만 그의 코드는 맞습니다. 그래서 그는 여전히 훌륭한 사람입니다. :))


0

일반적으로 개인 객체 _를 일시적으로 객체에 추가했습니다. 이 방법을 사용하려면 "전력 생성자"에서 개인 정보를 부드럽게 열어야합니다. 프로토 타입에서 메소드를 호출하면 프로토 타입 메소드를 겹쳐 쓸 수 있습니다.

  • "Power-constructor"에서 공개 메소드에 액세스 할 수있게하십시오 (ctx는 오브젝트 컨텍스트입니다)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • 이제이 openPrivacy가 있습니다.

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };

0

이것이 내가 해결 한 것입니다.

여기서 찾을 수있는 한 종류의 설탕 코드가 필요 합니다 . 또한 보호, 상속, 가상 정적 요소를 지원합니다.

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

0
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/JSClass


0

프로토 타입 https://github.com/TremayneChrist/ProtectJS 에서 진정한 개인 방법을 사용할 수 있도록 새로운 도구를 만들었습니다.

예:

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
어떻게 작동하는지 설명해 주시겠습니까? 메소드 를 어떻게 / 어디서 호출 할 수_private 있습니까?
Bergi

0

개인 생성자 메서드를 정의 할 수있는 실제 생성자 함수 주위에 클로저를 배치해야합니다. 이러한 개인 메소드를 통해 인스턴스 데이터를 변경하려면 함수 인수로 또는 .apply (this)로이 함수를 호출하여 "this"를 인스턴스에 제공해야합니다.

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

실제로는 작동하지 않습니다. 모든 사람 new Restaurant은 자신의 restaurantcontstructor 를 가지고 있으며 "시제품"은 완전히 남용됩니다.
Bergi

@ 버기. 사실, 당신 말이 맞아요 그것은 작동하지만 리소스 호그 일 것입니다 (이런 식입니까?). 나는 그것에 관한 나의 대답을 편집했다.
Flex Elektro Deimling

업데이트 해 주셔서 감사합니다. 무엇 이전 버전을 호출 해야할지 모르겠지만 (하지만 "버그":-)
Bergi

0

나는 그것이 너무 늦다는 것을 알고 있지만 어떻습니까?

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

0

이 질문에 대한 많은 답변이 이미 있지만 내 요구에 맞는 것은 없습니다. 그래서 나는 내 자신의 해결책을 생각해 냈습니다. 나는 누군가에게 유용하기를 바랍니다.

function calledPrivate(){
    var stack = new Error().stack.toString().split("\n");
    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

보시다시피이 시스템은 자바 스크립트에서이 유형의 클래스를 사용할 때 작동합니다. 내가 위에서 언급 한 방법 중 어느 것도 알아 내지 않는 한.


1
호기심 : 실제로 모든 기능을 노출해야하지만 런타임에 다른 기능을 모두 호출해야합니다. 모든 / 대부분의 다른 답변과 달리 외부 호출자에게 숨기지 않고 런타임에 작동하지 않아야합니까? 그렇다면 왜 그렇습니까? 이 접근법의 이점은 무엇이라고 생각하십니까? 나에게 이것은 불필요한 성능 오버 헤드, 불명확 한 API, 아마도 디버그 지옥을 야기 할 수밖에없는 것 같다. 그러나 나는 항상 새로운 관점에 개방되어있다.
JHH

2
@JHH 솔직히 말해서, 나는 이것을 다시 볼 때 손바닥을 am 다. 오버 헤드는 일반적으로 전혀 가치가 없지만,이 클래스에 대한 많은 호출을하지 않았으므로 나에게는별로 중요하지 않았습니다. 내가 이런 식으로 한 이유는 함수를 작성하고 호출하는 방식이 비교적 깨끗하기 때문입니다. 나는 그때까지 상징과 그와 같은 것을 이해하지 못했지만 이제는 수업을 사용할 때 일반적으로 갈 길이라고 생각합니다. 이 답변을 모두 제거하는 것을 고려하고 있습니다. 나는 몇 가지 어리석은 답변을 올렸지 만, 당신은 살고 배우고 있습니다.
thegunmaster

피드백을 주셔서 감사합니다! 내가 무언가를 잘못 이해했는지 확신 할 수 없었습니다. 그러나 그렇습니다, 우리 모두는 살고 배웁니다!
JHH

0

프라이빗 및 퍼블릭 인터페이스를 갖춘 깨끗하고 간단한 '클래스'솔루션과 컴포지션 지원에 대해서는 이 답변 을 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.