рдЕрдкрдиреЗ рдХреЛрдгреАрдпрдЬреЗрдПрд╕ рдмрдирд╛рдПрдВ: рднрд╛рдЧ 1 - рд╕реНрдХреЛрдк рдФрд░ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ

рдХреЛрдгреАрдп рдПрдХ рдкрд░рд┐рдкрдХреНрд╡ рдФрд░ рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдврд╛рдВрдЪрд╛ рд╣реИред рдпрд╣ рдХрд╛рдлреА рдмрдбрд╝реА рд╣реИ рдФрд░ рдХрдИ рдирдИ рдЕрд╡рдзрд╛рд░рдгрд╛рдУрдВ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИ рдЬрд┐рдиреНрд╣реЗрдВ рдкреНрд░рднрд╛рд╡реА рдврдВрдЧ рд╕реЗ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕рдореЗрдВ рдорд╣рд╛рд░рдд рд╣рд╛рд╕рд┐рд▓ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЕрдзрд┐рдХрд╛рдВрд╢ рдбреЗрд╡рд▓рдкрд░реНрд╕ рдЬреЛ рдПрдВрдЧреБрд▓рд░ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реЛрддреЗ рд╣реИрдВ, рд╡рд╣реА рдХрдард┐рдирд╛рдЗрдпреЛрдВ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рддреЗ рд╣реИрдВред рдкрд╛рдЪрди рдХрд╛рд░реНрдп рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреНрдпрд╛ рдХрд░рддрд╛ рд╣реИ? рдирд┐рд░реНрджреЗрд╢ рдмрдирд╛рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдХреНрдпрд╛ рд╣реИрдВ? рдПрдХ рд╕реЗрд╡рд╛ рдФрд░ рдПрдХ рдкреНрд░рджрд╛рддрд╛ рдХреЗ рдмреАрдЪ рдЕрдВрддрд░ рдХреНрдпрд╛ рд╣реИ?

рдЗрд╕ рддрдереНрдп рдХреЗ рдмрд╛рд╡рдЬреВрдж рдХрд┐ рдПрдВрдЧреБрд▓рд░ рдХреЗ рдкрд╛рд╕ рдмрд╣реБрдд рдЕрдЪреНрдЫреЗ рджрд╕реНрддрд╛рд╡реЗрдЬ рд╣реИрдВ , рдФрд░ рдХрдИ рдЯрди рдерд░реНрдб-рдкрд╛рд░реНрдЯреА рд╕рдВрд╕рд╛рдзрди рд╣реИрдВ , рддрдХрдиреАрдХ рдХреЛ рдЯреБрдХрдбрд╝реЛрдВ рдореЗрдВ рдлрд╛рдбрд╝рдиреЗ рдФрд░ рдЗрд╕рдХреЗ рдЬрд╛рджреВ рдХреЛ рдкреНрд░рдХрдЯ рдХрд░рдиреЗ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд╕реАрдЦрдиреЗ рдХрд╛ рдХреЛрдИ рдмреЗрд╣рддрд░ рддрд░реАрдХрд╛ рдирд╣реАрдВ рд╣реИред

рд▓реЗрдЦреЛрдВ рдХреА рдЗрд╕ рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ, рдореИрдВ рд╕реНрдХреНрд░реИрдЪ рд╕реЗ AngularJS рдХреЛ рдлрд┐рд░ рд╕реЗ рдмрдирд╛рдиреЗ рдЬрд╛ рд░рд╣рд╛ рд╣реВрдВ ред рд╣рдо рдЗрд╕реЗ рдПрдХ рд╕рд╛рде рдЪрд░рдгрдмрджреНрдз рддрд░реАрдХреЗ рд╕реЗ рдХрд░реЗрдВрдЧреЗ, рдЬрд┐рд╕рдХреЗ рджреМрд░рд╛рди рдЖрдк рдХреЛрдгреАрдп рдХреА рдЖрдВрддрд░рд┐рдХ рд╕рдВрд░рдЪрдирд╛ рдХреЛ рдЕрдзрд┐рдХ рдЧрд╣рд░рд╛рдИ рд╕реЗ рд╕рдордЭреЗрдВрдЧреЗред

рдЗрд╕ рд╢реНрд░реГрдВрдЦрд▓рд╛ рдХреЗ рдкрд╣рд▓реЗ рднрд╛рдЧ рдореЗрдВ, рд╣рдо рд╕реНрдХреЛрдк рдбрд┐рд╡рд╛рдЗрд╕ рдХреЛ рджреЗрдЦреЗрдВрдЧреЗ, рдФрд░ $ eval , $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдФрд░ $ рд▓рд╛рдЧреВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░реЗрдВрдЧреЗред рдПрдВрдЧреБрд▓рд░ рдореЗрдВ рдЧрдВрджреЗ-рдЪреЗрдХрд┐рдВрдЧ рдХреЗ рдбреЗрдЯрд╛ рдХреА рдЬрд╛рдБрдЪ рдХрд░рдирд╛ рдЬрд╛рджреВ рдЬреИрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдЖрдкрдХреЗ рд▓рд┐рдП рдирд╣реАрдВ рд╣реИред

рдЯреНрд░реЗрдирд┐рдВрдЧ


рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реНрд░реЛрдд рдЧрд┐рддреБрдм рдкрд░ рдЙрдкрд▓рдмреНрдз рд╣реИрдВ, рд▓реЗрдХрд┐рди рдореИрдВ рдЖрдкрдХреЛ рд╕рд▓рд╛рд╣ рдирд╣реАрдВ рджреВрдВрдЧрд╛ рдХрд┐ рдЖрдк рдЙрдиреНрд╣реЗрдВ рдХреЗрд╡рд▓ рдЕрдкрдиреЗ рдЖрдк рд╕реЗ рдХреЙрдкреА рдХрд░реЗрдВред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рдореИрдВ рдЬреЛрд░ рджреЗрдХрд░ рдХрд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдЖрдк рд╕рдм рдХреБрдЫ рд╕реНрд╡рдпрдВ рдХрд░реЗрдВ, рдХрджрдо рд╕реЗ рдХрджрдо, рдХреЛрдб рдХреЗ рд╕рд╛рде рдЦреЗрд▓ рд░рд╣реЗ рд╣реИрдВ рдФрд░ рдЗрд╕рдореЗрдВ рдмрд╣рдХ рд░рд╣реЗ рд╣реИрдВред рдкрд╛рда рдореЗрдВ рдореИрдВ JS рдмрд┐рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВ, рдЗрд╕рд▓рд┐рдП рдЖрдк рдкреГрд╖реНрда рдЫреЛрдбрд╝рдиреЗ рдХреЗ рдмрд┐рдирд╛ рднреА рдХреЛрдб рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ ( рд▓рдЧрднрдЧ рдкреНрд░рддрд┐ред - рдЕрдиреБрд╡рд╛рдж рдореЗрдВ рдХреЗрд╡рд▓ JS рдмрд┐рди рдХреЛрдб рдХреЗ рд▓рд┐рдВрдХ рд╣реЛрдВрдЧреЗ)ред

рд╣рдо рдРрд░реЛ рдФрд░ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХреЗ рд╕рд╛рде рдХреБрдЫ рдирд┐рдореНрди-рд╕реНрддрд░реАрдп рд╕рдВрдЪрд╛рд▓рди рдХреЗ рд▓рд┐рдП рд▓реЛ-рдбреИрд╢ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред рдХреЛрдгреАрдп рд╕реНрд╡рдпрдВ рд▓реЛ-рдбреИрд╢ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдорд╛рд░реЗ рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдпрд╣ рд╕рдВрднрд╡ рдХреЗ рд░реВрдк рдореЗрдВ рдЯреЗрдореНрдкрд▓реЗрдЯ рдХрдо-рд╕реНрддрд░ рдХреЛрдб рдХреЗ рдмрд╣реБрдд рд╕реЗ рдирд┐рдХрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИред рд╣рд░ рдЬрдЧрд╣ рдЖрдкрдХреЛ ( _ ) рдХреЛрдб (рдЕрдВрдбрд░рд╕реНрдХреЛрд░) рдореЗрдВ рдорд┐рд▓рддреЗ рд╣реИрдВ, рд▓реЛ-рдбреИрд╢ рдлрд╝рдВрдХреНрд╢рди рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред

рд╣рдо рд╕рд┐рдВрдкрд▓ рдЪреЗрдХ рдХреЗ рд▓рд┐рдП рднреА рдХрдВрд╕реЛрд▓.рдРрд╕рд░реНрдЯ рдлрдВрдХреНрд╢рди рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░реЗрдВрдЧреЗред рдпрд╣ рд╕рднреА рдЖрдзреБрдирд┐рдХ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╡рд╛рддрд╛рд╡рд░рдг рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

рдпрд╣рд╛рдБ рд▓реЛ-рдбреИрд╢ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдФрд░ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдореЗрдВ рдореБрдЦрд░ рд╣реИ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
var a = [1, 2, 3]; var b = [1, 2, 3]; var c = _.map(a, function(i) { return i * 2; }); console.assert(a !== b); console.assert(_.isEqual(a, b)); console.assert(_.isEqual([2, 4, 6], c)); 

рдХрдВрд╕реЛрд▓:
 рд╕рдЪ
 рд╕рдЪ
 рд╕рдЪ 


рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ - рд╕реНрдХреЛрдк (рдЧреБрдВрдЬрд╛рдЗрд╢)


рдХреЛрдгреАрдп рд╕реНрдХреЛрдк рдСрдмреНрдЬреЗрдХреНрдЯ рдирд┐рдпрдорд┐рдд рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИрдВ, рдЬрд┐рдирд╕реЗ рдЖрдк рдПрдХ рдорд╛рдирдХ рддрд░реАрдХреЗ рд╕реЗ рдЧреБрдг рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред рд╡реЗ рд╕реНрдХреЛрдк рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдмрдирд╛рдП рдЧрдП рд╣реИрдВред рдЖрдЗрдП рдЗрд╕рдХрд╛ рд╕рд░рд▓рддрдо рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд▓рд┐рдЦреЗрдВ:

 function Scope() { } 

рдЕрдм, рдирдП рдСрдкрд░реЗрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЖрдк рдЧреБрдВрдЬрд╛рдЗрд╢ рдСрдмреНрдЬреЗрдХреНрдЯ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЙрдирдореЗрдВ рдЧреБрдг рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред

 var aScope = new Scope(); aScope.firstName = 'Jane'; aScope.lastName = 'Smith'; 

рдЗрди рдЧреБрдгреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреБрдЫ рдЦрд╛рд╕ рдирд╣реАрдВ рд╣реИред рдХрд┐рд╕реА рд╡рд┐рд╢реЗрд╖ рд╕реЗрдЯрд░ рдХреЛ рдЕрд╕рд╛рдЗрди рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ; рдореВрд▓реНрдп рдкреНрд░рдХрд╛рд░реЛрдВ рдкрд░ рдХреЛрдИ рдкреНрд░рддрд┐рдмрдВрдз рдирд╣реАрдВ рд╣реИред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рд╕рднреА рдЬрд╛рджреВ рджреЛ рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рд╣реИрдВ: $ рдШрдбрд╝реА рдФрд░ $ рдкрдЪ ред

рдирд┐рдЧрд░рд╛рдиреА рд╡рд╕реНрддреБ рдЧреБрдг: $ рдШрдбрд╝реА рдФрд░ $ рдкрдЪ


$ рдШрдбрд╝реА рдФрд░ $ рдкрдЪ рдПрдХ рд╣реА рд╕рд┐рдХреНрдХреЗ рдХреЗ рджреЛ рдкрд╣рд▓реВ рд╣реИрдВред рд╕рд╛рде рдореЗрдВ рд╡реЗ рдПрдВрдЧреБрд▓рд░ рдореЗрдВ рдХрд┐рди-рдХрд┐рди рд╕реНрдХреЛрдк рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХрд╛ рдХреЛрд░ рдмрдирд╛рддреЗ рд╣реИрдВ: рдбреЗрдЯрд╛ рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ред

$ рдШрдбрд╝реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ , рдЖрдк рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреЗ рд▓рд┐рдП рдПрдХ "рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ" рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред рдПрдХ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рд╡рд╣ рд╣реИ рдЬреЛ рдЙрдЪрд┐рдд рджрд╛рдпрд░реЗ рдореЗрдВ рдмрджрд▓рд╛рд╡ рд╣реЛрдиреЗ рдкрд░ рдЕрдзрд┐рд╕реВрдЪрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рдПрдХ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ $ рдШрдбрд╝реА рдХреЗ рд▓рд┐рдП рджреЛ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкрд╛рд░рд┐рдд рдХрд░рдХреЗ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ:


