2010-12-10 4 views
3

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

<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, что я говорю о локальной переменной, и нет.

ответ

6

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

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

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

Вот некоторые из моих ответов здесь, на StackOverflow, связанный с яваскриптом закрытий которые я считаю стоит читать:

Hidden Features of JavaScript?

Please explain the use of JavaScript closures in loops

Или вы можете просто Google фразы «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

Причина, по которой 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 

Надеюсь, это немного облегчит ситуацию.

+0

образец прототипа более элегантный, я согласен. я шел из академического POV, и мне хотелось узнать, почему работает заводская модель. благодаря! – Ankur 2010-12-10 17:22:15

0

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

Чтобы использовать «это» так, как вы ожидаете, вы должны были бы использовать новый оператор так:

вар Foo = новый createAddress (...

И быть назначение переменных-членов на «это».

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

+0

хорошо, я могу согласиться с этим, но тогда почему используется первая работа с комментариями? (без других двух строк, которые я имею в виду). если this.street действительно будет присваивать window.street, то почему второй вызов showLabel возвращает другое значение и не теряет старое значение из-за перезаписи? – Ankur 2010-12-10 16:12:52

+0

Я правильно ответил на свой вопрос, сказав следующее: `this` является специальной переменной и, несмотря на закрытие, все равно будет ссылаться на объект, членом которого является функция. или я полностью не получаю это? – Ankur 2010-12-10 16:35:34

+0

@ Ankur: см. Мой ответ. Короче говоря, вы говорите прав. – slebetman 2010-12-10 16:39:01

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