arale 소스 코드 의 attribute 편 읽 기

시리즈 글: arale 소스 코드 의 class 편 읽 기attributes 기본 적 인 속성 추가, 획득, 제거 등 기능 을 제공 합 니 다.이것 은 인 스 턴 스 와 관련 된 상태 정보 로 읽 을 수 있 고 쓸 수 있 으 며 변화 가 발생 하면 관련 사건 이 자동 으로 발생 합 니 다.
먼저 Attribute 모듈 이 실현 할 기능 을 알 아 보 겠 습 니 다.
  • 속성 치 설정
  • {
      attr1: 'hello1',
    
      //     attr1    
      attr2: {
        value: 'hello2'
      }
    }
  • 속성 설정 과 가 져 오기 전에 함수 하 나 를 터치 하여 속성 을 먼저 처리 합 니 다
  • {
      attr3: {
        value: 'hello3',
        setter: function(v) {
          return v + '';
        },
        getter: function(v) {
          return v * 6;
        }
      }
    }
  • 속성 만 읽 기
  • {
      attr4: {
        value: 1,
        readOnly: true
      }
    }
  • 속성 변경 시 관련 이벤트 자동 발동
  • {
      _onChangeAttr1: function(val) {
        console.log('attr1 changed by' + val);
      }
    }
  • 속성 설정 시 두 상태 증가
  • // {silent: true}     _onChangeAttr1    
    instance.set('attr1', 2, {silent: true});
    
    // {override: true}    false,    ,  true  ,      。
    instance.set('attr2', {w: 12, h: 33}, {override: true});

    이제 소스 코드 가 어떻게 이 루어 졌 는 지 보 러 가자!
    initAtts 속성 초기 화initAttrs 실례 화 대상 에서 호출 됩 니 다.
    exports.initAttrs = function(config) {
      // initAttrs          ,             attrs,       
      var attrs = this.attrs = {};
      //          
      var specialProps = this.propsInAttrs || [];
      //          attrs       attrs  
      mergeInheritedAttrs(attrs, this, specialProps);
      //   config      
      if (config) {
        mergeInheritedAttrs(atts, config);
      }
      //     setter    ,      set   ,             
      setSetterAttrs(this, attrs, config);
      // Convert `on/before/afterXxx` config to event handler.
      parseEventsFromAttrs(this, attrs);
      //   this.attrs    special properties    this  
      copySpecialProps(specialProps, this, attrs, true);
    }
    initAttrs 에 사용 되 는 몇 가지 방법 을 살 펴 보 겠 습 니 다.
    1. merge 합병 속성
    function merge(receiver, supplier) {
      var key, value;
      for (key in supplier) {
        if (supplier.hasOwnPrototype(key)) {
          value = supplier[key];
          //   clone         ,      
          if (isArray(value)) {
            //         ,           
            value = value.slice();
          } else if (isPlainObject(value)) {
            //                  ,        ,       。
            var prev = receiver[key];
            isPlainObject(prev) || (prev = {});
            //          ,      
            value = merge(prev, value);
          }
          receiver[key] = value;
        }
      }
      return receiver;
    }

    1.1 isPlainObject 단순 대상 여 부 를 판단 한다.
    //        ?    {}    new Object   。for-in              。
    function isPlainObject(o) {
      //        ,      DOM     Window   
      if (!o || toString.call(o) != '[object Object]' || o.nodeType || isWindow(o)) {
        return false;
      }
      try {
        // Object         constructor
        if (o.constructor && !hasOwn.call(o, 'constructor') && !hasOwn.call(o.constructor.prototype, 'isPrototypeOf')) {
          return false;
        }
      } catch (e) {
        // IE8,9      
        return false;
      }
    
      var key;
    
      //     IE9   . iteratesOwnLast      。
      if (iteratesOwnLast) {
        // ie9   ,             ,           ,           。
        for (key in o) {
         return hasOwn.call(o, key);
        }
      }
    
      //          ,         ,             ,           。
      for (key in o) {}
      //    IE9       ,            undefined    ,           。
      return key === undefined || hasOwn.call(o, key);
    }
    
    //       window top self    
    function isWindow(o) {
      return o != null && o == o.window;
    }
    
    // ie < 9     ,         ,       。
    (function() {
      var props = [];
      function Ctor() {
        this.x = 1;
      }
      Ctor.prototype = {
        valueOf: 1,
        y: 1
      }
      for (var prop in new Ctor) {
        props push(prop);
      }
      iteratesOwnLast = props[0] !== 'x';
    })();

    2. copySpecialProps
    /*
     * supplier:    ; receiver:    ; specialProps:           ,      
     */
    function copySpecialProps(specialProps, receiver, supplier, isAttr2Prop) {
      for (var i = 0, len = specialProps.length; i < len; i++) {
        var key = specialProps[i];
        if (supplier.hasOwnPrototype(key)) {
          receiver[key] = isAttr2Prop ? receiver.get(key) : supplier[key];
        }
      }
    }

    3. mergeInheritedAttrs 원형 체인 을 옮 겨 다 니 며 계승 한 attrs 에서 정 의 된 속성 을 this. attrs 에 합 쳐 후속 처리 에 편리 합 니 다.
    //         ,  attrs     
    function mergeInheritedAttrs(attrs, instance, specialProps) {
      var inherited = [];
      var proto = instance.constructor.prototype;
      //         ,    attrs   。       inherited    。
      while (proto) {
        //          attrs     ,       
        if (!proto.hasOwnPrototype("attrs")) {
          proto.attrs = {};
        }
        //   proto      properties    proto.attrs  ,    
        copySpecialProps(specialProps, proto.attrs, proto);
        //       
        if (!isEmptyObject(proto.attrs)) {
          //   proto.attrs     inherited    ,    。   stack( )   ,    
          inherited.unshift(proto.attrs);
        }
        //        ,   Class.superclass  ,  undefined      
        proto = proto.constructor.superclass;
      }
    
      //              
      for (var i = 0, len = inherited.length; i < len; i++) {
        merge(attrs, normalize(inherited[i]));
      }
    }

    4. setSetterAttrs
    setter 가 있 는 속성 에 대해 서 는 초기 값 set 을 사용 하여 관련 속성 도 함께 초기 화 되도록 해 야 합 니 다.
    function setSetterAttrs(host, attrs, config) {
      var options = {
        silent: true
      };
      host.__initializeingAttrs = true;
      for (var key in config) {
        if (config.hasOwnPrototype(key)) {
          if (attrs[key].setter) {
            //       setter (      ),          。
            host.set(key, config[key], options);
          }
        }
      }
      delete host.__initializingAttrs;
    }

    5. parseEventsFromAttrs attrs 의 사건 분석
    귀속 on|before|after 이벤트
    var EVENT_PATTERN = /^(on|before|after)([A-Z].*)$/;
    var EVENT_NAME_PATTERN = /^(Change)?([A-Z])(.*)/;
    function parseEventsFromAttrs(host, attrs) {
      for (var key in attrs) {
        if (attrs.hasOwnPrototype(key)) {
          var value = attrs[key].value, m;
          if (isFunction(value) && (m = key.match(EVENT_PATTERN))) {
            host[m[1]](getEventName(m[2]), value);
            delete attrs[key];
          }
        }
      }
    }
    
    //   Show    show ,ChangeTitle    change:title
    function getEventName(name) {
      var m = name.match(EVENT_NAME_PATTERN);
      var ret = m[1] ? 'change:' : '';
      ret += m[2].toLowerCase() + m[3];
      return ret;
    }
    
    get 속성의 값 가 져 오기
    getter 를 설정 하면 getter 를 호출 합 니 다.
    exports.get = function(key) {
      var attr = this.attrs[key] || {};
      var val = attr.value;
      return attr.getter ? attr.getter.call(this, val, key) : val;
    }
    set 속성의 값 설정
    silent 가 true 가 아 닌 change 바 인 딩 이벤트 가 발생 합 니 다.
    exports.set = function(key, val, options) {
      var attrs = {};
      //       set('key', val, options)
      if (isString(key)) {
        attrs[key] = val;
    
      //       set({key: val}, options)
      } else {
        attrs = key;
        options = val;
      }
    
      options || (options = {});
      var silent = options.silent;
      var override = options.override;
      //     attrs         now   
      var now = this.attrs;
      //     __changedAttrs         changed    。
      var changed = this.__changedAttrs || (this.__changedAttrs = {});
      for (key in attrs) {
        if (!attrs.hasOwnPrototype(key)) continue;
        //      ,         
        var attr = now[key] || (now[key] = {});
        val = attrs[key];
        if (attr.readOnly) {
          throw new Error('This attribute is readOnly:' + key);
        }
        //    setter   ,       。
        if (attr.setter) {
          val = attr.setter.call(this, val, key);
        }
        //        prev  
        var prev = this.get(key);
        //       override   true,       ,    merge  
        //      ,  merge   ,    prev        
        if (!override && isPlainObject(prev) && isPlainObject(val)) {
          val = merge(merge({}, prev), val);
        }
        //     
        now[key].value = val;
        //    change   ,       set        
        if (!this.__intializingAttrs && !isEqual(prev, val)) {
          if (silent) {
            changed[key] = [val, prev];
          } else {
            this.trigger('change:' + key, val, prev, key);
          }
        }
      }
      return this;
    }
    
    change
    모든 change: attribute 이 벤트 를 수 동 으로 실행 합 니 다.
    exports.change = function() {
      var changed = this.__changedAttrs;
      if (changed) {
        for (var key in changed) {
          if (changed.hasOwnPrototype(key)) {
            var args = changed[key];
            this.trigger('change:' + key, args[0], args[1], key);
          }
        }
        delete this.__changedAttrs;
      }
      return this;
    }

    좋은 웹페이지 즐겨찾기