Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Обращение к приватной переменной из прототипной функции (https://javascript.ru/forum/misc/27432-obrashhenie-k-privatnojj-peremennojj-iz-prototipnojj-funkcii.html)

unclechu 12.04.2012 19:44

Обращение к приватной переменной из прототипной функции
 
Доброго времени суток! Подскажите, никак не могу найти решение касательно js. Давайте сразу к простому примеру:
var human = (function () {
	var prototype = {}
	prototype.getName

	prototype.getName = function (str) {
		if (str) this.name = str
		return this.name
	}

	function constructor(data) {
		this.name
	}
	constructor.prototype = prototype
	return constructor
}())

var h1 = new human
h1.getName('Vasia')
var h2 = new human
h2.getName('Petia')

Условие тут таково, что должны быть прототипные функции, которые будут обращаться в приватным переменным экземпляра конструктора.
В данном случае this.name — это публичное свойство, но что если я не хочу чтобы можно было напрямую менять name в духе:
var h1 = new human
h1.getName('Vasia')
h1.name = 'Petia'

Как мне это преградить? Иными словами: как работать с приватными переменными из функции в прототипе? Вроде как банальная потребность.
Единственное что пришло в голову: это хранить массив id экземпляров с объектами данных выше уровнем, чем конструктор, а в this.id хранить уже сам идентификатор конкретного экземпляра. Но, согласитесь, это маразм, да и к тому же поменять id многого ли стоит?

Раед 12.04.2012 20:10

Цитата:

Сообщение от unclechu
как работать с приватными переменными из функции в прототипе

Цитата:

Сообщение от nasqad
надо новое замыкание создавать

может так
var human = (function () {
    var prototype = {}
    prototype.getName
   
    +function(){
   var name;
    prototype.getName = function (str) {
        if (str) name = str
        return name
    }
 
    function constructor(data) {
        name
    }
   }()
    constructor.prototype = prototype
    return constructor
}())
 
var h1 = new human
h1.getName('Vasia')
var h2 = new human
h2.getName('Petia')

unclechu 12.04.2012 20:14

Цитата:

Сообщение от Раед (Сообщение 168618)
как обычно, через замыкание
var human = (function () {
    var name;
    var prototype = {}
    prototype.getName
 
    prototype.getName = function (str) {
        if (str) name = str
        return name
    }
 
    function constructor(data) {
        name
    }
    constructor.prototype = prototype
    return constructor
}())
 
var h1 = new human
h1.getName('Vasia')
var h2 = new human
h2.getName('Petia')

И каков по вашему результат?
Если бы всё было так просто, я бы не спрашивал, после выполнения этого кода, стало так:
h1.getName() // Petia
h2.getName() // Petia

Мне нужно чтобы name был уникальным для каждого экземпляра.

Раед 12.04.2012 20:26

Цитата:

Сообщение от unclechu
как работать с приватными переменными из функции в прототипе

Цитата:

Сообщение от nasqad
надо новое замыкание создавать

может так
var human = (function () {
    var prototype = {}
    prototype.getName
   
    +function(){
   var name;
    prototype.getName = function (str) {
        if (str) name = str
        return name
    }
 
    function constructor(data) {
        name
    }
   }()
    constructor.prototype = prototype
    return constructor
}())
 
var h1 = new human
h1.getName('Vasia')
var h2 = new human
h2.getName('Petia')

Mixxx 12.04.2012 20:32

Исправил вашь код:
function log(a){document.body.innerHTML+=a+'<br>'}

//функция хуман это не конструктор, она возвращает конструктор которы надо потом инициализировать с помощью new
var human = (function () {
	var prototype = {}
	//prototype.getName
	var _data, _name;
	
	prototype.setName = function (str)
	{
		if (str) _name = str
	}
	prototype.getName = function () 
	{
		return _name
	}

	function constructor(data) {
		//this.name
                       _data=data

	}
	constructor.prototype = prototype
	return constructor
})

var Alfa = new (human())() //столько скобок нужно что бы new относилось к constructor а не к human, 

Alfa.setName('Vasia')
log('Alfa: '+ Alfa.getName() )

var Beta = new (human())()
Beta.setName('Petia')
Beta._name='Евгений Ваганыч'; //создаться публичное свойство _name но приватное _name не изменится
log('Beta: '+ Beta.getName() );

log('Alfa: '+ Alfa.getName() )



Раед,
Вашь код не работает в IE9 и Опере (в других не тестил)
опера ругается Uncaught exception: TypeError: 'h1.getName' is not a function
И я не понимаю что за синтаксис такой +function

Раед 12.04.2012 21:14

Цитата:

Сообщение от Mixxx
Вашь код не работает в IE9 и Опере

ну это понятно. так должен заработать
var human = (function () {
    var prototype = {}
    prototype.getName
    
    +function(){
   var name;
    prototype.getName = function (str) {
        if (str) name = str
        return name
    }
  
    function constructor(data) {
        name
    }
   }()
    constructor.prototype = prototype
    return constructor
}())
  
var h1 = new (human())();
h1.getName('Vasia')
var h2 = new (human())();
h2.getName('Petia')

Цитата:

Сообщение от Mixxx
что за синтаксис такой +function

а Это для вызова на месте. вместо скобок.

Octane 12.04.2012 21:26

Просто назовите свойство _name. Такое соглашение о именование свойств, которые не должны использоваться снаружи, давно устоялось, известно и используется множеством js-программистов.

Mixxx 12.04.2012 21:47

Цитата:

а Это для вызова на месте. вместо скобок.
Не знал что так можно
Цитата:

Сообщение от Раед
ну это понятно. так должен заработать

Всеравно не работает ни в ие ни в опере
Опера например ругается
Цитата:

Uncaught exception: TypeError: 'human()' is not a constructor
Цитата:

Сообщение от Octane (Сообщение 168645)
Просто назовите свойство _name.

_name слишком скромно, лучше назвать $_$___$nAMe тогда точно никто его не изменит :D

Раед 12.04.2012 22:54

Цитата:

Сообщение от Mixxx
лучше назвать $_$___$nAMe

не, лучше так: ['аопл уыркШГЦР ЦЫЕАП \n\n kdgnrjk \trnga 58456g563&*^*&^$%^$(']

Octane 12.04.2012 23:17

Ну городите замыкания, раз вас веселит соглашение об именовании свойств, которое, как UpperCamelCase для конструкторов и lowerCamelCase для всего остального, присутствует в guidеline'ах к стилю JavaScript-кода.

GuardCat 12.04.2012 23:44

Кроме огорода замыканий не вижу вариантов. Можно, например, создавать в конструкторе одноимённое прототипному свойство, которое будет вызывать функцию с тем же именем. из прототипа, передавая ей приватную переменную. Саму процедуру обёртывания и замыкания можно тоже в прототип переместить. Так в конструкторе повторы будут, конечно, но меньше, чем если просто там размещать все функции. Получается как-то так:
function Human(name) {
	this.makeWrap("getName", [name]);
}

Human.prototype = {
	makeWrap:  function (propertyName, args) {
		this[propertyName] = (
			function(args) {
				return function() {
					return Human.prototype[propertyName](args);
				}
			}
		)(args)
	},
	getName: function(name) {
		return name[0];
	},
	constructor: Human
}
 
h1 = new Human("Василий Иванович");
h2 = new Human("Иван Васильевич");

alert(h1.getName())
alert(h2.getName())
. Как-то костыльно, nein? Вариант с соглашением проще. и лучше, на мой взгляд.

Mixxx 13.04.2012 00:02

Цитата:

Сообщение от GuardCat (Сообщение 168677)
Вариант с соглашением проще. и лучше, на мой взгляд.

Конечно проще, я его и юзаю всегда. Только если человек спросил как сделать как бы "приватную" переменную зачем его отговаривать? Он же не просто так спросил. Значит ему так надо. Коллеги, давайте уже наконец доверять друг другу :D

unclechu 13.04.2012 00:31

Цитата:

Сообщение от Mixxx (Сообщение 168628)
Исправил вашь код:
function log(a){document.body.innerHTML+=a+'<br>'}

//функция хуман это не конструктор, она возвращает конструктор которы надо потом инициализировать с помощью new
var human = (function () {
	var prototype = {}
	//prototype.getName
	var _data, _name;
	
	prototype.setName = function (str)
	{
		if (str) _name = str
	}
	prototype.getName = function () 
	{
		return _name
	}

	function constructor(data) {
		//this.name
                       _data=data

	}
	constructor.prototype = prototype
	return constructor
})

var Alfa = new (human())() //столько скобок нужно что бы new относилось к constructor а не к human, 

Alfa.setName('Vasia')
log('Alfa: '+ Alfa.getName() )

var Beta = new (human())()
Beta.setName('Petia')
Beta._name='Евгений Ваганыч'; //создаться публичное свойство _name но приватное _name не изменится
log('Beta: '+ Beta.getName() );

log('Alfa: '+ Alfa.getName() )



Раед,
Вашь код не работает в IE9 и Опере (в других не тестил)
опера ругается Uncaught exception: TypeError: 'h1.getName' is not a function
И я не понимаю что за синтаксис такой +function

Это конечно решение, но я так понимаю смысл использование прототипов теряется? Потому как: Alfa.__proto__ !== Beta.__proto__ Ну то-бишь не реюзится отсылка к прототипу уже, а попросту новые прототипы?

В таком случае решение с символом «_» — лучший вариант, когда нужны тру-прототипы.

Mixxx 13.04.2012 01:23

GuardCat,
Посмотрел вашь код
понял что вы предлагаете запоминать name в массиве который зависнет как параметр анонимной функции внутри нового метода getName который переопределит makeWrap. Надо сказать это весьма и весьма хиро%опо :D
setName можно реализовать также как в конструкторе:
...
	setName: function(newName)
	{
		this.makeWrap('getName',[newName])
	},
...

Но ведь это ужасно :( получается прямого доступа к полю нейм нету даже внутри методов объекта.

Во что у меня получилось из вашего кода (массивы убрал так как тут они не нужны):
function log(a){alert(a)}

function Human(name) 
{
	this.makeWrap("getName", name);
}

Human.prototype = 
{
	makeWrap:  function (propertyName, args) 
	{
		
		this[propertyName] = 
		(
			function(args) 
			{
				return function() 
				{
					return Human.prototype[propertyName](args);
				}
			}
		)(args)

	},
	setName: function(newName)
	{
		this.makeWrap('getName',newName)
	},
	getName: function(name) 
	{
		return name;
	},
	setLastName: function(LName)
	{
		this.makeWrap('getLastName',LName)
	},
	getLastName: function(LName)
	{
		return LName
	},
	getFullName: function()
	{
		return this.getName()+' '+this.getLastName();
	}

//	,constructor: Human
}
 
h1 = new Human("Владимир");
h2 = new Human("Дмитрий");
h1.setLastName('Путин');
log(h1.getFullName())

h2.setLastName('Медведев');
log(h2.getFullName())

h2.setName('Александр')
log(h2.getFullName())

log('Протипы равны? '+(h1.__proto__ === h2.__proto__))

GuardCat 13.04.2012 07:07

Mixxx, массивы будут удобны, если возникнет необходимость передавать больше одного параметра. Можно и в этом случае обойтись без массивов, но с ними проще.

unclechu 13.04.2012 14:55

Maxmaxmахimus, спасибо за ссылку, есть кое что полезное на заметку.

unclechu 13.04.2012 15:16

Приваты, судя по ECMA будут, и как я понял это будет private и объект x.
private key
x.key // x для приватов вместо this


Часовой пояс GMT +3, время: 21:20.