Переменная область действия фабричного шаблона Javascript - PullRequest
3 голосов
/ 10 декабря 2010

Я следую руководству, в котором показан шаблон фабрики для создания объектов в javascript.Следующий код поставил меня в тупик, почему он работает.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>6-2.htm</title>
</head>
<body>
<script type="text/javascript">
function createAddress(street, city, state, zip) {
  var obj = new Object();
  obj.street = street;
  obj.city = city;
  obj.state = state;
  obj.zip = zip;
  obj.showLabel = function() {
    //alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip);
    //var obj;
    alert(obj.street + "\n" + obj.city + ", " + obj.state + " " + obj.zip);
  };
  return obj;
};

var JohnAddr = createAddress("12 A St.", "Johnson City", "TN", 37614);
var JoeAddr = createAddress("10061 Bristol Park", "Pensacola", "FL", 32503);

JohnAddr.showLabel();
JoeAddr.showLabel();
</script>
</body>
</html>

первая закомментированная строка кажется мне подходящей (используя ключевое слово this в функции showLabel).Я не уверен, как работает использование obj на своем месте.obj должен был бы где-то ссылаться на глобальную переменную, потому что внутри этой функции, когда она запускается, obj не определено, верно?Поскольку я делаю 2 объекта, в данном случае не просто удача, что оба отображаются нормально, более старые значения для содержимого obj хранятся и ссылаются правильно.но как?если я раскомментирую второй комментарий, то он ломается, и я понимаю, почему, теперь я прямо говорю js, что я говорю о локальной переменной, а ее нет.

Ответы [ 4 ]

6 голосов
/ 10 декабря 2010

Добро пожаловать в мир замыканий.Вы правы, когда чувствуете, что чувствуете себя так, будто ведете себя глобально, но не совсем глобально.Вот как ведут себя замыкания.

В основном в javascript, когда функция возвращает не все локальные переменные, это обязательно сборщик мусора / освобождается, как в Java или C. Если есть ссылка на эту переменную, то эта переменная выживает в области действияфункция, в которой она определена.

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

Вот некоторые из моих других ответов здесь по поводу переполнения стека, связанных с закрытиями javascript, которые, я считаю, стоит прочитать:

Скрытые функцииJavaScript?

Пожалуйста, объясните использование замыканий JavaScript в циклах

Или вы можете просто использовать фразу "закрытие javascript", чтобы изучить тему.


Дополнительный ответ.

Что касается объяснения того, почему this работает в вашем коде (в отличие от * кашля * попытки исправить ваш код так, чтобы this работал дажехотя он работает в исправленной версии * кашель *; -):

Javascript имеет поздние привязки.Очень поздно, очень-очень поздно.this не только не связан во время компиляции, он даже не связан во время выполнения.Он ограничен во время выполнения, то есть до тех пор, пока не будет вызвана функция, вы не можете знать, на что это действительно указывает.Вызывающий в основном решает, что является значением this, а не функцией, где используется this.

Некоторые забавные маневры позднего связывания с JavaScript:

function foo () {
    alert(this.bar);
}

var bar = "hello";
var obj = {
    foo : foo,
    bar : "hi"
};
var second_obj = {
    bar : "bye"
};

foo(); // says hello, 'this' refers to the global object and this.bar
       // refers to the global variable bar.

obj.foo(); // says hi, 'this' refers to the first thing before the last dot
           // ie, the object foo belongs to

// now this is where it gets weird, an object can borrow/steal methods of
// another object and have its 'this' re-bound to it

obj.foo.call(second_obj); // says bye because call and apply allows 'this'
                          // to be re-bound to a foreign object. In this case
                          // this refers to second_obj

В вашем коде, this удобно относится к объекту, вызывающему функцию как к ее методу, поэтому она работает, даже если вы явно не используете предположительно правильный синтаксис конструктора.

2 голосов
/ 10 декабря 2010

Причина, по которой obj работает внутри функции showLabel, заключается в том, что obj является локальной переменной.Функция объявляется каждый раз, когда вызывается адрес создания.В JavaScript мы называем это закрытием.

Вообще говоря, создание объекта-прототипа предпочтительнее данного шаблона фабрики.

Теперь взглянем на этот пример прототипа:

var Address = function(street, city, state, zip){
    this.street = street;
    this.city = city;
    this.state = state;
    this.zip= zip;
};

Address.prototype.showLabel = function(){
    alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip);
}

Теперь, когда я создаю новый адрес с новым ключевым словом:

// create new address
var address = new Address('1', '2', '3', '4');
address.showLabel(); // alert

Код будет вести себя точно так, как вы ожидаете.Однако, если я не использую новое ключевое слово this, внутри конструктора фактически находится объект window.

// create new address
var address = Address('1', '2', '3', '4'); // address == undefined
window.showLabel(); // address was added to window

Надеюсь, это немного прояснит ситуацию.

1 голос
/ 10 декабря 2010
function Address(street, city, state, zip) {

  this.street = street;
  this.city = city;
  this.state = state;
  this.zip = zip;
  this.showLabel = function() {
    //alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip);
    //var obj;
    alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip);
  };
};

var JohnAddr = new Address(...);
JohnAddr.showLabel();
0 голосов
/ 10 декабря 2010

Это часть странности js, showLabel - это замыкание и имеет доступ к obj, потому что оно было в области действия при создании - новое замыкание создается при каждом вызове createAddress.

Чтобы использовать 'this' так, как вы ожидаете, вам нужно использовать новый оператор, так:

var foo = new createAddress (...

И присваивать переменные-члены 'this'.

В этом случае, когда new не используется, «this» является глобальным объектом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...