рдХреЛрдгреАрдп рдореЗрдВ, рдЖрдкрдиреЗ рдПрдХ рдШрдбрд╝реА рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдШрдбрд╝реА рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ред рдпрд╣ рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реИ ("user.firstName" рдЬреИрд╕рд╛ рдХреБрдЫ) рдЬрд┐рд╕реЗ рдЖрдк HTML рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕реЗ рдПрдХ рдирд┐рд░реНрджреЗрд╢рди рдХреА рд╡рд┐рд╢реЗрд╖рддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ, рдпрд╛ рд╕реАрдзреЗ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╕реЗред рдпрд╣ рд░реЗрдЦрд╛ рд╣рдорд╛рд░реА рддрд░рд╣ рдПрдХ рдШрдбрд╝реА рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ рдПрдВрдЧреБрд▓рд░ рджреНрд╡рд╛рд░рд╛ рдкрд╛рд░реНрд╕ рдФрд░ рд╕рдВрдХрд▓рд┐рдд рдХреА рдЧрдИ рдереАред рд╣рдо рджреЗрдЦреЗрдВрдЧреЗ рдХрд┐ рдпрд╣ рдЕрдЧрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдЙрд╕реА рд▓реЗрдЦ рдореЗрдВ, рд╣рдо рд╡реЙрдЪ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП рдирд┐рдореНрди-рд╕реНрддрд░реАрдп рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХрд╛ рдкрд╛рд▓рди рдХрд░реЗрдВрдЧреЗред

$ рдШрдбрд╝реА рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП , рд╣рдореЗрдВ рд╕рднреА рдкрдВрдЬреАрдХреГрдд рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХреЛрдВ рдХреЛ рдХрд╣реАрдВ рд╕реНрдЯреЛрд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЖрдЗрдП рдЙрдирдХреЗ рд▓рд┐рдП рд╕реНрдХреЛрдк рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдореЗрдВ рдПрдХ рд╕рд░рдгреА рдЬреЛрдбрд╝реЗрдВ:

 function Scope() { this.$$watchers = []; } 

$ $ рдЙрдкрд╕рд░реНрдЧ рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рдЪрд░ рдХреЛрдгреАрдп рдврд╛рдВрдЪреЗ рдореЗрдВ рдирд┐рдЬреА рд╣реИ рдФрд░ рдЗрд╕реЗ рдЖрд╡реЗрджрди рдХреЛрдб рд╕реЗ рдирд╣реАрдВ рдмреБрд▓рд╛рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред

рдЕрдм рдЖрдк рдлрд╝рдВрдХреНрд╢рди $ рд╡реЙрдЪ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣ рджреЛ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рд▓реЗрдЧрд╛ рдФрд░ рдЙрдиреНрд╣реЗрдВ $ $ рд╡реЙрдЪрд░реНрд╕ рд╕рд░рдгреА рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░реЗрдЧрд╛ред рдпрд╣ рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рд╣рд░ рдХрд╛рд░реНрдпрдХреНрд╖реЗрддреНрд░ рд╡рд╕реНрддреБ рдХреЛ рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрд╕реЗ рдПрдХ рдкреНрд░реЛрдЯреЛрдЯрд╛рдЗрдк рдореЗрдВ рд░рдЦреЗрдВ:

 Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn }; this.$$watchers.push(watcher); }; 

рд╕рд┐рдХреНрдХреЗ рдХрд╛ рджреВрд╕рд░рд╛ рдкрд╣рд▓реВ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдлрдВрдХреНрд╢рди рд╣реИред рдпрд╣ рджрд┐рдП рдЧрдП рджрд╛рдпрд░реЗ рдХреЗ рд▓рд┐рдП рдкрдВрдЬреАрдХреГрдд рд╕рднреА рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХреЛрдВ рдХреЛ рд▓реЙрдиреНрдЪ рдХрд░рддрд╛ рд╣реИред рдЖрдЗрдП рдЗрд╕рдХреЗ рд╕рд░рд▓рддрдо рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВ, рдЬрд┐рд╕рдореЗрдВ рд╕рднреА рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХреЛрдВ рдХреЛ рдмрд╕ рд╣рд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдЙрдирдореЗрдВ рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рд╢реНрд░реЛрддрд╛ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВ:

 Scope.prototype.$digest = function() { _.forEach(this.$$watchers, function(watch) { watch.listenerFn(); }); }; 

рдЕрдм рдЖрдк рдПрдХ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдЪрд▓рд╛ рд╕рдХрддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдХреЗ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рдЗрд╕рдХрд╛ рд╢реНрд░реЛрддрд╛ рдХрд╛рд░реНрдп рдХрд░реЗрдЧрд╛:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { _.forEach(this.$$watchers, function(watch) { watch.listenerFn(); }); }; var scope = new Scope(); scope.$watch( function() {console.log('watchFn'); }, function() {console.log('listener'); } ); scope.$digest(); scope.$digest(); scope.$digest(); 

рдХрдВрд╕реЛрд▓:
 "рд╢реНрд░реЛрддрд╛"
 "рд╢реНрд░реЛрддрд╛"
 "рд╢реНрд░реЛрддрд╛" 


рдпрд╣ рдЕрдкрдиреЗ рдЖрдк рдореЗрдВ рд╡рд┐рд╢реЗрд╖ рдЙрдкрдпреЛрдЧреА рдирд╣реАрдВ рд╣реИред рд╣рдо рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреНрдпрд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рд╣реИрдВрдбрд▓рд░ рдХреЛ рдХреЗрд╡рд▓ рддрднреА рд▓реЙрдиреНрдЪ рдХрд┐рдпрд╛ рдЬрд╛рдП рдЬрдм рдШрдбрд╝реА рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рд╕рдВрдХреЗрддрд┐рдд рдбреЗрдЯрд╛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдмрджрд▓ рдЧрдпрд╛ рд╣реЛред

рдбреЗрдЯрд╛ рдкрд░рд┐рд╡рд░реНрддрди рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдирд╛


рдЬреИрд╕рд╛ рдХрд┐ рдкрд╣рд▓реЗ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЗ рд╡реЙрдЪ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдбреЗрдЯрд╛ рд╡рд╛рдкрд╕ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП рдЬрд┐рдирдХреЗ рдкрд░рд┐рд╡рд░реНрддрди рдЗрд╕рдХреЗ рд▓рд┐рдП рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВред рдЖрдорддреМрд░ рдкрд░ рдпрд╣ рдбреЗрдЯрд╛ рджрд╛рдпрд░реЗ рдореЗрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП, рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд▓рд┐рдП, рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдЗрд╕рдХреЗ рд▓рд┐рдП рдЧреБрдВрдЬрд╛рдЗрд╢ рдкрд╛рд░рд┐рдд рдХреА рдЬрд╛рддреА рд╣реИред рдХрд╛рд░реНрдпрдХреНрд╖реЗрддреНрд░ рд╕реЗ рдкрд╣рд▓рд╛ рдирд╛рдо рджреЗрдЦрдиреЗ рд╡рд╛рд▓рд╛ рдШрдбрд╝реА рдлрд╝рдВрдХреНрд╢рди рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛:

 function(scope) { return scope.firstName; } 

рдЬреНрдпрд╛рджрд╛рддрд░ рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рдШрдбрд╝реА рдлрд╝рдВрдХреНрд╢рди рдмрд┐рд▓реНрдХреБрд▓ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ: рдпрд╣ рдмреНрдпрд╛рдЬ рдХреА рдбреЗрдЯрд╛ рдХреЛ рджрд╛рдпрд░реЗ рд╕реЗ рдирд┐рдХрд╛рд▓рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рд╡рд╛рдкрд╕ рдХрд░рддрд╛ рд╣реИред

$ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдХрд╛ рдХрд╛рд░реНрдп рдЗрд╕ рд╡реЙрдЪ рдлрдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рд╣реИ рдФрд░ рдкрд┐рдЫрд▓реА рдмрд╛рд░ рд╡рд╛рдкрд╕ рдЖрдиреЗ рдХреЗ рд╕рд╛рде рдЗрд╕рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдореВрд▓реНрдп рдХреА рддреБрд▓рдирд╛ рдХрд░рдирд╛ рд╣реИред рдпрджрд┐ рдорд╛рди рднрд┐рдиреНрди рд╣реИрдВ, рддреЛ рдбреЗрдЯрд╛ "рдЧрдВрджрд╛" рд╣реИ, рдФрд░ рдЖрдкрдХреЛ рд╕рдВрдмрдВрдзрд┐рдд рд╢реНрд░реЛрддрд╛ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдШрдбрд╝реА рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рдЕрдВрддрд┐рдо рд▓реМрдЯрд╛ рд╣реБрдЖ рдореВрд▓реНрдп рдпрд╛рдж рд░рдЦрдирд╛ рдЪрд╛рд╣рд┐рдП, рдФрд░ рдЪреВрдВрдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЗ рд▓рд┐рдП рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЕрдкрдирд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрд╕рдореЗрдВ рдЗрд╕ рдбреЗрдЯрд╛ рдХреЛ рд╕реНрдЯреЛрд░ рдХрд░рдирд╛ рд╕рдмрд╕реЗ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИред рдпрд╣рд╛рдВ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдПрдХ рдирдпрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ рдЬреЛ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЗ рд▓рд┐рдП рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рдХреА рдЬрд╛рдВрдЪ рдХрд░рддрд╛ рд╣реИ:

 Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); }; 

рдПрдХ рдШрдбрд╝реА рд╕рдорд╛рд░реЛрд╣ рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЗ рд▓рд┐рдП рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ, рд╡рд░реНрддрдорд╛рди рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреЛ рдПрдХ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдкреНрд░рд╛рдкреНрдд рдореВрд▓реНрдп рдХреА рддреБрд▓рдирд╛ рдкрд┐рдЫрд▓реЗ рд╡рд┐рд╢реЗрд╖рддрд╛ рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдкрд┐рдЫрд▓реЗ рдПрдХ рдХреЗ рд╕рд╛рде рдХреА рдЬрд╛рддреА рд╣реИред рдпрджрд┐ рдорд╛рди рднрд┐рдиреНрди рд╣реЛрддреЗ рд╣реИрдВ, рддреЛ рд╢реНрд░реЛрддрд╛ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд▓рд┐рдП, рдореВрд▓реНрдпреЛрдВ рдФрд░ рджрд╛рдпрд░реЗ рджреЛрдиреЛрдВ рдХреЛ рд╢реНрд░реЛрддрд╛ рдХреЗ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЕрдВрдд рдореЗрдВ, рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЗ рдЕрдВрддрд┐рдо рдЧреБрдг рдореЗрдВ рдПрдХ рдирдпрд╛ рдорд╛рди рд▓рд┐рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИ рддрд╛рдХрд┐ рдЕрдЧрд▓реА рдмрд╛рд░ рдПрдХ рддреБрд▓рдирд╛ рдХреА рдЬрд╛ рд╕рдХреЗред

рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдЬрдм $ рдкрд╛рдЪрди рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ рддреЛ рд╢реНрд░реЛрддрд╛рдУрдВ рдХреЛ рдЗрд╕ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рдХреИрд╕реЗ рдЯреНрд░рд┐рдЧрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); }; var scope = new Scope(); scope.firstName = 'Joe'; scope.counter = 0; scope.$watch( function(scope) { return scope.firstName; }, function(newValue, oldValue, scope) { scope.counter++; } ); // We haven't run $digest yet so counter should be untouched: console.assert(scope.counter === 0); // The first digest causes the listener to be run scope.$digest(); console.assert(scope.counter === 1); // Further digests don't call the listener... scope.$digest(); scope.$digest(); console.assert(scope.counter === 1); // ... until the value that the watch function is watching changes again scope.firstName = 'Jane'; scope.$digest(); console.assert(scope.counter === 2); 

рдХрдВрд╕реЛрд▓:
 рд╕рдЪ
 рд╕рдЪ
 рд╕рдЪ
 рд╕рдЪ 


рдЕрдм рд╣рдо рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдХреЛрдгреАрдп рджрд╛рдпрд░реЗ рдХреЗ рдореВрд▓ рдХреЛ рд▓рд╛рдЧреВ рдХрд░ рдЪреБрдХреЗ рд╣реИрдВ: рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХреЛрдВ рдХрд╛ рдкрдВрдЬреАрдХрд░рдг рдХрд░рдирд╛ рдФрд░ рдЙрдиреНрд╣реЗрдВ $ рдкрд╛рдЪрди рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ рд▓реЙрдиреНрдЪ рдХрд░рдирд╛ред

рдЕрдм рд╣рдо рдХреЛрдгреАрдп рдореЗрдВ рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреЗ рдкреНрд░рджрд░реНрд╢рди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреБрдЫ рдирд┐рд╖реНрдХрд░реНрд╖ рдирд┐рдХрд╛рд▓ рд╕рдХрддреЗ рд╣реИрдВ:


рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдЕрд▓рд░реНрдЯ


рдпрджрд┐ рдЖрдкрдХреЛ рдЕрд▓рд░реНрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдЪрд▓ рд░рд╣рд╛ рд╣реИ, рддреЛ рдЖрдк рдЗрд╕ рддрдереНрдп рдХрд╛ рд▓рд╛рдн рдЙрдард╛ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдкреНрд░рддреНрдпреЗрдХ рд╡реЙрдЪ рдлрдВрдХреНрд╢рди, $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдХреЗ рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ, рд╢реБрд░реВ рд╣реЛрдирд╛ рдирд┐рд╢реНрдЪрд┐рдд рд╣реИред рдЖрдкрдХреЛ рдмрд╕ рдПрдХ рд╢реНрд░реЛрддрд╛ рдХреЗ рдмрд┐рдирд╛ рдПрдХ рдШрдбрд╝реА рд╕рдорд╛рд░реЛрд╣ рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдЗрд╕реЗ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП, $ рдШрдбрд╝реА рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдпрд╣ рдЬрд╛рдВрдЪрдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рд╢реНрд░реЛрддрд╛ рдХреЛ рдЫреЛрдбрд╝ рджрд┐рдпрд╛ рдирд╣реАрдВ рдЧрдпрд╛ рд╣реИ, рдФрд░ рдпрджрд┐ рдРрд╕рд╛ рд╣реИ, рддреЛ рд╕реНрдЯрдм рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдмрджрд▓реЗ рдореЗрдВ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ:

 Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; this.$$watchers.push(watcher); }; 

