SyntheticEvent

SyntheticEvent模块将原生事件对象event合成为react机制的合成事件对象,包含触发节点所在的ReactDomComponent实例,以及阻止默认事件、冒泡等方法。

合成事件对象的dispatchConfig属性记录相应的事件名,以获取触发事件组件实例及其直系父组件的绑定回调函数,同时该组实例和回调函数添加为合成事件对象的_dispatchInstances、_dispatchListeners属性。

 

'use strict';

var _assign = require('object-assign');

var PooledClass = require('./PooledClass');

// 返回特定值的空函数
var emptyFunction = require('fbjs/lib/emptyFunction');

var warning = require('fbjs/lib/warning');

var didWarnForAddedNewProperty = false;

// 判断浏览器平台是否支持Proxy代理
// var proxy=new Proxy(target,handler)调用代理对象的方法将转发给目标对象target;代理对象不同于目标对象
// 句柄对象将重写代理对象的内建方法,否则将直接对目标对象起作用
// 内建方法包含:
// set(target,key,value,receiver)赋值,key作为属性名,value作为属性值
// get(target,key,receiver)取值,key作为属性名
// has(target,key)包含属性,key作为属性名,拦截key in target操作
// deleteProperty(target,key)删除属性,key作为属性名,拦截delete proxy[key]操作
// defineProperty(target,key,descriptor)定义属性,拦截Object.defineProperty(proxy,key,descriptor)操作,返回布尔值
//    若目标对象不可扩展,使用代理对象的defineProperty扩展不存在的属性将报错
//    若目标对象不可写或不可配置,使用代理对象的defineProperty不能更改这两个设置
// preventExtensions(target)设定代理对象不可扩展,需要目标对象也同样不可扩展Object.isExtensible(target)=false
// isExtensible(target)判断目标对象是否可扩展,拦截Object.isExtensible(proxy)操作
// getOwnPropertyDescriptor(target,key)拦截Object.getOwnPropertyDescriptor(proxy,key)获取属性描述符操作
// getPrototypeOf(target)获取原型对象__proto__,拦截Object.getPrototypeOf(proxy)
// setPrototypeOf(target, proto)设置原型对象,拦截Object.setPrototypeOf(proxy)
// apply(target,ctx,args)目标对象作为函数或基于apply、call方法使用时调用,ctx为上下文对象,args为参数
// construct(target,args)使用new关键字将target作为构造函数时调用
// ownKeys(target)数组形式获取对象的属性,拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)操作

// es6的Reflect对象,将Object对象的内建方法移入Reflect对象中,Reflect.defineProperty等同Object.defineProperty
// 使语言更合理、操作更单纯
var isProxySupported = typeof Proxy === 'function';

// 销毁合成事件实例时必须被销毁或提示不能读取的属性
var shouldBeReleasedProperties = ['dispatchConfig', '_targetInst', 'nativeEvent', 
'isDefaultPrevented', 'isPropagationStopped', '_dispatchListeners', '_dispatchInstances'];

// EventInterface[propName]为真值且是函数时,以原生event对象为参数,获取合成事件对象的属性
// EventInterface[propName]为否值时直接copy原生event对象的属性,做了兼容性处理的target属性除外
var EventInterface = {
  type: null,
  target: null,
  // currentTarget is set when dispatching; no use in copying it here
  currentTarget: emptyFunction.thatReturnsNull,
  eventPhase: null,
  bubbles: null,
  cancelable: null,
  timeStamp: function (event) {
    return event.timeStamp || Date.now();
  },
  defaultPrevented: null,
  isTrusted: null
};

// react形式的合成事件对象,做兼容性处理
// 添加dispatchConfig派发事件对象,react内部使用
// _targetInst属性指向邻近的ReactDomComponent实例
// nativeEvent属性指向原生事件对象
// isDefaultPrevented属性判断默认事件是否被终止;preventDefault方法终止默认事件
// isPropagationStopped属性判断事件冒泡是否被终止;preventDefault方法终止事件冒泡

