Skip to content

Commit 7af5b3c

Browse files
update line breaks(partial)
1 parent f0567b6 commit 7af5b3c

1 file changed

Lines changed: 3 additions & 98 deletions

File tree

  • 1-js/12-generators-iterators/1-generators

1-js/12-generators-iterators/1-generators/article.md

Lines changed: 3 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -11,128 +11,91 @@
1111
ظاهر یک تابع generator به صورت زیر است:
1212

1313
```js
14-
1514
function* generateSequence() {
16-
1715
yield 1;
18-
1916
yield 2;
20-
2117
return 3;
22-
2318
}
24-
2519
```
2620

27-
2821
توابع generator با توابع معمول، رفتار متفاوتی دارند. زمانی که این توابع صدا می‌شوند، بدنه آن‌ها اجرا نمی‌شود؛ در عوض، یک آبجکت خاص به نام "generator object" برمی‌گردانند که به وسیله آن اجرای تابع را می‌توان کنترل کرد.
2922

3023
برای مثال:
3124

3225
```js run
33-
3426
function* generateSequence() {
35-
3627
yield 1;
37-
3828
yield 2;
39-
4029
return 3;
41-
4230
}
4331

4432
//برمی‌گرداند generator یک آبجکت generator تابع
45-
4633
let generator = generateSequence();
47-
4834
\*!*
49-
5035
alert(generator); // [object Generator]
51-
5236
\*/!*
53-
5437
```
5538

56-
5739
اجرای بدنه تابع هنوز شروع نشده است:
5840

5941
![](generateSequence-1.svg)
6042

6143
متد اصلی یک آبجکت generator متد `()next` است. هنگامی که صدا می‌شود، بدنه تابع تا اولین `yield value` اجرا می‌شود(`value` می‌تواند حذف شود که در این صورت `undefined` است.)؛ سپس اجرای تابع متوقف می‌شود و مقدار yield شده برگرداننده می‌شود.
6244

6345
مقدار برگردانده شده توسط متود `next` همواره یک آبجکت با 2 پراپرتی است:
64-
6546
- `value`: مقدار برگرداننده شده توسط `yield`.
66-
6747
- `done`: یک Boolean است که در صورت اتمام بدنه تابع مقدار true و در غیر این صورت مقدار false دارد.
6848

6949
برای مثال، در کد زیر، یک آبجکت generator ایجاد شده و اولین مقدار `yield` شده توسط آن گرفته شده است:
7050

7151
```js run
72-
7352
function* generateSequence() {
74-
7553
yield 1;
76-
7754
yield 2;
78-
7955
return 3;
80-
8156
}
8257

8358
let generator = generateSequence();
8459

8560
\*!*
86-
8761
let one = generator.next();
88-
8962
\*/!*
9063

9164
alert(JSON.stringify(one)); // {value: 1, done: false}
92-
9365
```
9466

95-
9667
اکنون فقط مقدار اول را گرفته‌ایم و اجرای تابع در خط دوم متوقف شده است:
9768

9869
![](generateSequence-2.svg)
9970

10071
اکنون اگر دوباره `()generator.next` را صدا بزنیم اجرای تابع شروع می‌شود و تا `yield` بعدی و برگردانده شدن مقدار ادامه می‌یابد:
10172

10273
```js
103-
10474
let two = generator.next();
10575

10676
alert(JSON.stringify(two)); // {value: 2, done: false}
107-
10877
```
10978

110-
11179
![](generateSequence-3.svg)
11280

11381
و اگر برای بار سوم آن را صدا بزنیم، اجرای تابع به `return` می‌رسد و تمام می‌شود:
11482

11583
```js
116-
11784
let three = generator.next();
11885

11986
alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*}
120-
12187
```
12288

123-
12489
![](generateSequence-4.svg)
12590

12691
اکنون از روی `done:true` متوجه می‌شویم کار generator تمام شده و `value:3` آخرین مقدار برگردانده شده توسط generator است.
12792

12893
دیگر صدا کردن `()generator.next` منطقی نیست. اگر این کار را انجام دهیم، آبجکت یکسانی با `done:true` برگردانده می‌شود.
12994

13095
```smart header="`function* f(…)`یا`function *f(…)`؟"
131-
13296
هر دو سینتکس صحیح هستند.
13397

13498
ولی معمولا اولی ترجیح داده می‌شود؛ چون `*` نوع تابع و نه نام تابع را مشخص می‌کند.
135-
13699
````
137100
138101
## generatorها iterable هستند.
@@ -142,80 +105,55 @@ alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*}
142105
با استفاد از `for..of` می‌توان از `value` آن‌ها استفاده کرد:
143106
144107
```js run
145-
146108
function* generateSequence() {
147-
148109
yield 1;
149-
150110
yield 2;
151-
152111
return 3;
153-
154112
}
155113
156114
let generator = generateSequence();
157115
158116
for(let value of generator) {
159-
160117
alert(value); // ابتدا 1 و سپس 2
161-
162118
}
163-
164119
````
165120