рдпрджрд┐ рдЖрдк рдЗрд╕ рдЯреЗрдореНрдкреНрд▓реЗрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдзреНрдпрд╛рди рд░рдЦреЗрдВ рдХрд┐ рдПрдВрдЧреБрд▓рд░ рд╡реЙрдЪ рдлрд╝рдВрдХреНрд╢рди рд╕реЗ рд▓реМрдЯрд╛рдП рдЧрдП рдореВрд▓реНрдп рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддрд╛ рд╣реИ, рднрд▓реЗ рд╣реА рд╢реНрд░реЛрддрд╛ рдлрд╝рдВрдХреНрд╢рди рдШреЛрд╖рд┐рдд рди рд╣реЛред рдпрджрд┐ рдЖрдк рдХреЛрдИ рдорд╛рди рд▓реМрдЯрд╛рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рд▓рд┐рдП рдЬрд╛рдБрдЪ рдореЗрдВ рднрд╛рдЧ рд▓реЗрдЧрд╛ред рдЕрдирд╛рд╡рд╢реНрдпрдХ рдХрд╛рдо рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП - рдмрд╕ рдлрд╝рдВрдХреНрд╢рди рд╕реЗ рдХреБрдЫ рднреА рд╡рд╛рдкрд╕ рди рдХрд░реЗрдВ, рдЕрдкрд░рд┐рднрд╛рд╖рд┐рдд рд╣рдореЗрд╢рд╛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рд╡рд╛рдкрд╕ рдЖ рдЬрд╛рдПрдЧрд╛:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); }; var scope = new Scope(); scope.$watch(function() { console.log('digest listener fired'); }); scope.$digest(); scope.$digest(); scope.$digest(); 

рдХрдВрд╕реЛрд▓:
 "рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд╢реНрд░реЛрддрд╛ рдирд┐рдХрд╛рд▓ рджрд┐рдпрд╛"
 "рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд╢реНрд░реЛрддрд╛ рдирд┐рдХрд╛рд▓ рджрд┐рдпрд╛"
 "рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд╢реНрд░реЛрддрд╛ рдирд┐рдХрд╛рд▓ рджрд┐рдпрд╛" 


рдХрд░реНрдиреЗрд▓ рддреИрдпрд╛рд░ рд╣реИ, рд▓реЗрдХрд┐рди рдЕрднреА рднреА рдкреВрд░реА рддрд░рд╣ рд╕реЗ рджреВрд░ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкрд░рд┐рджреГрд╢реНрдп рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рдирд╣реАрдВ рд░рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИ: рд╢реНрд░реЛрддрд╛ рдХрд╛рд░реНрдп рд╕реНрд╡рдпрдВ рдЧреБрдгреЛрдВ рдХреЛ рдХрд╛рд░реНрдпрдХреНрд╖реЗрддреНрд░ рд╕реЗ рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВред рдпрджрд┐ рдРрд╕рд╛ рд╣реЛрддрд╛ рд╣реИ, рдФрд░ рдПрдХ рдЕрдиреНрдп рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдЗрд╕ рд╕рдВрдкрддреНрддрд┐ рдХреЛ рджреЗрдЦрддрд╛ рд╣реИ, рддреЛ рдпрд╣ рдкрддрд╛ рдЪрд▓ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рдкрд░рд┐рд╡рд░реНрддрди рдХреА рд╕реВрдЪрдирд╛ рдирд╣реАрдВ рдорд┐рд▓реЗрдЧреА, рдХрдо рд╕реЗ рдХрдо рдЗрд╕ рдорд╛рд░реНрдЧ рдореЗрдВ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ :

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {} }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); }; var scope = new Scope(); scope.firstName = 'Joe'; scope.counter = 0; scope.$watch( function(scope) { return scope.counter; }, function(newValue, oldValue, scope) { scope.counterIsTwo = (newValue === 2); } ); scope.$watch( function(scope) { return scope.firstName; }, function(newValue, oldValue, scope) { scope.counter++; } ); // After the first digest the counter is 1 scope.$digest(); console.assert(scope.counter === 1); // On the next change the counter becomes two, but our other watch hasn't noticed this yet scope.firstName = 'Jane'; scope.$digest(); console.assert(scope.counter === 2); console.assert(scope.counterIsTwo); // false // Only sometime in the future, when $digest() is called again, does our other watch get run scope.$digest(); console.assert(scope.counterIsTwo); // true 

рдХрдВрд╕реЛрд▓:
 рд╕рдЪ
 рд╕рдЪ
 рдЭреВрдард╛
 рд╕рдЪ 


рдЪрд▓реЛ рдЗрд╕реЗ рдареАрдХ рдХрд░рддреЗ рд╣реИрдВред

рдЬрдм рддрдХ рдЧрдВрджрд╛ рдбреЗрдЯрд╛ рд╣реИ, $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдХрд░реЗрдВ


рдЖрдкрдХреЛ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдХреЛ рдареАрдХ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рддрд╛рдХрд┐ рдпрд╣ рддрдм рддрдХ рдЪреЗрдХ рдХрд░рддрд╛ рд░рд╣реЗ рдЬрдм рддрдХ рдХрд┐ рджреЗрдЦреЗ рдЧрдП рдорд╛рди рдмрджрд▓рдирд╛ рдмрдВрдж рди рд╣реЛ рдЬрд╛рдПрдБред

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЪрд▓рд┐рдП $ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдХрд╛ рдореМрдЬреВрджрд╛ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдлрдВрдХреНрд╢рди рдХрд╛ рдирд╛рдо рдмрджрд▓рдХрд░ $ $ рдбрд╛рдЗрдЬреЗрдиреЗрд╕реЗ рдХрд░рддреЗ рд╣реИрдВ , рдФрд░ рдЗрд╕реЗ рдмрджрд▓ рджреЗрддреЗ рд╣реИрдВ рддрд╛рдХрд┐ рдпрд╣, рд╕рднреА рдШрдбрд╝реА рдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдПрдХ рдмрд╛рд░ рдЪрд▓ рд░рд╣рд╛ рд╣реЛ, рдПрдХ рдмреВрд▓рд┐рдпрди рд╡реИрд░рд┐рдПрдмрд▓ рд▓реМрдЯрд╛рддрд╛ рд╣реИ рдЬреЛ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рджреЗрдЦреЗ рдЧрдП рдлрд╝реАрд▓реНрдб рдХреЗ рдорд╛рдиреЛрдВ рдореЗрдВ рдХрдо рд╕реЗ рдХрдо рдПрдХ рдмрджрд▓рд╛рд╡ рдерд╛ рдпрд╛ рдирд╣реАрдВ:

 Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; }; 

рдЙрд╕рдХреЗ рдмрд╛рдж, $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдлрд┐рд░ рд╕реЗ рдШреЛрд╖рд┐рдд рдХрд░реЗрдВ рддрд╛рдХрд┐ рдЬрдм рддрдХ рдмрджрд▓рд╛рд╡ рди рд╣реЛ рддрдм рддрдХ рдпрд╣ рд▓реВрдк рдореЗрдВ $ $ $ рдбрд╛рдЗрдЬреЗрдУрд╕ рд╢реБрд░реВ рд╣реЛ:

 Scope.prototype.$digest = function() { var dirty; do { dirty = this.$$digestOnce(); } while (dirty); }; 

$ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдЕрдм рдкрдВрдЬреАрдХреГрдд рд╡реЙрдЪ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдХреЛ рдХрдо рд╕реЗ рдХрдо рдПрдХ рдмрд╛рд░ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддрд╛ рд╣реИред рдпрджрд┐ рдкрд╣рд▓реЗ рдкрд╛рд╕ рдореЗрдВ, рдХрд┐рд╕реА рднреА рджреЗрдЦреЗ рдЧрдП рдорд╛рди рдХреЛ рдмрджрд▓ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдкрд╛рд╕ рдХреЛ "рдЧрдВрджреЗ" рдХреЗ рд░реВрдк рдореЗрдВ рдЪрд┐рд╣реНрдирд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рджреВрд╕рд░рд╛ рдкрд╛рд╕ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИред рдпрд╣ рддрдм рддрдХ рд╣реЛрддрд╛ рд╣реИ рдЬрдм рддрдХ рдкреВрд░реЗ рдорд╛рд░реНрдЧ рдкрд░ рдПрдХ рдПрдХрд▓ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдореВрд▓реНрдп рдХрд╛ рдкрддрд╛ рдирд╣реАрдВ рд▓рдЧрд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ - рд╕реНрдерд┐рддрд┐ рд╕реНрдерд┐рд░ рд╣реЛ рдЬрд╛рддреА рд╣реИред
рдХреЛрдгреАрдп рдореЗрдВ рд╕реНрдХреЛрдкреНрд╕ рдореЗрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ $ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯреЛрд╕ рдлрд╝рдВрдХреНрд╢рди рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рдпрд╣ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдореЗрдВ рд╕реАрдзреЗ рд▓реВрдк рдореЗрдВ рдмрдирд╛рдИ рдЧрдИ рд╣реИред рд╣рдорд╛рд░реЗ рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП, рдкреНрд░рджрд░реНрд╢рди рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд╕реНрдкрд╖реНрдЯрддрд╛ рдФрд░ рдкрдардиреАрдпрддрд╛ рдЕрдзрд┐рдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ, рдпрд╣реА рдХрд╛рд░рдг рд╣реИ рдХрд┐ рд╣рдордиреЗ рдереЛрдбрд╝рд╛ рд╕рд╛ рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд┐рдпрд╛ред

рдпрд╣рд╛рдБ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдореЗрдВ рдирдпрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn ||function() { } }; this.$$watchers.push(watcher); }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; }; Scope.prototype.$digest = function() { var dirty; do { dirty = this.$$digestOnce(); } while (dirty); }; var scope = new Scope(); scope.firstName = 'Joe'; scope.counter = 0; scope.$watch( function(scope) { return scope.counter; }, function(newValue, oldValue, scope) { scope.counterIsTwo = (newValue === 2); } ); scope.$watch( function(scope) { return scope.firstName; }, function(newValue, oldValue, scope) { scope.counter++; } ); // After the first digest the counter is 1 scope.$digest(); console.assert(scope.counter === 1); // On the next change the counter becomes two, and the other watch listener is also run because of the dirty check scope.firstName = 'Jane'; scope.$digest(); console.assert(scope.counter === 2); console.assert(scope.counterIsTwo); 

рдХрдВрд╕реЛрд▓:
 рд╕рдЪ
 рд╕рдЪ
 рд╕рдЪ 


рдШрдбрд╝реА рдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд╕рдВрдмрдВрдз рдореЗрдВ рдПрдХ рдФрд░ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдирд┐рд╖реНрдХрд░реНрд╖ рдирд┐рдХрд╛рд▓рд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ: рд╡реЗ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдСрдкрд░реЗрд╢рди рдХреЗ рджреМрд░рд╛рди рдХрдИ рдмрд╛рд░ рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕реАрд▓рд┐рдП рдЕрдХреНрд╕рд░ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рд╡реЙрдЪ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдмреЗрдорддрд▓рдм рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП: рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдХреЛрдИ рд╕рд╛рдЗрдб рдЗрдлреЗрдХреНрдЯ рдирд╣реАрдВ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рдпрд╛ рдРрд╕реЗ рд╕рд╛рдЗрдб рдЗрдлреЗрдХреНрдЯреНрд╕ рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдП рдЬрд┐рдирдХреЗ рд▓рд┐рдП рдпрд╣ рдХрдИ рдмрд╛рд░ рдареАрдХ рдХрд╛рдо рдХрд░реЗрдЧрд╛ред рдпрджрд┐, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдШрдбрд╝реА рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ, рдПрдХ AJAX рдЕрдиреБрд░реЛрдз рд╣реИ, рддреЛ рдЗрд╕ рдЕрдиреБрд░реЛрдз рдХреЛ рдХрд┐рддрдиреА рдмрд╛рд░ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЗрд╕рдХреА рдХреЛрдИ рдЧрд╛рд░рдВрдЯреА рдирд╣реАрдВ рд╣реИред

рд╣рдорд╛рд░реЗ рдореМрдЬреВрджрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рдПрдХ рдмрдбрд╝реА рдЦрд╛рдореА рд╣реИ: рджреЛ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдПрдХ-рджреВрд╕рд░реЗ рдХреЗ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░реЗрдВрдЧреЗ рддреЛ рдХреНрдпрд╛ рд╣реЛрдЧрд╛? рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕реНрдерд┐рддрд┐ рдХрднреА рд╕реНрдерд┐рд░ рдирд╣реАрдВ рд╣реЛрддреА рд╣реИ? рдЗрд╕реА рддрд░рд╣ рдХреА рд╕реНрдерд┐рддрд┐ рдиреАрдЪреЗ рджрд┐рдП рдЧрдП рдХреЛрдб рдореЗрдВ рд▓рд╛рдЧреВ рдХреА рдЧрдИ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдХреЗ рд▓рд┐рдП рдХреЙрд▓ рдХреА рдЯрд┐рдкреНрдкрдгреА рдХреА рдЬрд╛рддреА рд╣реИред

рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдХреНрдпрд╛ рд╣реЛрдЧрд╛:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; this.$$watchers.push(watcher); }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; }; Scope.prototype.$digest = function() { var dirty; do { dirty = this.$$digestOnce(); } while (dirty); }; var scope = new Scope(); scope.counter1 = 0; scope.counter2 = 0; scope.$watch( function(scope) { return scope.counter1; }, function(newValue, oldValue, scope) { scope.counter2++; } ); scope.$watch( function(scope) { return scope.counter2; }, function(newValue, oldValue, scope) { scope.counter1++; } ); // Uncomment this to run the digest // scope.$digest(); console.log(scope.counter1); 

рдХрдВрд╕реЛрд▓:
 0 


JSBin рдереЛрдбрд╝реА рджреЗрд░ рдХреЗ рдмрд╛рдж рдлрд╝рдВрдХреНрд╢рди рдХреЛ рд░реЛрдХ рджреЗрддрд╛ рд╣реИ (рдореЗрд░реА рдорд╢реАрди рдкрд░ рд▓рдЧрднрдЧ 100,000 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпрд╛рдВ рд╣реЛрддреА рд╣реИрдВ)ред рдпрджрд┐ рдЖрдк рдЗрд╕ рдХреЛрдб рдХреЛ рдЪрд▓рд╛рддреЗ рд╣реИрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдиреЛрдб рдХреЗ рддрд╣рдд, рддреЛ рдпрд╣ рд╣рдореЗрд╢рд╛ рдХреЗ рд▓рд┐рдП рдЪрд▓реЗрдЧрд╛ред

$ рдкрдЪрдиреЗ рдореЗрдВ рдЕрд╕реНрдерд┐рд░рддрд╛ рд╕реЗ рдЫреБрдЯрдХрд╛рд░рд╛


рд╣рдореЗрдВ рдмрд╕ рдЗрддрдирд╛ рдХрд░рдирд╛ рд╣реИ рдХрд┐ $ рдкрд╛рдЪрди рдХреЛ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рддрдХ рд╕реАрдорд┐рдд рдХрд░рдирд╛ рд╣реИред рдпрджрд┐ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рдХреЗ рд╕рдорд╛рдкреНрдд рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рднреА рдЧреБрдВрдЬрд╛рдЗрд╢ рдмрджрд▓рддреА рд░рд╣рддреА рд╣реИ, рддреЛ рд╣рдо рдЕрдкрдиреЗ рд╣рд╛рдереЛрдВ рдХреЛ рдКрдкрд░ рдЙрдард╛рддреЗ рд╣реИрдВ рдФрд░ рдЫреЛрдбрд╝ рджреЗрддреЗ рд╣реИрдВ - рд╢рд╛рдпрдж рд░рд╛рдЬреНрдп рдХрднреА рд╕реНрдерд┐рд░ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рдПрдХ рдЕрдкрд╡рд╛рдж рдХреЛ рдлреЗрдВрдХ рджрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╕реНрдХреЛрдк рдХреА рд╕реНрдерд┐рддрд┐ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреНрдпрд╛ рд╣реЛрдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реИред

рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рдХреА рдЕрдзрд┐рдХрддрдо рд╕рдВрдЦреНрдпрд╛ рдХреЛ рдЯреАрдЯреАрдПрд▓ (рдЬреАрд╡рди рдХреЗ рд▓рд┐рдП рд╕рдордп рдХреЗ рд▓рд┐рдП рдХрдо) рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ, рдЗрд╕реЗ 10 рдкрд░ рд╕реЗрдЯ рдХрд░реЗрдВред рдпрд╣ рд╕рдВрдЦреНрдпрд╛ рдЫреЛрдЯреА рд▓рдЧ рд╕рдХрддреА рд╣реИ (рд╣рдо рдмрд╕ рд▓рдЧрднрдЧ 100,000 рдмрд╛рд░ рдкрдЪрддреЗ рд╣реИрдВ), рд▓реЗрдХрд┐рди рдзреНрдпрд╛рди рд░рдЦреЗрдВ рдХрд┐ рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдПрдХ рдкреНрд░рджрд░реНрд╢рди рдореБрджреНрджрд╛ рд╣реИ - рдкрд╛рдЪрди рдЕрдХреНрд╕рд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рд╣рд░ рдмрд╛рд░ рд╣рд░ рдШрдбрд╝реА рдХрд╛рд░реНрдп рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрд╣ рд╕рдВрднрд╡ рдирд╣реАрдВ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдкрд╛рд╕ 10 рд╕реЗ рдЕрдзрд┐рдХ рдШрдбрд╝реА-рдлрд╝рдВрдХреНрд╢рди рдПрдХ рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ рд╡реНрдпрд╡рд╕реНрдерд┐рдд рд╣реЛрдВрдЧреЗред
рдХреЛрдгреАрдп TTL рдЕрдиреБрдХреВрд▓рди рдпреЛрдЧреНрдп рд╣реИред рд╣рдо рднрд╡рд┐рд╖реНрдп рдХреЗ рд▓реЗрдЦреЛрдВ рдореЗрдВ рдЗрд╕ рдкрд░ рд╡рд╛рдкрд╕ рдЖрдПрдВрдЧреЗ рдЬрдм рд╣рдо рдкреНрд░рджрд╛рддрд╛рдУрдВ рдФрд░ рдирд┐рд░реНрднрд░рддрд╛ рдЗрдВрдЬреЗрдХреНрд╢рди рдкрд░ рдЪрд░реНрдЪрд╛ рдХрд░реЗрдВрдЧреЗред

рдареАрдХ рд╣реИ, рдЪрд▓реЛ рдЬрд╛рд░реА рд░рдЦреЗрдВ - рдЪрд▓реЛ рдкрд╛рдЪрди рд▓реВрдк рдореЗрдВ рдПрдХ рдХрд╛рдЙрдВрдЯрд░ рдЬреЛрдбрд╝реЗрдВред рдЕрдЧрд░ рд╣рдо TTL рддрдХ рдкрд╣реБрдБрдЪрддреЗ рд╣реИрдВ - рдПрдХ рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХреЗрдВ:

 Scope.prototype.$digest = function() { var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; 

рдкрд┐рдЫрд▓реЗ рдЙрджрд╛рд╣рд░рдг рдХрд╛ рдПрдХ рдЕрджреНрдпрддрди рд╕рдВрд╕реНрдХрд░рдг рдПрдХ рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХрддрд╛ рд╣реИ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; this.$$watchers.push(watcher); }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; }; Scope.prototype.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; var scope = new Scope(); scope.counter1 = 0; scope.counter2 = 0; scope.$watch( function(scope) { return scope.counter1; }, function(newValue, oldValue, scope) { scope.counter2++; } ); scope.$watch( function(scope) { return scope.counter2; }, function(newValue, oldValue, scope) { scope.counter1++; } ); scope.$digest(); 

рдХрдВрд╕реЛрд▓:
 "рдмрд┐рдирд╛ рдкрдврд╝реЗ 10 рдкрд╛рдЪрди рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рддрдХ рдкрд╣реБрдБрдЪ рдЧрдП (рдкрдВрдХреНрддрд┐ 36)" 


рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдиреЗ рдлрд┐рдХреНрд╕реЗрд╢рди рд╕реЗ рдЫреБрдЯрдХрд╛рд░рд╛ рдкрд╛ рд▓рд┐рдпрд╛ред

рдЕрдм рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рд╣рдо рдХреИрд╕реЗ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдХреБрдЫ рдмрджрд▓ рдЧрдпрд╛ рд╣реИред

рдореВрд▓реНрдп рджреНрд╡рд╛рд░рд╛ рдкрд░рд┐рд╡рд░реНрддрди рдХреА рдЬрд╛рдБрдЪ рдХрд░реЗрдВ


рдлрд┐рд▓рд╣рд╛рд▓, рд╣рдо рдирдП рдореВрд▓реНрдпреЛрдВ рдХреА рддреБрд▓рдирд╛ рдкреБрд░рд╛рдиреЗ рд▓реЛрдЧреЛрдВ рдХреЗ рд╕рд╛рде рд╕рдЦреНрдд рд╕рдорд╛рдирддрд╛ рдСрдкрд░реЗрдЯрд░ === рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрд░рддреЗ рд╣реИрдВред рдЬреНрдпрд╛рджрд╛рддрд░ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ: рдЖрджрд┐рдо рдкреНрд░рдХрд╛рд░ (рд╕рдВрдЦреНрдпрд╛, рддрд╛рд░, рдЖрджрд┐) рдХреЗ рд▓рд┐рдП рдкрд░рд┐рд╡рд░реНрддрди рд╕рд╛рдорд╛рдиреНрдп рд░реВрдк рд╕реЗ рдкрд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдпрд╣ рднреА рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рд╡рд╕реНрддреБ рдпрд╛ рд╕рд░рдгреА рдХреЛ рджреВрд╕рд░реЗ рджреНрд╡рд╛рд░рд╛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВред рд▓реЗрдХрд┐рди рдХреЛрдгреАрдп рдкрд░рд┐рд╡рд░реНрддрди рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХрд╛ рдПрдХ рдФрд░ рддрд░реАрдХрд╛ рд╣реИ, рдпрд╣ рдЖрдкрдХреЛ рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рд╕рд░рдгреА рдпрд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рдЕрдВрджрд░ рдХреБрдЫ рдмрджрд▓ рдЧрдпрд╛ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдореВрд▓реНрдп рд╕реЗ рддреБрд▓рдирд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рди рдХрд┐ рд╕рдВрджрд░реНрдн рджреНрд╡рд╛рд░рд╛ ред

рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рддреНрдпрд╛рдкрди рдХреЛ рдмреВрд▓рд┐рдпрди рдкреНрд░рдХрд╛рд░ рдХреЗ рд╡реИрдХрд▓реНрдкрд┐рдХ рддреАрд╕рд░реЗ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЛ $ рд╡реЙрдЪ рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдкрд╛рд╕ рдХрд░рдХреЗ рд╕рдХреНрд░рд┐рдп рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдпрджрд┐ рдпрд╣ рдзреНрд╡рдЬ рд╕рддреНрдп рд╣реИ, рддреЛ рдорд╛рди рдЬрд╛рдБрдЪ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЪрд▓реЛ $ рдШрдбрд╝реА рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рддреЗ рд╣реИрдВ - рд╣рдо рдзреНрд╡рдЬ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВрдЧреЗ рдФрд░ рдЗрд╕реЗ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ ( рд╡реЙрдХрд░ рдЪрд░) рдореЗрдВ рд╕рд╣реЗрдЬреЗрдВрдЧреЗ:

 Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 

рд╣рдо рд╕рднреА рдиреЗ рдСрдмреНрдЬрд░реНрд╡рд░ рдХреЛ рдПрдХ рдЭрдВрдбрд╛ рдЬреЛрдбрд╝ рджрд┐рдпрд╛ рдерд╛, рдЬрдмрд░рди рдбрдмрд▓ рдиреЗрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЗрд╕реЗ рдмреВрд▓рд┐рдпрди рдкреНрд░рдХрд╛рд░ рдХреЗ рд▓рд┐рдП рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рдЬрдм рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рддреАрд╕рд░реЗ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рдмрд┐рдирд╛ $ рд╡реЙрдЪ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рддреЛ valueEq рдХреЛ рдЕрдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЬрд┐рд╕реЗ рд╡реЙрдЪрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ рдЧрд▓рдд рдореЗрдВ рдмрджрд▓ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рдореВрд▓реНрдп рджреНрд╡рд╛рд░рд╛ рдЬрд╛рдБрдЪ рд╕реЗ рддрд╛рддреНрдкрд░реНрдп рдпрд╣ рд╣реИ рдХрд┐ рдпрджрд┐ рдореВрд▓реНрдп рдПрдХ рд╡рд╕реНрддреБ рдпрд╛ рдПрдХ рд╕рд░рдгреА рд╣реИ, рддреЛ рдкреБрд░рд╛рдиреА рдФрд░ рдирдИ рд╕рд╛рдордЧреНрд░реА рджреЛрдиреЛрдВ рдкрд░ рдЬрд╛рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реЛрдЧрд╛ред рдпрджрд┐ рдХреЛрдИ рдорддрднреЗрдж рд╣реИрдВ, рддреЛ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ "рдЧрдВрджреЗ" рдХреЗ рд░реВрдк рдореЗрдВ рдЪрд┐рд╣реНрдирд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдиреЗрд╕реНрдЯреЗрдб рдСрдмреНрдЬреЗрдХреНрдЯ рдпрд╛ рд╕рд░рдгрд┐рдпрд╛рдБ рд╕рд╛рдордЧреНрд░реА рдореЗрдВ рд╣реЛ рд╕рдХрддреА рд╣реИрдВ, рдЬрд┐рд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдЙрдиреНрд╣реЗрдВ рдореВрд▓реНрдп рджреНрд╡рд╛рд░рд╛ рдкреБрдирд░рд╛рд╡рд░реНрддреА рдЬрд╛рдБрдЪ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред

рдХреЛрдгреАрдп рдХрд╛ рдЕрдкрдирд╛ рдореВрд▓реНрдп рддреБрд▓рдирд╛рддреНрдордХ рдХрд╛рд░реНрдп рд╣реИ , рд▓реЗрдХрд┐рди рд╣рдо рд▓реЛ-рдбреИрд╢ рдореЗрдВ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред рдЖрдЗрдП рдПрдХ рддреБрд▓рдирд╛ рдлрд╝рдВрдХреНрд╢рди рд▓рд┐рдЦреЗрдВ рдЬреЛ рдорд╛рдиреЛрдВ рдФрд░ рдзреНрд╡рдЬ рдХреА рдПрдХ рдЬреЛрдбрд╝реА рд▓реЗрддрд╛ рд╣реИ:

 Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue; } }; 

"рдореВрд▓реНрдп рджреНрд╡рд╛рд░рд╛" рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдкреНрд░реЗрдорд┐рдХрд╛ рдХреЗ рд▓рд┐рдП "рдкреБрд░рд╛рдиреЗ рдореВрд▓реНрдпреЛрдВ" рдХреЛ рд╕рд╣реЗрдЬрдирд╛ рднреА рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдпрд╣ рдХреЗрд╡рд▓ рд╡рд░реНрддрдорд╛рди рдореВрд▓реНрдпреЛрдВ рдХреЗ рд▓рд┐рдВрдХ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдХрд┐рдП рдЧрдП рдХрд┐рд╕реА рднреА рдмрджрд▓рд╛рд╡ рдХреЛ рд╣рдо рд╕рдВрджрд░реНрдн рджреНрд╡рд╛рд░рд╛ рд╕рдВрдЧреНрд░рд╣рд┐рдд рд╡рд╕реНрддреБ рдореЗрдВ рднреА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВрдЧреЗред рд╣рдо рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдХреБрдЫ рдмрджрд▓ рдЧрдпрд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВ рдЕрдЧрд░ рдПрдХ рд╣реА рдбреЗрдЯрд╛ рдХреЗ рджреЛ рд▓рд┐рдВрдХ рд╣рдореЗрд╢рд╛ $ $ рдореЗрдВ рдЧрд┐рд░рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рд╣реИред рдЗрд╕рд▓рд┐рдП, рд╣рдореЗрдВ рд╕рд╛рдордЧреНрд░реА рдХреА рдПрдХ рдЧрд╣рд░реА рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдмрдирд╛рдиреА рд╣реЛрдЧреА, рдФрд░ рдЗрд╕ рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдХреЛ рд╕рд╣реЗрдЬрдирд╛ рд╣реЛрдЧрд╛ред

рддреБрд▓рдирд╛рддреНрдордХ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдХреЛрдгреАрдп рдбреЗрдЯрд╛ рдХреА рдЧрд╣рд░реА рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдлрд╝рдВрдХреНрд╢рди рд╣реИрдВ , рд▓реЗрдХрд┐рди рд╣рдо рд▓реЛ-рдбреИрд╢ рд╕реЗ рдЙрд╕реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред рдЖрдЗрдП $ $ рдбрд╛рдЗрдЬреЗрдСрди рдХреЛ рдкрд░рд┐рд╖реНрдХреГрдд рдХрд░реЗрдВ рддрд╛рдХрд┐ рдпрд╣ рддреБрд▓рдирд╛ рдХреЗ рд▓рд┐рдП $ $ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗ рдФрд░ рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ рддреЛ рдЖрдЦрд┐рд░реА рдореЗрдВ рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдмрдирд╛рддрд╛ рд╣реИ:

 Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; 

рдЕрдм рдЖрдк рдорд╛рдиреЛрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░рдиреЗ рдХреЗ рджреЛ рддрд░реАрдХреЛрдВ рдХреЗ рдмреАрдЪ рдЕрдВрддрд░ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { }, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue; } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; Scope.prototype.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; var scope = new Scope(); scope.counterByRef = 0; scope.counterByValue = 0; scope.value = [1, 2, {three: [4, 5]}]; // Set up two watches for value. One checks references, the other by value. scope.$watch( function(scope) { return scope.value; }, function(newValue, oldValue, scope) { scope.counterByRef++; } ); scope.$watch( function(scope) { return scope.value; }, function(newValue, oldValue, scope) { scope.counterByValue++; }, true ); scope.$digest(); console.assert(scope.counterByRef === 1); console.assert(scope.counterByValue === 1); // When changes are made within the value, the by-reference watcher does not notice, but the by-value watcher does. scope.value[2].three.push(6); scope.$digest(); console.assert(scope.counterByRef === 1); console.assert(scope.counterByValue === 2); // Both watches notice when the reference changes. scope.value = {aNew: "value"}; scope.$digest(); console.assert(scope.counterByRef === 2); console.assert(scope.counterByValue === 3); delete scope.value; scope.$digest(); console.assert(scope.counterByRef === 3); console.assert(scope.counterByValue === 4); 

рдХрдВрд╕реЛрд▓:
рд╕рдЪ
рд╕рдЪ
рд╕рдЪ
рд╕рдЪ
рд╕рдЪ
рд╕рдЪ 


рдореВрд▓реНрдп рд╕реЗ рд╕рддреНрдпрд╛рдкрди рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╕рдВрджрд░реНрдн рджреНрд╡рд╛рд░рд╛ рд╕рддреНрдпрд╛рдкрди рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдкрд░ рдЕрдзрд┐рдХ рдорд╛рдВрдЧ рд╣реИред рдиреЗрд╕реНрдЯреЗрдб рд╕рдВрд░рдЪрдирд╛рдУрдВ рдкрд░ Iterating рдореЗрдВ рд╕рдордп рд▓рдЧрддрд╛ рд╣реИ, рдФрд░ рд╡рд╕реНрддреБрдУрдВ рдХреА рдкреНрд░рддрд┐рдпрд╛рдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рд╕реЗ рдореЗрдореЛрд░реА рдХреА рдЦрдкрдд рдмрдврд╝ рдЬрд╛рддреА рд╣реИред рдпрд╣реА рдХрд╛рд░рдг рд╣реИ рдХрд┐ рдХреЛрдгреАрдп рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд╕рддреНрдпрд╛рдкрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рдЖрдкрдХреЛ рдзреНрд╡рдЬ рдХреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред
рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рд▓рд┐рдП рдореВрд▓реНрдпреЛрдВ рдХреА рдЬрд╛рдБрдЪ рдХреЗ рд▓рд┐рдП рдХреЛрдгреАрдп рдХрд╛ рддреАрд╕рд░рд╛ рддрдВрддреНрд░ рднреА рд╣реИ: "рд╕рдВрдЧреНрд░рд╣ рджреЗрдЦреЗрдВред" рдореВрд▓реНрдпреЛрдВ рджреНрд╡рд╛рд░рд╛ рдЬрд╛рдБрдЪ рдХреЗ рддрдВрддреНрд░ рдореЗрдВ, рдпрд╣ рд╡рд╕реНрддреБрдУрдВ рдФрд░ рд╕рд░рдгрд┐рдпреЛрдВ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рдХреЛ рдкреНрд░рдХрдЯ рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд, рдиреЗрд╕реНрдЯреЗрдб рд╕реНрддрд░реЛрдВ рдореЗрдВ рдЬрд╛рдиреЗ рдХреЗ рдмрд┐рдирд╛ рдЪреЗрдХ рд╕рд░рд▓ рд╣реИред рдпрд╣ рд╕реНрд╡рд╛рднрд╛рд╡рд┐рдХ рд░реВрдк рд╕реЗ рддреЗрдЬ рд╣реИред рд╡реЙрдЪрд┐рдВрдЧ рд╕рдВрдЧреНрд░рд╣ $ watchCollection рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЙрдкрд▓рдмреНрдз рд╣реИ - рд╣рдо рд╢реНрд░реГрдВрдЦрд▓рд╛ рдХреЗ рдЕрдЧрд▓реЗ рд▓реЗрдЦреЛрдВ рдореЗрдВ рдЗрд╕рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдкрд░ рдЪрд░реНрдЪрд╛ рдХрд░реЗрдВрдЧреЗред

рдорд╛рдиреЛрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рд╣рдореЗрдВ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХреА рдПрдХ рд╡рд┐рд╢реЗрд╖рддрд╛ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

NaN рдорд╛рди


рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдореЗрдВ, рдорд╛рди NaN (рд╕рдВрдЦреНрдпрд╛ рдирд╣реАрдВ) рдЦреБрдж рдХреЗ рдмрд░рд╛рдмрд░ рдирд╣реАрдВ рд╣реИред рдпрд╣ рдЕрдЬреАрдм рд▓рдЧ рд╕рдХрддрд╛ рд╣реИ, рд╢рд╛рдпрдж рдЗрд╕рд▓рд┐рдП рдХрд┐ рдпрд╣ рд╣реИред рд╣рдордиреЗ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рдорд╛рдиреЛрдВ рдХреА рдЬрд╛рдБрдЪ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ NaN рдХреЛ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИ , рдЗрд╕рд▓рд┐рдП NaN рдкрд░ рдирдЬрд╝рд░ рд░рдЦрдиреЗ рд╡рд╛рд▓рд╛ рдШрдбрд╝реА рдлрд╝рдВрдХреНрд╢рди рд╣рдореЗрд╢рд╛ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ "рдЧрдВрджреЗ" рдХреЗ рд░реВрдк рдореЗрдВ рдЪрд┐рд╣реНрдирд┐рдд рдХрд░реЗрдЧрд╛ред

"рд╡реИрд▓реНрдпреВ рдмрд╛рдп" рдЯреЗрд╕реНрдЯ рдореЗрдВ, рдЗрд╕ рдорд╛рдорд▓реЗ рдХреЛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд▓реЛ-рдбреИрд╢ рд╕реЗ рдЖрдЗрд╕реНрдХрд▓ рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИ ред "рд╕рдВрджрд░реНрдн рджреНрд╡рд╛рд░рд╛" рдЪреЗрдХ рдореЗрдВ, рд╣рдореЗрдВ рдЗрд╕реЗ рд╕реНрд╡рдпрдВ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рддреЛ рдЪрд▓реЛ $ $ рдХреЛ рдкрд░рд┐рд╖реНрдХреГрдд рдХрд░ рд░рд╣реЗ рд╣реИрдВ :

 Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; 

рдЕрдм NaN рдХреЗ рд╕рд╛рде рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдЙрдореНрдореАрдж рдХреЗ рдореБрддрд╛рдмрд┐рдХ рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд░рддреЗ рд╣реИрдВ:

JS рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; Scope.prototype.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; var scope = new Scope(); scope.number = 0; scope.counter = 0; scope.$watch( function(scope) { return scope.number; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$digest(); console.assert(scope.counter === 1); scope.number = parseInt('wat', 10); // Becomes NaN scope.$digest(); console.assert(scope.counter === 2); 

рдХрдВрд╕реЛрд▓:
рд╕рдЪ
рд╕рдЪ 


рдЕрдм рд╣рдо рдорд╛рдиреЛрдВ рдХреА рдЬрд╛рдБрдЪ рдХрд░рдиреЗ рд╕реЗ рдзреНрдпрд╛рди рд╣рдЯрд╛рддреЗ рд╣реИрдВ рдХрд┐ рд╣рдо рдЖрд╡реЗрджрди рдХреЛрдб рд╕реЗ рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреЗ рд╕рд╛рде рдХреИрд╕реЗ рдмрд╛рддрдЪреАрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

$ eval - рдЧреБрдВрдЬрд╛рдЗрд╢ рд╕рдВрджрд░реНрдн рдореЗрдВ рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрди


рдХреЛрдгреАрдп рдореЗрдВ рд╕реНрдХреЛрдк рд╕рдВрджрд░реНрдн рдореЗрдВ рдХреЛрдб рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рд╡рд┐рдХрд▓реНрдк рд╣реИрдВред рдЗрдирдореЗрдВ рд╕реЗ рд╕рдмрд╕реЗ рд╕рд░рд▓ $ eval рдлрд╝рдВрдХреНрд╢рди рд╣реИ ред рдпрд╣ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдПрдХ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рд▓реЗрддрд╛ рд╣реИ, рдФрд░ рдХреЗрд╡рд▓ рдПрдХ рдЪреАрдЬ рдЬреЛ рдЗрд╕реЗ рдХрд░рддрд╛ рд╣реИ, рддреБрд░рдВрдд рдЗрд╕реЗ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕реЗ рдПрдХ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд░реНрддрдорд╛рди рдЧреБрдВрдЬрд╛рдЗрд╢ рдкрд╛рд░ рдХрд░рддрд╛ рд╣реИред рдЦреИрд░, рдлрд┐рд░ рдпрд╣ рдирд┐рд╖реНрдкрд╛рджрди рдХрд╛ рдкрд░рд┐рдгрд╛рдо рджреЗрддрд╛ рд╣реИред $ eval рдПрдХ рджреВрд╕рд░рд╛ рдкреИрд░рд╛рдореАрдЯрд░ рднреА рд▓реЗрддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдпрд╣ рдЕрдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред $ Eval рдХрд╛

рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдмрд╣реБрдд рд╕рд░рд▓ рд╣реИ:

 Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; 


$ Eval рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рднреА рдмрд╣реБрдд рд╕рд░рд▓ рд╣реИ:

JS Bin рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; Scope.prototype.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; var scope = new Scope(); scope.number = 1; scope.$eval(function(theScope) { console.log('Number during $eval:', theScope.number); }); 

рдХрдВрд╕реЛрд▓:
"$ Eval рдХреЗ рджреМрд░рд╛рди рд╕рдВрдЦреНрдпрд╛:"
 1 


рддреЛ рдХрд┐рд╕реА рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рдРрд╕реЗ рд╡рд┐рд╕реНрддреГрдд рддрд░реАрдХреЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреНрдпрд╛ рд╣реИ? рдПрдХ рд▓рд╛рдн рдпрд╣ рд╣реИ рдХрд┐ $ eval рдХреЛрдб рдмрдирд╛рддрд╛ рд╣реИ рдЬреЛ рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреА рд╕рд╛рдордЧреНрд░реА рдХреЗ рд╕рд╛рде рдереЛрдбрд╝рд╛ рдЕрдзрд┐рдХ рдкрд╛рд░рджрд░реНрд╢реА рд╣реЛрддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, $ eval $ apply рдХреЗ рд▓рд┐рдП рдПрдХ рдмрд┐рд▓реНрдбрд┐рдВрдЧ рдмреНрд▓реЙрдХ рд╣реИ , рдЬрд┐рд╕реЗ рд╣рдо рдЬрд▓реНрдж рд╣реА рд▓реЗ рд▓реЗрдВрдЧреЗред

рд╣рд╛рд▓рд╛рдВрдХрд┐, $ eval рдХрд╛ рд╕рдмрд╕реЗ рдмрдбрд╝рд╛ рд▓рд╛рдн рдХреЗрд╡рд▓ рддрдм рд╣реЛрдЧрд╛ рдЬрдм рд╣рдо рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдмрдЬрд╛рдп "рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ" рдХреЗ рдЙрдкрдпреЛрдЧ рдкрд░ рдЪрд░реНрдЪрд╛ рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░реЗрдВрдЧреЗред $ рдШрдбрд╝реА рдХреЗ рд╕рд╛рде , рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдХреЛ $ eval рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдкрд╛рд╕ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рд╡рд╣ рдЗрд╕реЗ рд╕рдВрдХрд▓рд┐рдд рдХрд░рддреА рд╣реИ рдФрд░ рдЗрд╕реЗ рджрд╛рдпрд░реЗ рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддреА рд╣реИред рд▓реЗрдЦреЛрдВ рдХреА рдПрдХ рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ рдЖрдЧреЗ, рд╣рдо рдЗрд╕реЗ рд▓рд╛рдЧреВ рдХрд░реЗрдВрдЧреЗред

$ рд▓рд╛рдЧреВ - $ рдкрдЪ рд▓реВрдк рдХреЗ рд╕рд╛рде рдмрд╛рд╣рд░реА рдХреЛрдб рдПрдХреАрдХрд░рдг


рд╕рдВрднрд╡рддрдГ $ рд▓рд╛рдЧреВ рд╕рднреА рд╕реНрдХреЛрдк рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рд╕рдмрд╕реЗ рдкреНрд░рд╕рд┐рджреНрдз рд╣реИ ред рдпрд╣ рдХреЛрдгреАрдп рдХреЗ рд╕рд╛рде рддреАрд╕рд░реЗ рдкрдХреНрд╖ рдХреЗ рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдорд╛рдирдХ рддрд░реАрдХреЗ рдХреЗ рд░реВрдк рдореЗрдВ рддреИрдирд╛рдд рд╣реИред рдФрд░ рдЗрд╕рдХреЗ рдХрд╛рд░рдг рд╣реИрдВред

$ рд▓рд╛рдЧреВ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдПрдХ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддрд╛ рд╣реИ, $ eval рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ , рд▓реЗрдХрд┐рди рдЕрдВрдд рдореЗрдВ рдпрд╣ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ ред рдпрд╣рд╛рдБ рдЗрд╕рдХрд╛ рд╕рд░рд▓рддрдо рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ:

 Scope.prototype.$apply = function(expr) { try { return this.$eval(expr); } finally { this.$digest(); } }; 

$ рдкрд╛рдЪрди рдХреЛ рдирд┐рд░реНрднрд░рддрд╛ рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдВрдд рдореЗрдВ рдмреНрд▓реЙрдХ рдореЗрдВ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ , рднрд▓реЗ рд╣реА рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдЕрдкрд╡рд╛рдж рд╣реЛред

рд╡рд┐рдЪрд╛рд░ рдпрд╣ рд╣реИ рдХрд┐ $ рд▓рд╛рдЧреВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ , рд╣рдо рдЙрд╕ рдХреЛрдб рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдПрдВрдЧреБрд▓рд░ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рдирд╣реАрдВ рд╣реИред рдпрд╣ рдХреЛрдб рдбреЗрдЯрд╛ рдХреЛ рджрд╛рдпрд░реЗ рдореЗрдВ рдмрджрд▓ рд╕рдХрддрд╛ рд╣реИ рдФрд░ $ рд▓рд╛рдЧреВ рд╣реЛрдиреЗ рд╕реЗ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рд╣реЛ рдЬрд╛рдПрдЧрд╛ рдХрд┐ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдЗрди рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рдкрдХрдбрд╝ рд▓реЗрдВред рдЬрдм рд╡реЗ "рдХреЛрдгреАрдп рдЬреАрд╡рди рдЪрдХреНрд░ рдореЗрдВ рдХреЛрдб рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ" рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рддреЗ рд╣реИрдВ рддреЛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЙрдирдХрд╛ рдпрд╣реА рдЕрд░реНрде рд╣реЛрддрд╛ рд╣реИред рдпрд╣ рдФрд░ рдХреБрдЫ рдирд╣реАрдВ рд╣реИред

$ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдореЗрдВ рд▓рд╛рдЧреВ :

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; Scope.prototype.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { return this.$eval(expr); } finally { this.$digest(); } }; var scope = new Scope(); scope.counter = 0; scope.$watch( function(scope) { return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$apply(function(scope) { scope.aValue = 'Hello from "outside"'; }); console.assert(scope.counter === 1); 

рдХрдВрд╕реЛрд▓:
рд╕рдЪ 



рд╡рд┐рд▓рдВрдмрд┐рдд рдирд┐рд╖реНрдкрд╛рджрди - $ evalAsync


рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдореЗрдВ, рдХреЛрдб рдХреЗ рдПрдХ рдЯреБрдХрдбрд╝реЗ рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдирд╛ рдЕрдХреНрд╕рд░ рдЖрд╡рд╢реНрдпрдХ рд╣реЛрддрд╛ рд╣реИ "рдмрд╛рдж рдореЗрдВ" -рдпрд╣ рдирд┐рд╖реНрдкрд╛рджрди рдореЗрдВ рджреЗрд░реА рд╣реИ, рдЬрдм рддрдХ рдХрд┐ рд╡рд░реНрддрдорд╛рди рдирд┐рд╖реНрдкрд╛рджрди рд╕рдВрджрд░реНрдн рдореЗрдВ рд╕рднреА рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдирд╣реАрдВ рд╣реЛ рдЬрд╛рддреЗред рдпрд╣ рдЖрдорддреМрд░ рдкрд░ рд╢реВрдиреНрдп (рдпрд╛ рд╢реВрдиреНрдп рдХреЗ рдХрд░реАрдм) рджреЗрд░реА рдХреЗ рд╕рд╛рде рд╕реЗрдЯрдЯрд╛рдЗрдордЖрдЙрдЯ () рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ ред

рдпрд╣ рддрд░рдХреАрдм рднреА рдХреЛрдгреАрдп рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдореЗрдВ рдХрд╛рдо рдХрд░рддреА рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдЗрд╕рдХреЗ рд▓рд┐рдП $ рдЯрд╛рдЗрдордЖрдЙрдЯ рд╕реЗрд╡рд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдмреЗрд╣рддрд░ рд╣реЛрддрд╛ рд╣реИ , рдЬреЛ рдХрд┐ рдЕрдиреНрдп рдмрд╛рддреЛрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, $ рд▓рд╛рдЧреВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдкрд╛рдЪрди рд▓реВрдк рдХреЗ рд╕рд╛рде рдХреЙрд▓ рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рддрд╛ рд╣реИ ред рд▓реЗрдХрд┐рди рдПрдВрдЧреБрд▓рд░ - $ evalAsync рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрди рдХреЛ рд╕реНрдердЧрд┐рдд рдХрд░рдиреЗ рдХрд╛ рдПрдХ рдФрд░ рддрд░реАрдХрд╛ рд╣реИ

ред рдпрд╣ рдПрдХ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рд▓реЗрддрд╛ рд╣реИ, рдФрд░ рдмрд╛рдж рдореЗрдВ рдЗрд╕рдХреЗ рдирд┐рд╖реНрдкрд╛рджрди рдХреЛ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╛ рддреЛ рд╕реАрдзреЗ рд╡рд░реНрддрдорд╛рди рдкрд╛рдЪрди рдЪрдХреНрд░ рдХреЗ рдЕрдВрджрд░ (рдпрджрд┐ рдпрд╣ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдЪрд▓ рд░рд╣рд╛ рд╣реИ), рдпрд╛ рддреБрд░рдВрдд рдЕрдЧрд▓реЗ рдкрд╛рдЪрди рдЪрдХреНрд░ рд╕реЗ рдкрд╣рд▓реЗред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЖрдк рдкреНрд░реЗрдХреНрд╖рдХ рдХреЗ рд╢реНрд░реЛрддрд╛ рдХрд╛рд░реНрдп рд╕реЗ рд╕реАрдзреЗ рдХрд┐рд╕реА рднреА рдХреЛрдб рдХреЗ рдирд┐рд╖реНрдкрд╛рджрди рдХреЛ рд╕реНрдердЧрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдпрд╣ рдЬрд╛рдирддреЗ рд╣реБрдП рднреА рдХрд┐ рдХреЛрдб рдХреЛ рдЖрд╕реНрдердЧрд┐рдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рд╡рдЬреВрдж, рдЗрд╕реЗ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд▓реВрдк рдХреЗ рдЕрдЧрд▓реЗ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдкрд░ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдореЗрдВ рдпрд╣ рддрдп рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рд╣рдо $$ evalAsync рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдердЧрд┐рдд рдХрд┐рдП рдЧрдП рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд╣рд╛рдБ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░реЗрдВрдЧреЗ ред рд╕реНрдХреЛрдк рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдореЗрдВ рдЗрд╕реЗ рдЖрд░рдВрдн рдХрд░рдХреЗ рдЖрдк рдЗрд╕рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рд░рдгреА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ :

 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; } 

рдЗрд╕рдХреЗ рдмрд╛рдж, $ evalAsync рд╕реНрд╡рдпрдВ рд▓рд┐рдЦреЗрдВ , рдЬреЛ рдХрддрд╛рд░ рдореЗрдВ рдлрд╝рдВрдХреНрд╢рди рдЬреЛрдбрд╝ рджреЗрдЧрд╛:

 Scope.prototype.$evalAsync = function(expr) { this.$$asyncQueue.push({scope: this, expression: expr}); }; 

рд╣рдо рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдХрддрд╛рд░ рд╡рд╕реНрддреБ рдореЗрдВ рдЧреБрдВрдЬрд╛рдЗрд╢ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ рдЗрд╕рдХрд╛ рдХрд╛рд░рдг рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреА рд╡рд┐рд░рд╛рд╕рдд рд╣реИ, рдЬрд┐рд╕реЗ рд╣рдо рдЗрд╕ рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ рдЕрдЧрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рдЪрд░реНрдЪрд╛ рдХрд░реЗрдВрдЧреЗред

рдЕрдм рдкрд╣рд▓реА рдмрд╛рдд рдЬреЛ рд╣рдо $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдореЗрдВ рдХрд░реЗрдВрдЧреЗ, рд╡рд╣ рд╣реИ рдЙрди рд╕рднреА рдлрдВрдХреНрд╢рдиреНрд╕ рдХреЛ рдирд┐рдХрд╛рд▓рдирд╛ рдЬреЛ рджреЗрд░реА рд╕реЗ рд╢реБрд░реВ рд╣реЛрдиреЗ рд╡рд╛рд▓реА рдХрддрд╛рд░ рдореЗрдВ рд╣реИрдВ рдФрд░ $ $ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЙрдиреНрд╣реЗрдВ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддреЗ рд╣реИрдВ :

 Scope.prototype.$digest = function() { var ttl = 10; var dirty; do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; 

рдпрд╣ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдпрджрд┐ рдЖрдкрдиреЗ рдХрд┐рд╕реА рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдирд┐рд╖реНрдкрд╛рджрди рдореЗрдВ рджреЗрд░реА рдХреА рд╣реИ рдФрд░ рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреЛ рдЧрдВрджрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдЪрд┐рд╣реНрдирд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдЖрд╕реНрдердЧрд┐рдд рдХрд╣рд╛ рдЬрд╛рдПрдЧрд╛, рд▓реЗрдХрд┐рди рдЙрд╕реА рдкрд╛рдЪрди рд▓реВрдк рдореЗрдВред

рдпрд╣рд╛рдБ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдХрд┐ рдХреИрд╕реЗ $ evalAsync рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; Scope.prototype.$digest = function() { var ttl = 10; var dirty; do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { return this.$eval(expr); } finally { this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { this.$$asyncQueue.push({scope: this, expression: expr}); }; var scope = new Scope(); scope.asyncEvaled = false; scope.$watch( function(scope) { return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; scope.$evalAsync(function(scope) { scope.asyncEvaled = true; }); console.log("Evaled inside listener: "+scope.asyncEvaled); } ); scope.aValue = "test"; scope.$digest(); console.log("Evaled after digest: "+scope.asyncEvaled); 

рдХрдВрд╕реЛрд▓:
"рд╢реНрд░реЛрддрд╛ рдХреЗ рдЕрдВрджрд░ рдХрд╛ рдореВрд▓реНрдпрд╛рдВрдХрди: рдЧрд▓рдд"
"Evaled after digest: true" 


рджрд╛рдпрд░реЗ рдореЗрдВ рдЖрддреЗ рд╣реИрдВ


$ EvalAsync рдлрд╝рдВрдХреНрд╢рди рдХреБрдЫ рдФрд░ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕реЗ рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд╢реЗрдбреНрдпреВрд▓ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП рдЕрдЧрд░ рдпрд╣ рдЕрднреА рдирд╣реАрдВ рдЪрд▓ рд░рд╣рд╛ рд╣реИред рдЗрд╕рдХрд╛ рдореБрджреНрджрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдЬрдм рднреА рдЖрдк $ evalAsync рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВ , рддреЛ рдЖрдкрдХреЛ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рдЖрдкрдХрд╛ рдЖрд╕реНрдердЧрд┐рдд рдлрд╝рдВрдХреНрд╢рди "рдмрд╣реБрдд рдЬрд▓реНрдж" рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░реЗрдЧрд╛, рдФрд░ рддрдм рдирд╣реАрдВ рдЬрдм рдХреЛрдИ рдФрд░ рдЪреАрдЬрд╝ рдкрдЪрдиреЗ рд▓рдЧреЗ ред

$ evalAsync рдХреЛ рдХрд┐рд╕реА рддрд░рд╣ рд╕рдордЭрдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рдХреНрдпрд╛ рдкрд╛рдЪрди рдЕрднреА рдЪрд▓ рд░рд╣рд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВред рдЗрд╕ рдкреНрд░рдпреЛрдЬрди рдХреЗ рд▓рд┐рдП, рдХреЛрдгреАрдп рдЧреБрдВрдЬрд╛рдЗрд╢ "рдЪрд░рдг" рдирд╛рдордХ рдПрдХ рддрдВрддреНрд░ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреА рд╣реИ, рдЬреЛ рдХрд┐ рдХрд╛рд░реНрдпрдХреНрд╖реЗрддреНрд░ рдореЗрдВ рдПрдХ рдирд┐рдпрдорд┐рдд рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реИ, рдЬреЛ рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЕрдм рдХреНрдпрд╛ рд╣реЛ рд░рд╣рд╛ рд╣реИред $ рдЧреБрдВрдЬрд╛рдЗрд╢ рдЪрд░рдг рдореЗрдВ $ рдЧреБрдВрдЬрд╛рдЗрд╢

рдирд┐рд░реНрдорд╛рдгрдХрд░реНрддрд╛ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ , рдЗрд╕реЗ рд╢реВрдиреНрдп рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реЗрдЯ рдХрд░реЗрдВ :

 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$phase = null; } 

рдЕрдЧрд▓рд╛, рдЪрд▓реЛ рдЪрд░рдг рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдлрд╝рдВрдХреНрд╢рди рд▓рд┐рдЦрддреЗ рд╣реИрдВ: рдПрдХ рд╕реНрдерд╛рдкрдирд╛ рдХреЗ рд▓рд┐рдП, рджреВрд╕рд░рд╛ рд╕рдлрд╛рдИ рдХреЗ рд▓рд┐рдПред рд╣рдо рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рдЬрд╛рдВрдЪ рднреА рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ рдХрд┐ рдХреЛрдИ рднреА рдкрд┐рдЫрд▓реЗ рдПрдХ рдХреЛ рдкреВрд░рд╛ рдХрд┐рдП рдмрд┐рдирд╛ рдЪрд░рдг рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рд╣реИ:

 Scope.prototype.$beginPhase = function(phase) { if (this.$$phase) { throw this.$$phase + ' already in progress.'; } this.$$phase = phase; }; Scope.prototype.$clearPhase = function() { this.$$phase = null; }; 

$ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдлрдВрдХреНрд╢рди рдореЗрдВ, "$ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ" рдЪрд░рдг рд╕реЗрдЯ рдХрд░реЗрдВ, рдЗрд╕рдореЗрдВ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд▓реВрдк рд▓рдкреЗрдЯреЗрдВ:

 Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); }; 

рдЬрдм рд╣рдо рдпрд╣рд╛рдВ рд╣реИрдВ, рддреЛ рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ $ рд▓рд╛рдЧреВ рдХреЛ рдЕрдВрддрд┐рдо рд░реВрдк рджреЗрдВ рддрд╛рдХрд┐ рдЪрд░рдг рдпрд╣рд╛рдВ рд▓рд┐рдЦрд╛ рдЬрд╛рдПред рдпрд╣ рдбреАрдмрдЧрд┐рдВрдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЙрдкрдпреЛрдЧреА рд╣реЛрдЧрд╛:

 Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; 

рдЕрдм рдЖрдк рдЕрдВрдд рдореЗрдВ $ evalAsync рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдкрд░ рдХреЙрд▓ рд╢реЗрдбреНрдпреВрд▓ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ ред рдпрд╣рд╛рдВ рдЖрдкрдХреЛ рдЪрд░рдг рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА рдпрджрд┐ рдпрд╣ рдЦрд╛рд▓реА рд╣реИ (рдФрд░ рдЕрднреА рддрдХ рдХреЛрдИ рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рдХрд╛рд░реНрдпреЛрдВ рдХреА рдпреЛрдЬрдирд╛ рдирд╣реАрдВ рдмрдирд╛рдИ рдЧрдИ рд╣реИ) - рд╣рдо $ рдкрд╛рдЪрди рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреА рдпреЛрдЬрдирд╛ рдмрдирд╛рддреЗ рд╣реИрдВ :

 Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; 

рдЗрд╕ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ, $ evalAsync рдХреЛ рдХреЙрд▓ рдХрд░рдХреЗ , рдЖрдк рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдирд┐рдХрдЯ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдкрд╛рдЪрди рд╣реЛрдЧрд╛, рдЪрд╛рд╣реЗ рдХреЙрд▓ рдХрд╣рд╛рдВ рд╕реЗ рдЖрдП:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$phase = null; } Scope.prototype.$beginPhase = function(phase) { if (this.$$phase) { throw this.$$phase + ' already in progress.'; } this.$$phase = phase; }; Scope.prototype.$clearPhase = function() { this.$$phase = null; }; Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; var scope = new Scope(); scope.asyncEvaled = false; scope.$evalAsync(function(scope) { scope.asyncEvaled = true; }); setTimeout(function() { console.log("Evaled after a while: "+scope.asyncEvaled); }, 100); // Check after a delay to make sure the digest has had a chance to run. 

:
"Evaled after a while: true" 


рдкрдЪрдиреЗ рдХреЗ рдмрд╛рдж рд░рдирд┐рдВрдЧ рдХреЛрдб - $ $ рдкреЛрд╕реНрдЯрдбрд┐рдЧреЗрд╕реНрдЯ


$ $ PostDigest рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ рдХреЛрдб рдХреЛ рдкрдЪрд╛рдиреЗ рдХреЗ рд▓реВрдк рдХреЗ рдзрд╛рдЧреЗ рдореЗрдВ рдЬреЛрдбрд╝рдиреЗ рдХрд╛ рдПрдХ рдФрд░ рддрд░реАрдХрд╛ рд╣реИ ред

рдлрд╝рдВрдХреНрд╢рди рдирд╛рдо рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдбрдмрд▓ рдбреЙрд▓рд░ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдЧрд╣рди рдХреЛрдгреАрдп рдХрд╛рд░реНрдп рд╣реИ рдЬрд┐рд╕реЗ рдПрдВрдЧреБрд▓рд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред рд▓реЗрдХрд┐рди рдпрд╣ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдорд╣рддреНрд╡рдкреВрд░реНрдг рдирд╣реАрдВ рд╣реИ, рд╣рдо рдЗрд╕реЗ рд╡реИрд╕реЗ рднреА рд▓рд╛рдЧреВ рдХрд░реЗрдВрдЧреЗред $ EvalAsync рдХреА

рддрд░рд╣ , $$ postDigest рдЖрдкрдХреЛ рдмрд╛рдж рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдХреЛрдб рдХреЗ рдирд┐рд╖реНрдкрд╛рджрди рдореЗрдВ рджреЗрд░реА рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ, рдЕрдЧрд▓реЗ рдкрд╛рдЪрди рдХреЗ рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХреЗ рддреБрд░рдВрдд рдмрд╛рдж рдЖрд╕реНрдердЧрд┐рдд рдХрд╛рд░реНрдп рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред $ $ PostDigest рдХрд╛ рдЙрдкрдпреЛрдЧ $ рдкрдЪрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдордЬрдмреВрд░ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИрдЗрд╕рд▓рд┐рдП, рдЬрдм рддрдХ рдХрд┐ рдХреЛрдИ рддреГрддреАрдп-рдкрдХреНрд╖ рдХреЛрдб рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рд╢реБрд░реВ рдирд╣реАрдВ рдХрд░рддрд╛, рддрдм рддрдХ рдЖрд╕реНрдердЧрд┐рдд рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд▓реЙрдиреНрдЪ рдореЗрдВ рджреЗрд░реА рд╣реЛ рд╕рдХрддреА рд╣реИред рдЬреИрд╕рд╛ рдХрд┐ рдирд╛рдо рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ, $ $ рдкреЛрд╕реНрдЯрдбрд┐рдЬреЗрд╕реНрдЯ рд╕рд┐рд░реНрдл рдкрдЪрд╛рдиреЗ рдХреЗ рддреБрд░рдВрдд рдмрд╛рдж рд╕реНрдердЧрд┐рдд рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрджрд┐ рдЖрдк $ $ рдкреЛрд╕реНрдЯрдбрд┐рдЬрд╕реНрдЯ рдХреЛ рджрд┐рдП рдЧрдП рдХреЛрдб рдореЗрдВ рд╕рдВрд╢реЛрдзрд┐рдд рдЧреБрдВрдЬрд╛рдЗрд╢ рд░рдЦрддреЗ рд╣реИрдВ , рддреЛ рдмрджрд▓рд╛рд╡ рдХреЛ рдкрдХрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЛ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдпрд╛ $ рдЕрдкреНрд▓рд╛рдИ рдХрд╛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

рдЖрд░рдВрдн рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдЗрдП рд╕реНрдХреЛрдк рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдореЗрдВ рдПрдХ рдФрд░ рдХрддрд╛рд░ рдЬреЛрдбрд╝реЗрдВ , рдЗрд╕ рдмрд╛рд░ $ $ рдкреЛрд╕реНрдЯрдбрд┐рдЧреЗрд╕реНрдЯ рдХреЗ рд▓рд┐рдП :

 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; } 