// 参数dispatchConfig为react注入的派发事件形式,形式为
//  {
//    phasedRegistrationNames: {
//      bubbled: onEvent,
//      captured: onEvent + 'Capture'
//    },
//    dependencies: [topEvent]
//  };
// 参数targetInst为触发节点邻近的ReactDomComponent实例
// 参数nativeEvent为浏览器原生事件对象,
// 参数nativeEventTarget为触发事件的节点
function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
  if (process.env.NODE_ENV !== 'production') {
    // these have a getter/setter for warnings
    delete this.nativeEvent;
    delete this.preventDefault;
    delete this.stopPropagation;
  }

  this.dispatchConfig = dispatchConfig;
  this._targetInst = targetInst;
  this.nativeEvent = nativeEvent;

  var Interface = this.constructor.Interface;
  for (var propName in Interface) {
    if (!Interface.hasOwnProperty(propName)) {
      continue;
    }
    if (process.env.NODE_ENV !== 'production') {
      delete this[propName]; // this has a getter/setter for warnings
    }
    var normalize = Interface[propName];

    // EventInterface[propName]为真值函数时,接受原生event为参数,返回合成事件对象的属性
    if (normalize) {
      this[propName] = normalize(nativeEvent);

    // 除target外,其他属性和原生event保持等值
    } else {
      if (propName === 'target') {
        this.target = nativeEventTarget;
      } else {
        this[propName] = nativeEvent[propName];
      }
    }
  }

  var defaultPrevented = nativeEvent.defaultPrevented != null ? 
    nativeEvent.defaultPrevented : nativeEvent.returnValue === false;

  if (defaultPrevented) {
    this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
  } else {
    this.isDefaultPrevented = emptyFunction.thatReturnsFalse;
  }

  this.isPropagationStopped = emptyFunction.thatReturnsFalse;

  return this;
}

_assign(SyntheticEvent.prototype, {
  // 终止默认事件
  preventDefault: function () {
    this.defaultPrevented = true;
    var event = this.nativeEvent;
    if (!event) {
      return;
    }

    if (event.preventDefault) {
      event.preventDefault();
    } else if (typeof event.returnValue !== 'unknown') {
      event.returnValue = false;
    }
    this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
  },

  // 终止事件冒泡
  stopPropagation: function () {
    var event = this.nativeEvent;
    if (!event) {
      return;
    }

    if (event.stopPropagation) {
      event.stopPropagation();
    } else if (typeof event.cancelBubble !== 'unknown') {
      event.cancelBubble = true;
    }

    this.isPropagationStopped = emptyFunction.thatReturnsTrue;
  },

  // 事件结束后调用,之后判断SyntheticEvent实例的isPersistent,销毁SyntheticEvent实例数据
  // SyntheticEvent实例通过PooledClass移入实例池中
  persist: function () {
    this.isPersistent = emptyFunction.thatReturnsTrue;
  },

  // 事件结束后销毁SyntheticEvent实例数据的标识符
  isPersistent: emptyFunction.thatReturnsFalse,

  // PooledClass机制销毁实例数据时使用,生产环境销毁合成事件实例必要的属性,开发环境提示属性不可读取
  destructor: function () {
    var Interface = this.constructor.Interface;
    for (var propName in Interface) {
      if (process.env.NODE_ENV !== 'production') {
        Object.defineProperty(this, propName, getPooledWarningPropertyDefinition(propName, Interface[propName]));
      } else {
        this[propName] = null;
      }
    }
    for (var i = 0; i < shouldBeReleasedProperties.length; i++) {
      this[shouldBeReleasedProperties[i]] = null;
    }
    if (process.env.NODE_ENV !== 'production') {
      Object.defineProperty(this, 'nativeEvent', getPooledWarningPropertyDefinition('nativeEvent', null));
      Object.defineProperty(this, 'preventDefault', getPooledWarningPropertyDefinition('preventDefault', emptyFunction));
      Object.defineProperty(this, 'stopPropagation', getPooledWarningPropertyDefinition('stopPropagation', emptyFunction));
    }
  }

});

