Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Выяснить, что функция вызвана в режиме конструктора (https://javascript.ru/forum/misc/21065-vyyasnit-chto-funkciya-vyzvana-v-rezhime-konstruktora.html)

Octane 27.08.2011 14:04

Выяснить, что функция вызвана в режиме конструктора
 
Есть идеи, как внутри F различать способы вызова?

function F() {}

new F;
F.call(new F);
F();


обычно делаю так
function F() {
    if (this.constructor == F) {
        return 1;
    }
    return 2;
}


Но понадобилось отделить вариант с F.call(obj), и вот когда obj - это объект, созданный с помощью F, такая проверка не подходит. Нужно отделить способы запуска [[Construct]] и [[Call]].

melky 27.08.2011 14:20

я не вижу возможного решения.

т.е. как разделить эти два вызова ?
function F(){ this.a=function(){}; }

var a = new F;

var b = new F;

a.a();
// и 
F.call( b ).a();

Octane 27.08.2011 14:24

Ну решение есть:
function F() {
	if (this.constructor == F) {
		if (this._constructed) {
			alert("F.call(new F)");
			return null;
		}
		this._constructed = true;
		return this;
	}
	return null;
}

new F;

F.call(new F);

F.call({});

F();

Просто может я торможу и еще как-то проще/лучше/надежнее можно сделать. Не очень хочется иметь ненужное enumerable-свойство в каждом объекте.

melky 27.08.2011 14:36

Цитата:

Сообщение от Octane (Сообщение 122907)
Просто может я торможу и еще как-то проще/лучше/надежнее можно сделать. Не очень хочется иметь ненужное enumerable-свойство в каждом объекте.

Object.defineProperty ? ie9+

Octane 27.08.2011 14:41

Да смысл, надо будет помнить о дополнительных проверках для старых браузеров.

Sweet 27.08.2011 15:00

Цитата:

Сообщение от Octane
Не очень хочется иметь ненужное enumerable-свойство в каждом объекте.

Можно не создавать спецальное свойство, а смотреть на какое-нибудь, какое есть:)

tenshi 27.08.2011 20:13

а зачем их отличать? constructor - обычный метод, который предназначен для инициализации объекта. запись ( new F( 1, 2, 3 ) ) - это просто грубо говоря более короткая запись следующей ( var obj= {}; obj.__proto__= F.prototype; return obj.constructor( 1, 2, 3 ) || obj )

Octane 27.08.2011 20:49

function F(x) {
	if (this.constructor == F) {
		this.x = x;
	}
	return this.x;
}

var f = new F(3), obj = {x: 5};

alert(f.x); //3

alert(F.call(obj)); //5
*!*
alert(F.call(f)); //ожидаем получить 3, а будет undefined
*/!*

Sweet 27.08.2011 21:01

В конкретном примере было бы уместно проверять, передан ли аргумент:
if (this.constructor == F && arguments.length > 0) {

B@rmaley.e><e 27.08.2011 21:49

Обыграть то, что в случае вызова через new F сначала создается пустой объект, а потом уже к нему применяется конструктор. В случае с call'ом к объекту уже применён конструктор.
function F(x) {
	if (this.constructor == F && this.sealed === undefined) {
		this.x = x;
	}
	this.sealed = true;
	return this.x;
}

var f = new F(3), obj = {x: 5};

alert(f.x); //3

alert(F.call(obj)); //5
alert(F.call(f)); //ожидаем получить 3, а будет 3

Octane 27.08.2011 21:59

В третьем сообщении я привел такой же пример решения с помощью дополнительного свойства.

float 27.08.2011 23:18

Цитата:

Не очень хочется иметь ненужное enumerable-свойство в каждом объекте.
Вроде можно и не иметь:
function F(x) {
		if (this.constructor == F && !F.i) {
			F.i = 1;
			this.x = x;
		}
		return this.x;
	}
	
	var f = new F(3), obj = {x: 5};
	
	alert(f.x); //3
	
	alert(F.call(obj)); //5
	alert(F.call(f)); //3

Octane 27.08.2011 23:30

Так конструктор будет одноразовый

float 27.08.2011 23:35

тфу, чуть не так хотел.
function F(x) {
		if (this.constructor == F && !this.constructor.i) {
			this.constructor.i = 1;
			this.x = x;
		}
		return this.x;
	}
	
	var f = new F(3), obj = {x: 5};
	
	alert(f.x); //3
	
	alert(F.call(obj)); //5
	alert(F.call(f)); //3

B@rmaley.e><e 28.08.2011 08:42

Так в этом случае
Цитата:

Сообщение от float
this.constructor == F

же.

float 28.08.2011 11:57

точно. забыл что конструктор это ссылка.

float 28.08.2011 12:50

ну, пожалуй, последний мой вариант:):
function isEmptyObj(obj) {
		var a; for(a in obj) {return false;}
		return true;
	}

	function F(x) {
		if (this.constructor == F && isEmptyObj(this)) {
			this.x = x;
		}
		return this.x;
	}
	
	var f = new F(3), obj = {x: 5};
	
	alert(f.x); // 3
	alert(F.call(obj)); // 5
	alert(F.call(f)); // 3

Octane 28.08.2011 13:58

Только hasOwnProperty надо добавить.


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