рдЗрд╕рдХреЗ рдмрд╛рдж, рд╣рдо $ $ рдкреЛрд╕реНрдЯрдбрд┐рдЧреЗрд╕реНрдЯ рдХреЛ рд╣реА рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВ ред рд╡рд╣ рдЬреЛ рдХрд░рддреА рд╣реИ рд╡рд╣ рдХрддрд╛рд░ рдореЗрдВ рд╕реНрд╡реАрдХреГрдд рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдЬреЛрдбрд╝ рджреЗрддреА рд╣реИ:

 Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); }; 

рдФрд░ рдЕрдВрдд рдореЗрдВ, $ рдкрд╛рдЪрди рдХреЗ рдЕрдВрдд рдореЗрдВ , рд╣рдореЗрдВ рдПрдХ рдмрд╛рд░ рдореЗрдВ рд╕рднреА рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рдФрд░ рдХрддрд╛рд░ рдХреЛ рд╕рд╛рдл рдХрд░рдирд╛ рд╣реЛрдЧрд╛:

 Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { this.$$postDigestQueue.shift()(); } }; 

рдпрд╣рд╛рдБ $$ postDigest рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ :

JS рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; } Scope.prototype.$beginPhase = function(phase) { if (this.$$phase) { throw this.$$phase + ' already in progress.'; } this.$$phase = phase; }; Scope.prototype.$clearPhase = function() { this.$$phase = null; }; Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); }); return dirty; }; Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { this.$$postDigestQueue.shift()(); } }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); }; var scope = new Scope(); var postDigestInvoked = false; scope.$$postDigest(function() { postDigestInvoked = true; }); console.assert(!postDigestInvoked); scope.$digest(); console.assert(postDigestInvoked); 

:
true
true 


рдЕрдкрд╡рд╛рдж рд╕рдВрднрд╛рд▓рдирд╛


рд╣рдорд╛рд░рд╛ рд╡рд░реНрддрдорд╛рди $ рдЧреБрдВрдЬрд╛рдЗрд╢ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛрдгреАрдп рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рдХрд░реАрдм рдФрд░ рдХрд░реАрдм рд╣реЛ рд░рд╣рд╛ рд╣реИред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдпрд╣ рдЕрднреА рднреА рдХрд╛рдлреА рдирд╛рдЬреБрдХ рд╣реИред рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рд╣рдордиреЗ рдЕрдкрд╡рд╛рдж рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рдзреНрдпрд╛рди рдирд╣реАрдВ рджрд┐рдпрд╛ рд╣реИред