// 作为SyntheticEvent构造函数中的this.constructor.Interface
SyntheticEvent.Interface = EventInterface;

if (process.env.NODE_ENV !== 'production') {
  if (isProxySupported) {
    // 将SyntheticEvent修改为代理对象,除construct、apply的内建方法直接作用于原SyntheticEvent对象
    SyntheticEvent = new Proxy(SyntheticEvent, {
      // 当创建的代理对象SyntheticEvent作为构造函数使用new关键字调用时执行
      construct: function (target, args) {
        // this关键字指向何者???
        return this.apply(target, Object.create(target.prototype), args);
      },

      // 当创建的代理对象SyntheticEvent作为函数调用时,其返回的实例设置超纲属性时将予以警告
      // 销毁实例数据时,超纲属性不会被销毁
      apply: function (constructor, that, args) {
        return new Proxy(constructor.apply(that, args), {
          set: function (target, prop, value) {
            if (prop !== 'isPersistent' && !target.constructor.Interface.hasOwnProperty(prop) && 
              shouldBeReleasedProperties.indexOf(prop) === -1) {
              process.env.NODE_ENV !== 'production' ? 
                warning(didWarnForAddedNewProperty || target.isPersistent(), 
                  'This synthetic event is reused for performance reasons. If you\'re ' 
                  + 'seeing this, you\'re adding a new property in the synthetic event object. ' 
                  + 'The property is never released. See ' 
                  + 'https://fb.me/react-event-pooling for more information.') 
                : void 0;
              didWarnForAddedNewProperty = true;
            }
            target[prop] = value;
            return true;
          }
        });
      }
    });
  }
}

// 用于生成子类
SyntheticEvent.augmentClass = function (Class, Interface) {
  var Super = this;

  var E = function () {};

  // 继承SyntheticEvent类的原型方法preventDefault、stopPropagation、persist、isPersistent、destructor
  E.prototype = Super.prototype;
  var prototype = new E();

  // prototype继承SyntheticEvent的原型方法后,再包含Class的原型属性,并作为Class的原型对象
  _assign(prototype, Class.prototype);

  Class.prototype = prototype;

  // Class构造函数使用时预先将SyntheticEvent作为普通函数使用生成实例
  // 再使用SyntheticEvent.augmentClass修改Class类的原型对象
  Class.prototype.constructor = Class;

  Class.Interface = _assign({}, Super.Interface, Interface);

  // 使Class具有SyntheticEvent构造函数形式的生成子类的功能
  Class.augmentClass = Super.augmentClass;

  PooledClass.addPoolingTo(Class, PooledClass.fourArgumentPooler);
};

PooledClass.addPoolingTo(SyntheticEvent, PooledClass.fourArgumentPooler);

module.exports = SyntheticEvent;

// 开发环境提示属性不可读取
function getPooledWarningPropertyDefinition(propName, getVal) {
  var isFunction = typeof getVal === 'function';
  return {
    configurable: true,
    set: set,
    get: get
  };

  function set(val) {
    var action = isFunction ? 'setting the method' : 'setting the property';
    warn(action, 'This is effectively a no-op');
    return val;
  }

  function get() {
    var action = isFunction ? 'accessing the method' : 'accessing the property';
    var result = isFunction ? 'This is a no-op function' : 'This is set to null';
    warn(action, result);
    return getVal;
  }

  function warn(action, result) {
    var warningCondition = false;
    process.env.NODE_ENV !== 'production' ? 
      warning(warningCondition, 'This synthetic event is reused for performance reasons.' 
        + ' If you\'re seeing this, ' 
        + 'you\'re %s `%s` on a released/nullified synthetic event. %s. ' 
        + 'If you must keep the original synthetic event around, use event.persist(). ' 
        + 'See https://fb.me/react-event-pooling for more information.', action, propName, result) 
      : void 0;
  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值