Imitation, anjularjs bidirectional binding, pure JavaScript implementation

Source: Internet
Author: User

Anjularjs in the two-way binding is very interesting, the following imitation of its source code, wrote a simple version, is the poor robustness of the point.

650) this.width=650; "src=" Http://s3.51cto.com/wyfs02/M02/52/98/wKiom1RkoE6CEn6rAAA6mIc19S0230.jpg "title=" is not named. JPG "alt=" Wkiom1rkoe6cen6raaa6mic19s0230.jpg "/>

From the "Wo zi ji de" you can see that when the contents of the input box change, the contents of the 2 rows are also changing dynamically. A little excited. where x variables are angularjs with two-way bindings, y variables are used by their own (Zi Ji de).

Html:

<p>angularjs</p><p ng-bind= ' x ' ></p><input type= ' text ' ng-model= ' x '; class= ' Form-control '/><p>wo zi ji de</p><p by-bind= ' y ' ></p><p by-bind= ' y ' ></p ><input type= ' text ' by-model= ' y '; class= ' Form-control '/>





Implementation principle:

1.$watch The 2 parameters of the function, the first is the variable to listen to, and the second is the function that runs when the variable is changed. These 2 watcher in JSON format, WATCHFN: The function returns the value of the current moment, may change, and may not change. As shown below, last represents the previous value, initially 1.

