Knockout双向绑定

knockout双工绑定基于 observe 模式,性能高。核心就是observable对象的定义。这个函数最后返回了一个也叫作 observable 的函数,也就是用户定义值的读写器(accessor)。javascript

this.firstName=ko.observable(“Bert”);
this.firstName();
this.firstName(“test”);

ko.observable作了什么java

ko.observable = function (initialValue) {
    var _latestValue = initialValue; //保留上一次的参数,与observable造成闭包
     
    function observable() {
        if (arguments.length > 0) {
            // Write,Ignore writes if the value hasn't changed
            if (observable.isDifferent(_latestValue, arguments[0])) {
                observable.valueWillMutate();
                _latestValue = arguments[0];
                if (DEBUG) observable._latestValue = _latestValue;
                observable.valueHasMutated();
            }
     
            return this; // Permits chained assignments
        }
        else {
            // Read
            ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
            return _latestValue;
        }
    }
    ko.subscribable.call(observable);
    ko.utils.setPrototypeOfOrExtend(observable, ko.observable['fn']);
     
    if (DEBUG) observable._latestValue = _latestValue;
    /**这里省略了专为 closure compiler 写的语句**/

    return observable;
     
}

经过 ko.subscribable.call(observable); 使这个函数有了被订阅的功能,让 firstName 在改变时能通知全部订阅了它的对象。其实就是维护了一个回调函数的队列,当本身的值改变时,就执行这些回调函数。根据上面的代码,回调函数是在 observable.valueHasMutated(); 执行的。node

ko.computed作了什么express

this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();    
    }, this);
$.computed = function(obj, scope){
    //computed是由多个$.observable组成
    var getter, setter
    if(typeof obj == "function"){
        getter = obj
    }else if(obj && typeof obj == "object"){
        getter = obj.getter;
        setter = obj.setter;
        scope  = obj.scope;
    }
    var v
    var ret = function(neo){
        if(arguments.length ){
            if(typeof setter == "function"){//setter不必定存在的
                if(v !== neo ){
                    setter.call(scope, neo);
                    v = neo;
                }
            }
            return ret;
        }else{
            v = getter.call(scope);
            return v;
        }
    }
    return ret;
}
$.dependencyDetection = (function () {
    var _frames = [];
    return {
        begin: function (ret) {
            _frames.push(ret);
        },
        end: function () {
            _frames.pop();
        },
        collect: function (self) {
            if (_frames.length > 0) {
                self.list = self.list || [];
                var fn = _frames[_frames.length - 1];
                if ( self.list.indexOf( fn ) >= 0)
                    return;
                self.list.push(fn);
            }
        }
    };
})();
$.valueWillMutate = function(observable){
    var list = observable.list
    if($.type(list,"Array")){
        for(var i = 0, el; el = list[i++];){
            el();
        }
    }
}

双向绑定如何实现

$.buildEvalWithinScopeFunction =  function (expression, scopeLevels) {
    var functionBody = "return (" + expression + ")";
    for (var i = 0; i < scopeLevels; i++) {
        functionBody = "with(sc[" + i + "]) { " + functionBody + " } ";
    }
    return new Function("sc", functionBody);
}
$.applyBindings = function(model, node){       
   
    var nodeBind = $.computed(function (){
        var str = "{" + node.getAttribute("data-bind")+"}"
        var fn = $.buildEvalWithinScopeFunction(str,2);
        var bindings = fn([node,model]);
        for(var key in bindings){
            if(bindings.hasOwnProperty(key)){
                var fn = $.bindingHandlers["text"]["update"];
                var observable = bindings[key]
                $.dependencyDetection.collect(observable);//绑定viewModel与UI
                fn(node, observable)
            }
        }
    },node);
    return nodeBind
     
}
$.bindingHandlers = {}
$.bindingHandlers["text"] = {
    'update': function (node, observable) {
        var val = observable()
        if("textContent" in node){
            node.textContent = val;
        }
    }
}
window.onload = function(){
    var model = new MyViewModel();
    var node = document.getElementById("node");
    $.applyBindings(model, node);
}

KO使用闭包

一、ko绑定方式,当即执行用于须要后处理的一些数值app

//点击事件
data-bind="click:$root.fun1.bind($param1,param2)"
//当即执行
data-bind="attr: { src : $root.fun2(param1,param2) }”
//缺省参数
data-bind="event: { mouseover: myFunction }"
<script type="text/javascript">  
    var viewModel = {  
        myFunction: function(data, event) {  
            if (event.shiftKey) {  
                //do something different when user has shift key down  
            } else {  
                //do normal action  
            }  
        }  
    };  
    ko.applyBindings(viewModel);  
</script>

注意:在bind方式传递参数时,data和event两个参数依然被缺省传递。 新加入的参数,在使用时排在第一位,定义时只能排在$data后面ide

二、event事件函数

<input type="text" placeholder="输入关键字搜索" data-bind="event:{keyup:$root.fun1.bind($data,$element)}">

完整的 key press 过程分为两个部分,按键被按下,而后按键被松开并复位。
当按钮被松开时,发生 keyup 事件。它发生在当前得到焦点的元素上。
keydown事件发生在键盘的键被按下的时候,接下来触发 keypress事件。  keyup 事件在按键被释放的时候触发。
KeyPress 只能捕获单个字符;KeyDown 和KeyUp 能够捕获组合键。 

三、性能

self.weeklyRecommend(this);  //监控对象总体发生变化时响应
self.weeklyRecommend(ko.mapping.fromJs(this));  //能够监控对象下每一个元素的改变

四、ko事件注册ui

ko.bindingHandlers.singleExamHover = {
    init: function(element, valueAccessor){
        $(element).hover(
            function(){
                //todo 
            },
            function(){
                //todo
            }
        );
    },
    update:function(element, valueAccessor){
        var _value = ko.unwrap(valueAccessor());
        if(_value){
            $(element).addClass("current");
        }else{
            $(element).removeClass("current");
        }
    }
};
<div class="h-set-homework" data-bind="singleExamHover:question.checked”>

五、事件冒泡

By default, Knockout will allow the click event to continue to bubble up to any higher level event handlers。

If necessary, you can prevent the event from bubbling by including an additional binding that is named clickBubble and passing false to it 

<div data-bind="click: myDivHandler">
    <button data-bind="click: myButtonHandler, clickBubble: false">
        Click me
    </button>
</div>

Normally, in this case myButtonHandler would be called first, then the click event would bubble up to myDivHandler. However, the clickBubble binding that we added with a value of false prevents the event from making it past myButtonHandler

六、$data

This is the view model object in the current context. In the root context,  $data and  $root are equivalent. Inside a nested binding context, this parameter will be set to the current data item (e.g., inside a  with: person binding,  $data will be set to  person).  $data is useful when you want to reference the viewmodel itself, rather than a property on the viewmodel.
<div data-bind="click:changeEditor.bind($data,$element,param1,param2)"></div>
<script>
changeEditor : function(ele,param1,param2){
     console.log(this)
     console.log(ele==event.currenttarget)
}
</script>
相关文章
相关标签/搜索