2013-06-25 3 views
2

Ну у меня есть этот конструктор с одним секретом и один метод:Можно ли определить метод priviledge вне конструктора?

function Keeper(get) { 
    var secretPower = 'wisdom'; 

    this.get = get ? get : function() { 
     return 'Its secret power is: ' + secretPower; 
    } 
    // now this is privileged method only in case that there are no arguments? 
} 

Теперь я сделать два экземпляра, один имеет привилегированный метод ...

var yourKeeper = new Keeper(); 
yourKeeper.get();  // "Its secret power is: wisdom" 

... но другое одно другой. Это может коснуться контекст вокруг него, но не рядовых конструктору ...

var myKeeper = new Keeper(function() { 
     return 'Its secret power is: ' + secretPower; 
    }); 
myKeeper.get();   // ReferenceError: secretPower is not defined 

... и это не работает, как я хочу:

myKeeper.get = function() { 
    return 'Its secret power is: ' + secretPower; 
} 
myKeeper.get();   // ReferenceError: secretPower is not defined 

Конечно, это не будет работать , потому что secretPower глобальная переменная в этих случаях, так:

var secretPower = 'none'; 
myKeeper.get();   // "Its secret power is: none" 

так возможно определить метод Priviledge вне конструктора? Как?

Можно ли это сделать с помощью eval? (Я знаю ... это зло ... Мне просто интересно)

ответ

2

Да, это можно определить привилегированные методы вне конструктора , Вот как это сделать:

var Keeper = (function (key) { 
    function Keeper(get) { 
     var private = { 
      secretPower: "wisdom" 
     }; 

     this.getPrivate = function (k) { 
      if (k === key) return private; 
     }; 

     this.get = get || defaultGet; 
    } 

    function defaultGet() { 
     var private = this.getPrivate(key); 
     return "The secret power is: " + private.secretPower; 
    } 

    return Keeper; 
}({})); 

Вот как это работает:

  1. Мы создаем пространство имен, создавая Immediately Invoked Function Expression (IIFE).
  2. Мы создаем объект под названием key. Этот объект является закрытым для только что созданного пространства имен. Следовательно, доступ к нему могут получить только привилегированные функции и конструктор.
  3. Внутри конструктора мы создаем объект с именем private, который содержит частное состояние каждого экземпляра.
  4. Мы также создаем привилегированный метод с именем getPrivate, который принимает аргумент k и возвращает только частный объект, если k - это key, который мы создали на шаге 2. Следовательно, доступ к частному объекту может получить только привилегированные функции.
  5. Привилегированные функции, которые хотят получить доступ к приватному состоянию объекта, должны вызвать getPrivate(key), чтобы получить частный объект состояния.

Примечание: Этот метод является полезным, если у вас есть более одной привилегированной функции, которую нужно получить доступ к частному состоянию объекта. Кроме того, вызов getPrivate представляет (очень) небольшую служебную нагрузку для привилегированных функций.


BTW, если вы хотите, чтобы написать чистый объектно-ориентированный код в JavaScript, то посмотрите на augment библиотеке. Приведенный выше код будет выглядеть следующим образом, используя augment:

var Keeper = Object.augment(function (key) { 
    this.constructor = function (get) { 
     var private = { 
      secretPower: "wisdom" 
     }; 

     this.getPrivate = function (k) { 
      if (k === key) return private; 
     }; 

     this.get = get || defaultGet; 
    }; 

    function defaultGet() { 
     var private = this.getPrivate(key); 
     return "The secret power is: " + private.secretPower; 
    } 
}, {}); 
+0

Благодарим вас за ответ. Во всяком случае, у меня есть вопрос: Создайте новый экземпляр Хранителя и передайте функцию в качестве аргумента. Попробуйте вернуть secretPower с этой функцией. Это возможно, только если вы создаете экземпляр внутри пространства имен. Я понимаю, что defaultGet является привилегированной функцией, созданной вне конструктора. Во всяком случае, он по-прежнему привязан к пространству имен ... который, как ни странно, подразумевает, что он находится внутри конструктора. –

+0

@Windkiller No. Есть только один экземпляр 'defaultGet', который является общим для всех экземпляров' Keeper'. –

2

Сама идея обзора в javascript заключается в том, что это не должно быть возможным, что бы я советовал добавлять функцию getPrivate в рамках области конструктора и использовать его для доступа к «приватным» переменным.

function Keeper(get) { 
    var semiPrivates = { 
     secretPower:'Wisdom' 
    } 

    this.getPrivate = function (variable){ 
     return semiPrivates[variable]; 
    } 

    this.get = get ? get : function() { 
     return 'Its secret power is: ' + semiPrivates["secretPower"]; 
    } 
} 
+0

Как 'myKeeper.getPrivate ('secretPower')' может быть вызвана из-за пределов, то не было бы никакой разницы называть 'myKeeper [ 'secretPower']'. – marsze

+0

Да, но вопрос уже хочет как-то разоблачить «частные» переменные, поэтому эта конструкция, по крайней мере, позволит контролировать, какие «частные» переменные доступны и не будут загромождать объект. –

+0

@marsze Будет разница. Вы не можете изменить значение secretPower с помощью метода getPrivate(). –

1

Я бы никогда на самом деле это так, но вот решение с eval:

function test(){ 
    var private = 'hello'; 

    this.say = function(){ 

    } 

    this.setSay = function(func){ 
     eval('this.say = ' + func.toString()); 
    } 
} 

var x = new test(); 
x.setSay(function(){ 
    console.log(private + ' world'); 
}); 
x.say(); 

пример http://jsfiddle.net/claustrofob/AwMd3/

А вот пример, связанный с вашей Keeper объекта:

function Keeper(get) { 
    var secretPower = 'wisdom'; 
    var getFunc = function() { 
     return 'Its secret power is: ' + secretPower; 
    }; 

    var evalFunc = function(variable, val){ 
     eval(variable + ' = ' + val.toString()); 
    }; 

    this.__defineSetter__("get", function(val){ 
     evalFunc('getFunc', val); 
    }); 

    this.__defineGetter__("get", function(val){ 
     return getFunc; 
    }); 

    if (get !== undefined){ 
     evalFunc('getFunc', get); 
    } 
} 

здесь мы определяем сеттер и gette г для вашего метода get так что вы можете инициализировать свои объекты таким образом:

var x = new Keeper(function() { 
    return 'Its secret power is: ' + secretPower; 
}); 

и таким образом:

var x = new Keeper(); 
x.get = function() { 
    return 'Its secret power is: ' + secretPower; 
}; 

живой пример http://jsfiddle.net/claustrofob/yu6VJ/