var watcher = {Watchfn:function (watchvalue,self) {//return self.data.get (watchvalue); return self.result2[0].value;}, LISTENERFN:LISTENERFUNCTION,LAST:1};

2. because more than one variable is being monitored, there will be a lot of watcher, all watcher deposited into $ $watcher. Two dollars represents a private variable, and Anjularjs is a private variable that represents $scope, which is not accessible to the user.

this.$ $watchers. push (watcher);

3. with the variables to listen, the next step is to implement the listener . is the responsibility of the $digest object. $digest check each watcher in the $watcher to see if its current value is different from the last value, which of course indicates that the value being monitored has changed. The corresponding values displayed with Ng-bind (By-bind) should also be updated. This is what the LISTENERFN function in watcher is going to do.


4. Description: Byscope object to mimic $scope object. $scope. x corresponds to the HTML <p ng-bind= ' x '/> and <input ng-model= ' x '/>. While Byscope imitation (<p by-bind= ' y '/><input by-model= ' y '/>), is the use of a map key value array, ' Y ' is the key,key corresponding value is equivalent to $scope.x;

$scope. x just a word, without your initial $scope variable, I didn't do-_-!。  You heard it wrong, so you have to set up the Byscope object yourself and pass in the Y value. Why is it?

var bysowhat = new Byscope ("Y");

Anjularjs source, use get = Compiletofn (watchexp, ' watch ') in the code below. COMPILETOFN can convert the value of the string to the corresponding variable and function, only the scripting language can do oh. I can't finish the source code of this function.

$watch:  function (watchexp, listener, objectequality)  {         var scope = this,             get = compiletofn (watchexp,  ' watch '),             array = scope.$ $watchers,             watcher = {               fn: listener,               last: initWatchVal,               get: get,               exp: watchExp,               eq: !! Objectequality            }; 



Javascript:

Function listcontroler ($scope) {$scope. x = 100;} (function ($) {///depth First traversal of Dom tree, find by-bin= ' y ' node//node: node//attri: "By-bind"//return Value: Target node array function bianli2 (Node,attri , Value,resultnode) {var allnodes = node.childnodes;if (allnodes != undefined) {//Has child nodes, Continue recursive for (var i=0;i<allnodes.length;i++) {if (allnodes[i].nodetype == 1 &&  allnodes[i].nodename!= "SCRIPT") {//All element nodes, except script//find custom Properties Var attrstr = allnodes[i].getattribute (Attri);//when found, pass the node to Resultnodeif (Attrstr == value) Resultnode.push (Allnodes[i]);//alert (AllNodes[i]. NodeName); Bianli (Allnodes[i]);}}} Implement $watchfunction byscope (Watchvaluename) {//Store all watchthis.$ $watchers  = [];this.data =  new map ();//this.data.add ("Y", 123); this.result1=[];this.result2=[];//var self = this ; Bianli2 (Document.body, "By-bind", WATCHVALUENAME,THIS.RESULT1); Bianli2 (Document.body, "By-model", Watchvaluename, THIS.RESULT2);//alert (This.data.get("Y"));} The listener deposited $ $watchersByScope. prototype. $watch  = function (watchvalue, listenerfunction) {var  Self = this;var watcher = {watchfn:function (watchvalue,self) {//return  Self.data.get (watchvalue); return self.result2[0].value;},listenerfn:listenerfunction,last:1};this.$$ Watchers.push (watcher);//Run $digestsetinterval (function () {self. $digest (watchvalue)},100) every 0.1 seconds;//this. $digest ( Watchvalue);};/ /Contrast listener content: $digestByScope. prototype. $digestOnce  = function (watchvalue) {var self = this; Var dirty;for (var i=0;i<this.$ $watchers. length;i++) {var newvalue = this.$ $watchers [I].watchfn (watchvalue,self); var oldvalue = this.$ $watchers [i].last;//Monitored value changed if (newvalue  != oldvalue) {this.$ $watchers [I].listenerfn ();d irty = true;} Update monitoring values this.$ $watchers [I].last = newvalue;} return dirty;}; Byscope.prototype. $digest  = function (watchvalue) {var dirty;do{Dirty = this. $digestOnce (Watchvalue);} while (dirty);}; Var bysowhat = new byscope ("Y"); bysowhat. $watch (' Y ', function () {for (var i=0;i< bysowhat.result1.length;i++) {bysowhat.result1[i].innerhtml = bysowhat.result2[0].value;});}) (JQuery);



I read the code, and then look at the source of the progressive parsing

1.

get = Compiletofn (watchexp, ' watch '),

Since Watchexp is passed through a string, the string is converted to a variable or function (the function is also a variable). Just like the function name string ' MyFunction ' in SetInterval (' MyFunction ', 100).


2.

Array = scope.$ $watchers, watcher = {Fn:listener, last:initwatchval, Get:get, Exp:watchexp, EQ:!! Objectequality};

The basic information of the Watcher object in the listening process: Exp: the variable to be monitored; FN: the function that was executed when the variable's value was found; Last: The value of the previous variable; EQ: Depth listener, get: as described earlier.


3.

In the "Case" user pass string, we need to compile it, does we really need this?          if (!isfunction (listener)) {var listenfn = compiletofn (Listener | | noop, ' listener ');        Watcher.fn = function (newval, oldval, scope) {LISTENFN (scope);}; }

If the second variable passed by $scope. $watch is not a function, it is a string, then the string is converted to a function, saying who will pass the string Ah _-! Would you give up the train ticket and take a photo of the train ticket to the ticket inspector? This is also why the source comments into "Do we really need this?"


4.

if (typeof watchexp = = ' string ' && get.constant) {var originalfn = Watcher.fn;            Watcher.fn = function (newval, oldval, scope) {Originalfn.call (this, newval, oldval, scope);          Arrayremove (array, watcher);        }; }

Why not?



5.

if (!array) {array = scope.$ $watchers = [];        }//We use the unshift since we use a while loop in $digest for speed.        The while loop reads in reverse order. Array.unshift (watcher);

In the source code, when the first watcher is added, no array exists, so an array is created.

Unshift represents inserting data into the array header, which is equivalent to using the array as a stack. For the back $digest cycle speed.



6.

return function () {arrayremove (array, watcher); };

This makes up the closure, the purpose of which is to make the following line

Arrayremove (array, watcher);

Execute again when you want to be executed.




       */       $watch:  function (watchexp ,  listener, objectequality)  {        var scope  = this,            get =  Compiletofn (watchexp,  ' watch '),             array = scope.$ $watchers,             WATCHER&NBSP;=&NBSP;{&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;FN:  listener,              last:  Initwatchval,              get: get ,              exp: watchexp,               eq: !! objectequality            };         // in the case user pass string, we need  to compile it, do we really need this ?         if  (!isfunction (listener))  {           var listenfn = compiletofn (listener | |  noop,  ' listener ');           watcher.fn =  function (Newval, oldval, scope) &NBSP;{LISTENFN (scope);};         }        if  ( typeof watchexp ==  ' string '  && get.constant)  {           var originalfn = watcher.fn;          watcher.fn  = function (Newval, oldval, scope)  {             originalfn.call (This, newval, oldval, scope);             arrayremove (Array, watcher);           };        }         if  (!array)  {           array = scope.$ $watchers  = [];        }         // we use unshift since we use  a while loop in  $digest  for speed.         // the while loop reads in reverse order.         Array.unshift (watcher);         return function ()  {           arrayremove (Array, watcher);         };      },






Imitation, anjularjs bidirectional binding, pure JavaScript implementation

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.