рдХреЛрдгреАрдп рдореЗрдВ рд╕реНрдХреЛрдк рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХрд╛рдлреА рддреНрд░реБрдЯрд┐ рдкреНрд░рддрд┐рд░реЛрдзреА рд╣реЛрддреЗ рд╣реИрдВ: рдЬрдм рдЕрдкрд╡рд╛рдж рд╡реЙрдЪ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдореЗрдВ рд╣реЛрддреЗ рд╣реИрдВ, $ evalAsync рдпрд╛ $$ рдкреЛрд╕реНрдЯрдбрд┐рдЧреЗрд╕реНрдЯ рдореЗрдВ , рдпрд╣ рдкрд╛рдЪрди рдЪрдХреНрд░ рдХреЛ рдмрд╛рдзрд┐рдд рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рд╣рдорд╛рд░реЗ рд╡рд░реНрддрдорд╛рди рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ, рдЗрдирдореЗрдВ рд╕реЗ рдХреЛрдИ рднреА рддреНрд░реБрдЯрд┐ рд╣рдореЗрдВ рдкрдЪрд╛рдиреЗ рд╕реЗ рдмрд╛рд╣рд░ рдХрд░ рджреЗрдЧреАред

рдЖрдк рдЗрди рд╕рднреА рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдХреЙрд▓рд┐рдВрдЧ рдмреНрд▓реЙрдХ рдХреЗ рдЕрдВрджрд░ рд▓рдкреЗрдЯрдХрд░ рдЗрд╕реЗ рдЖрд╕рд╛рдиреА рд╕реЗ рдареАрдХ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ ... рдЖрдЬрд╝рдорд╛рдПрдВ
рдХреЛрдгреАрдп рдореЗрдВ, рдЗрди рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рд╡рд┐рд╢реЗрд╖ рд╕реЗрд╡рд╛ $ рдЕрдкрд╡рд╛рджрд╣реИрдВрдбрд▓рд░ рдХреЛ рдкрд╛рд╕ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдпрд╣ рдЕрднреА рддрдХ рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЕрдм рд╣рдо рдХрдВрд╕реЛрд▓ рдореЗрдВ рдХреЗрд╡рд▓ рдЖрдЙрдЯрдкреБрдЯ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВрдЧреЗред

$ EvalAsync рдФрд░ $$ рдкреЛрд╕реНрдЯрдбрд┐рдЬреЗрд╕реНрдЯ рдХреЗ рд▓рд┐рдП рдЕрдкрд╡рд╛рдж рд╣реИрдВрдбрд▓рд┐рдВрдЧ $ рдбрд╛рдЗрдЬреЗрд╕реНрдЯ рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ ред рджреЛрдиреЛрдВ рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рдЕрдкрд╡рд╛рдж рд▓реЙрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдФрд░ рдкрд╛рдЪрди рд╕рд╛рдорд╛рдиреНрдп рд░реВрдк рд╕реЗ рдЬрд╛рд░реА рд╣реИ:

 Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { try { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } catch (e) { (console.error || console.log)(e); } } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { try { this.$$postDigestQueue.shift()(); } catch (e) { (console.error || console.log)(e); } } }; 

рдШрдбрд╝реА рд╕рдорд╛рд░реЛрд╣ рдХреЗ рд▓рд┐рдП рдЕрдкрд╡рд╛рдж рд╣реИрдВрдбрд▓рд┐рдВрдЧ $ рдкрд╛рдЪрди рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ :

 Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { try { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); } catch (e) { (console.error || console.log)(e); } }); return dirty; }; 

рдЕрдм рд╣рдорд╛рд░рд╛ рдкрд╛рдЪрди рдЪрдХреНрд░ рдЕрдкрд╡рд╛рджреЛрдВ рдХреЗ рд╕рд╛рде рдмрд╣реБрдд рдЕрдзрд┐рдХ рд╡рд┐рд╢реНрд╡рд╕рдиреАрдп рд╣реИ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; } Scope.prototype.$beginPhase = function(phase) { if (this.$$phase) { throw this.$$phase + ' already in progress.'; } this.$$phase = phase; }; Scope.prototype.$clearPhase = function() { this.$$phase = null; }; Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { try { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); } catch (e) { (console.error || console.log)(e); } }); return dirty; }; Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { try { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } catch (e) { (console.error || console.log)(e); } } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { try { this.$$postDigestQueue.shift()(); } catch (e) { (console.error || console.log)(e); } } }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); }; var scope = new Scope(); scope.aValue = "abc"; scope.counter = 0; scope.$watch(function() { throw "Watch fail"; }); scope.$watch( function(scope) { scope.$evalAsync(function(scope) { throw "async fail"; }); return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$digest(); console.assert(scope.counter === 1); 

:
"Watch fail"
"async fail"
"Watch fail"
true 


рдСрдмреНрдЬрд░реНрд╡рд░ рдбрд┐рд╕реЗрдмрд▓


рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рддреЗ рд╕рдордп, рдЬреНрдпрд╛рджрд╛рддрд░ рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рдЖрдкрдХреЛ рдЧреБрдВрдЬрд╛рдЗрд╢ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рдЬреАрд╡рди рднрд░ рд╕рдХреНрд░рд┐рдп рд░рд╣рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдФрд░ рдЗрд╕реЗ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╣рдЯрд╛рдиреЗ рдХреА рдХреЛрдИ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред рд▓реЗрдХрд┐рди рдХреБрдЫ рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХреЗ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рд╣рдЯрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЬрдмрдХрд┐ рдЧреБрдВрдЬрд╛рдЗрд╢ рдХрд╛рдо рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рдЦрдирд╛ рдЪрд╛рд╣рд┐рдПред рдПрдВрдЧреБрд▓рд░ рдореЗрдВ $ рдШрдбрд╝реА

рдлрд╝рдВрдХреНрд╢рди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдПрдХ рдореВрд▓реНрдп рджреЗрддрд╛ рд╣реИ - рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдЬрд┐рд╕рдХрд╛ рдХреЙрд▓ рдкрдВрдЬреАрдХреГрдд рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рд╣рдЯрд╛ рджреЗрддрд╛ рд╣реИред рдЗрд╕реЗ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреЗ рд╣рдо рд╕рднреА рдХреА рдЬрд░реВрд░рдд рд╣реИ $ рдШрдбрд╝реА рдПрдХ рд╕рдорд╛рд░реЛрд╣ рд╣реИ рдХрд┐ рд╕реЗ рдирд╡ рдирд┐рд░реНрдорд┐рдд рд╕рд░рдгреА рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рд╣рдЯрд╛ рд░рд┐рдЯрд░реНрди $$ рдкрд░ рдирдЬрд░ рд░рдЦрдиреЗ рд╡рд╛рд▓реЛрдВ :

 Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var self = this; var watcher = { watchFn: watchFn, listenerFn: listenerFn, valueEq: !!valueEq }; self.$$watchers.push(watcher); return function() { var index = self.$$watchers.indexOf(watcher); if (index >= 0) { self.$$watchers.splice(index, 1); } }; }; 

рдЕрдм рдЖрдк $ рдШрдбрд╝реА рд╕реЗ рд▓реМрдЯреЗ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдпрд╛рдж рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ , рдФрд░ рдмрд╛рдж рдореЗрдВ рдХреЙрд▓ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬрдм рдЖрдкрдХреЛ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рдирд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ:

рдЬреЗрдПрд╕ рдмрд┐рди рдХреЛрдб
рдХреЛрдб рджреЗрдЦреЗрдВ
 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; } Scope.prototype.$beginPhase = function(phase) { if (this.$$phase) { throw this.$$phase + ' already in progress.'; } this.$$phase = phase; }; Scope.prototype.$clearPhase = function() { this.$$phase = null; }; Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var self = this; var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { }, valueEq: !!valueEq }; self.$$watchers.push(watcher); return function() { var index = self.$$watchers.indexOf(watcher); if (index >= 0) { self.$$watchers.splice(index, 1); } }; }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue || (typeof newValue === 'number' && typeof oldValue === 'number' && isNaN(newValue) && isNaN(oldValue)); } }; Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { try { var newValue = watch.watchFn(self); var oldValue = watch.last; if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); } catch (e) { (console.error || console.log)(e); } }); return dirty; }; Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { try { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } catch (e) { (console.error || console.log)(e); } } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { try { this.$$postDigestQueue.shift()(); } catch (e) { (console.error || console.log)(e); } } }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); }; var scope = new Scope(); scope.aValue = "abc"; scope.counter = 0; var removeWatch = scope.$watch( function(scope) { return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$digest(); console.assert(scope.counter === 1); scope.aValue = 'def'; scope.$digest(); console.assert(scope.counter === 2); removeWatch(); scope.aValue = 'ghi'; scope.$digest(); console.assert(scope.counter === 2); // No longer incrementing 

:
true
true
true 


рдЖрдЧреЗ рдХреНрдпрд╛ рд╣реИ


рд╣рдордиреЗ рдПрдХ рд▓рдВрдмрд╛ рд░рд╛рд╕реНрддрд╛ рддрдп рдХрд┐рдпрд╛ рд╣реИ рдФрд░ рдХреЛрдгреАрдп рд╡рд╕реНрддреБрдУрдВ рдХреА рдЙрддреНрдХреГрд╖реНрдЯ рдкрд░рдВрдкрд░рд╛рдУрдВ рдореЗрдВ рдЙрддреНрдХреГрд╖реНрдЯ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рдПрдХ рдЙрддреНрдХреГрд╖реНрдЯ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд┐рдпрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдПрдВрдЧреБрд▓рд░ рдореЗрдВ рд╕реНрдХреЛрдк рдСрдмреНрдЬреЗрдХреНрдЯ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЬрд┐рддрдирд╛ рд╣реИ рдЙрд╕рд╕реЗ рдХрд╣реАрдВ рдЬреНрдпрд╛рджрд╛ рд╣реИред

рд╕рдВрднрд╡рддрдГ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдХреЛрдгреАрдп рдореЗрдВ рдЧреБрдВрдЬрд╛рдЗрд╢ рд╕реНрд╡рддрдВрддреНрд░ рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдЕрд▓рдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд, рд╕реНрдХреЛрдк рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХреЛ рдЕрдиреНрдп рд╕реНрдХреЛрдк рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рдорд┐рд▓рд╛ рд╣реИ, рдФрд░ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рди рдХреЗрд╡рд▓ рдЙрди рдЧреБрдгреЛрдВ рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЬрд┐рдирд╕реЗ рд╡реЗ рд╕рдВрд▓рдЧреНрди рд╣реИрдВ, рдмрд▓реНрдХрд┐ рдкреЗрд░реЗрдВрдЯреНрд╕ рд╕реНрдХреЛрдк рдХреЗ рдЧреБрдг рднреА рд╣реИрдВред рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг, рдЬреЛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЗрддрдирд╛ рд╕рд░рд▓ рд╣реИ, рд╢реБрд░реБрдЖрддреА рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдХрдИ рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рд╕реНрд░реЛрдд рд╣реИред рдЗрд╕реАрд▓рд┐рдП рдЗрд╕ рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ рдЕрдЧрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рдЧреБрдВрдЬрд╛рдЗрд╢ рдХреА рд╡рд┐рд░рд╛рд╕рдд рд╢реЛрдз рдХрд╛ рд╡рд┐рд╖рдп рд╣реЛрдЧреАред

рднрд╡рд┐рд╖реНрдп рдореЗрдВ, рд╣рдо рдЗрд╡реЗрдВрдЯ рд╕рдмрд╕рд┐рд╕реНрдЯрдо рдкрд░ рднреА рдЪрд░реНрдЪрд╛ рдХрд░реЗрдВрдЧреЗ, рдЬрд┐рд╕реЗ рд╕реНрдХреЛрдк рдореЗрдВ рднреА рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ ред

рдЕрдиреБрд╡рд╛рджрдХ рд╕реЗ:

рдкрд╛рда рдХрд╛рдлреА рдмрдбрд╝рд╛ рд╣реИ, рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рддреНрд░реБрдЯрд┐рдпрд╛рдБ рдФрд░ рдЯрд╛рдЗрдкреЛрд╕ рд╣реИрдВред рдЙрдиреНрд╣реЗрдВ рд╡реНрдпрдХреНрддрд┐рдЧрдд рд░реВрдк рд╕реЗ рднреЗрдЬреЗрдВ - рдореИрдВ рд╕рдм рдХреБрдЫ рдареАрдХ рдХрд░ рджреВрдВрдЧрд╛

рдпрджрд┐ рдХреЛрдИ рдЬрд╛рдирддрд╛ рд╣реИ рдХрд┐ рд╣рдм рдкрд░ рдХреЛрдб рдореЗрдВ рд▓рд╛рдЗрдиреЛрдВ рдХрд╛ рдЪрдпрди рдХреИрд╕реЗ рдХрд░реЗрдВ, рддреЛ рдХрд╣реЗрдВ, рдЗрд╕рд╕реЗ рдХреЛрдб рдХреА рдкрдардиреАрдпрддрд╛ рдореЗрдВ рд╕реБрдзрд╛рд░ рд╣реЛрдЧрд╛ред

Source: https://habr.com/ru/post/In201832/


All Articles