166121
این شیوه از صدا کردن `next` تمیزتر است؛ این‌گونه فکر نمی‌کنید؟
167122

168123
...اما دقت کنید: مثال بالا ابتدا `1` و سپس `2` را نشان داد؛ خبری از `3` نیست!
169124

170125
علت این اتفاق این است که `for..of` آخرین مقدار را هنگامی که `done:true` است در نظر نمی‌گیرد. هنگام برگرداندن آخرین مقدار با `return`، بر خلاف `done:true`، `yield` است. در نتیجه برای نشان دادن تمام مقادیر باید آن‌ها را با `yield` برگردانیم:
171-
```js run
172126

127+
```js run
173128
function* generateSequence() {
174-
175129
yield 1;
176-
177130
yield 2;
178-
179131
\*!*
180-
181132
yield 3;
182-
183133
\*/!*
184-
185134
}
186135

187136
let generator = generateSequence();
188137

189138
for(let value of generator) {
190-
191139
alert(value); // ابتدا 1 سپس 2 و بعد از آن 3
192-
193140
}
194-
195141
```
196142

197-
198143
از آنجایی که generatorها iterable هستند، از تمام functionality آن‌ها نیز برای generatorها می‌توان استفاده کرد؛ مثل spread syntax `...`:
199144

200145
```js run
201-
202146
function* generateSequence() {
203-
204147
yield 1;
205-
206148
yield 2;
207-
208149
yield 3;
209-
210150
}
211151

212152
let sequence = [0, ...generateSequence()];
213153

214154
alert(sequence); // 0, 1, 2, 3
215-
216155
```
217156

218-
219157
در کد بالا، `()generateSequence...`، باعث می‌شود آبجکت generator که iterable هم هست به آرایه‌ای از اعداد تبدیل شود.(درباره spread syntax در چپتر [](info:rest-parameters-spread#spread-syntax) بیشتر بخوانید.)
220158

221159
## استفاده از generatorها برای iterableها
@@ -225,85 +163,52 @@ alert(sequence); // 0, 1, 2, 3
225163
کد آن به شرح زیر بود:
226164

227165
```js run
228-
229166
let range = {
230-
231167
from: 1,
232-
233168
to: 5,
234169

235170
// در ابتدا این متود را فقط یک بار صدا می‌کند for..of range
236-
237171
[Symbol.iterator]() {
238-
239172
// این، آبجکت ایتریتور را باز می‌گرداند:
240-
241173
// فقط با آن آبجکت کار می‌کند و از آن مقادیر بعدی را می‌خواند for..of سپس
242174
return {
243-
244175
current: this.from,
245-
246176
last: this.to,
247177

248178
// صدا می‌شود for..of توسط iteration در هر next()
249-
250179
next() {
251-
252-
// باید مقدار را به عنوان یک آبجکت برگرداند:
253-
// {value: ..., done: ...}
254-
180+
// {value: ..., done: ...}:باید مقدار را به عنوان یک آبجکت برگرداند
255181
if (this.current <= this.last) {
256-
257182
return { done: false, value: this.current++ };
258-
259183
} else {
260-
261184
return { done: true };
262-
263185
}
264-
265186
},
266-
267187
};
268-
269188
},
270-
271189
};
272190

273191
// برمی‌گرداند range.to تا range.from اعداد را از range روی iteration
274192
alert([...range]); // 1,2,3,4,5
275-
276193
```
277194

278-
279195
می‌توان از یک تابع generator برای iteration به جای Symbol.iterator استفاده کرد.
280196

281197
این همان `range` اما بسیار جمع و جور تر است:
282198

283199
```js run
284-
285200
let range = {
286-
287201
from: 1,
288-
289202
to: 5,
290203

291-
\*[Symbol.iterator]() {
292-
293-
//[Symbol.iterator] نسخه جمع و جور : function*()
294-
204+
*[Symbol.iterator]() {//[Symbol.iterator] نسخه جمع و جور : function*()
295205
for (let value = this.from; value <= this.to; value++) {
296-
297206
yield value;
298-
299207
}
300-
301208
},
302-
303209
};
304210

305211
alert([...range]); // 1,2,3,4,5
306-
307212
```
308213

309214

0 commit comments

Comments
 (0)