将函数分配给影响其他实例的对象的属性?

时间:2014-05-24 15:39:21

标签: javascript this

所以我通过构造参数将一个函数作为属性添加到我的,就像这样。

var Class = function (options) {
  this.a  = options.a;
  this.b  = options.b;
  this.fn = options.fn;
};

Class.prototype.fn = function () { return 1; };

var instance = new Class({
  a: 1,
  b: 2,
  fn: function () { return 2; }
});

无论出于何种原因,无论出于何种原因,其中一个函数都在其他实例之间共享。我将包括我正在编写的实际代码。为简洁起见,这是我的实际构造函数......

var QueryProperty = function (options) {
  if (options.writePath) {
    this.writePath = options.writePath;
  }
  else {
    throw new TypeError("Argument writePath need to specified");
  }

  this.readPath = options.readPath || null;

  if (options.transformation) {
    this._transformation = options.transformation;
  }

  if (options.isSpecified) {
    this._isSpecified = options.isSpecified;
  }
};

我尝试了以下内容,

  • 使用Function.prototype.bind
  • 绑定构造函数中的函数
  • 尝试在QueryProperty上使用一些静态方法创建一个实例,然后将这些函数分配给构造函数外的对象。
  • 一些基于JQuerys扩展方法的随机npm库,以及_.extend和&内置Object.create
  • 以及其他一些相关的事情,其中​​没有一个有效。

这是我正在创建对象的实例

var toNum = function (n) { return parseInt(n, 10); };

var properties = [
  new QueryProperty({
     readPath: 'activities',
     writePath: [0, '$match', 'incident', '$match', 'activity'],
     transformation: toNum,
   }),
   new QueryProperty({
     readPath: 'cause',
     writePath: [0, '$match', 'incident', '$match', 'cause'],
     transformation: toNum,
   }),
   new QueryProperty({
     readPath: 'fatal',
     writePath: [0, '$match', 'incident', '$match', 'fatal'],
     transformation: function (param) {
       return param !== 'false';
     },
     isSpecified: function (params) {
       return params[this.readPath].indexOf(',') === -1;
     }
   })
 ];

奇怪的是,在我将isSpecified包含在最后一个QueryProperty实例中之前,一切行为都很好。我的一部分认为可能在函数中使用this可能会搞砸了所有内容,就像当您调用this.isSpecified并且您记录属性this.readPath时,它说'fatal'这甚至在我将属性绑定到构造函数中的对象之后。它也很有趣,因为当我在变量前加上下划线时会发生这种情况,例如_readPath

此代码全部用于与相关的Node.js服务器。

编辑:

此处正在调用_isSpecified方法。

QueryProperty.prototype.apply = function (urlQuery, dbQuery) {
  if (this._isSpecified(urlQuery)) {
    this._pathInit.forEach(function (prop) {
      if (!dbQuery[prop]) {
        dbQuery[prop] = {};
      }
      dbQuery = dbQuery[prop];
    });
    dbQuery[this._pathLast] = this._getWriteValue(urlQuery);
  }
};

this._pathInit& this._pathLast都是属性,由Object.defineProperties定义,但我只是这样说,所以你知道这些属性的来源。

所有代码

属性模块

var _ = require('underscore');

/** 
 * class QueryProperty
 *   Properties
 *     - _readPath  :: Maybe[String]
 *     - _writePath :: Union(Array[String], String)
 *     - _pathInit  :: Array[String]
 *     - _pathLast  :: String
 *   Functions
 *     - apply           :: Object -> Object -> undefined 
 *     - isSpecified     :: Object -> Boolean
 *     - _transformation :: Any -> Any
 *     - _getWriteValue  :: -> Any
 */

var Class = function (options) {
  if (options.writePath) {
    this._writePath = options.writePath;
  }
  else {
    throw new TypeError("Arguement writePath need to specified");
  }

  this._readPath = options.readPath || null;

  if (options.transformation) {
    this._transformation = options.transformation.bind(this);
  }

  if (options.isSpecified) {
    this._isSpecified = options.isSpecified.bind(this);
  }
};

// determines if a urlQuery property is specified
Class.prototype._isSpecified = function (inQuery) {
  if (this._readPath !== null) {
    var ref = inQuery[this._readPath]; 
    return !(ref === undefined || ref === null);
  }
  else {
    return true;
  }
};

// takes read value & does some preprocessing
Class.prototype._transformation = function (x) { return x; }; 

Class.prototype._getWriteValue = function (inQuery){
  if (this._readPath !== null) {
    return this._transformation(inQuery[this._readPath]);
  }
  else {
    return this._transformation(inQuery);
  }
}; 

Object.defineProperties(Class.prototype, {
  // gets everything in the path except the last element
  readPath: {
    get: function () {
      return this._readPath;
    }
  },
  _pathInit: {
    get: function () {
      return _.isArray(this._writePath) ? 
        _.initial(this._writePath) : [];
    },
  },
  // gets the last element in the path
  _pathLast: {
    get: function () {
      return _.isArray(this._writePath) ? 
        _.last(this._writePath) : this._writePath;
    }
  }
});

Class.prototype.apply = function (urlQuery, dbQuery) {
  console.log(this._readPath);
  if (this._isSpecified(urlQuery)) {
    this._pathInit.forEach(function (prop) {
      if (!dbQuery[prop]) { 
        dbQuery[prop] = {}; 
      } 
      dbQuery = dbQuery[prop];
    }); 
    dbQuery[this._pathLast] = this._getWriteValue(urlQuery); 
  }
};

Class.create = function (obj) {
  var _new = new Class({
    writePath: obj.writePath,
    readPath: obj.readPath,    
  });

  _new._transformation = obj.transformation;
  _new._isSpecified = obj.isSpecified;

  return _new;
};

module.exports = Class;

模块调用它。

var _             = require('underscore'),
    QueryProperty = require('./property'),

    Module,
    properties,
    generateQuery;

/**
 * Used to make the difference between the 
 * parameters used in the http requests url 
 * query, & the location within the mongo
 * document structure. 
 */
var urlEncodeToMongoLookUp = {
  location: '',
  activities: 'incident.activity',
  cause: 'incident.cause',
  occupation: 'occupation.occupation',
  industry: '',
  injuries: 'incident.injury',
  workload: '',
  fatal: 'incident.fatal', 
  age: ''
};

var toNum = function (n) {
  return parseInt(n, 10);
};

/**
 * Properties of the query
 */
properties = [
  new QueryProperty({
    readPath: 'location',
    writePath: [0, '$match', 'incident', '$match', 'bodyLocation'],
    transformation: toNum,
  }),
  new QueryProperty({
    readPath: 'activities',
    writePath: [0, '$match', 'incident', '$match', 'activity'],
    transformation: toNum,
  }),
  new QueryProperty({
    readPath: 'cause',
    writePath: [0, '$match', 'incident', '$match', 'cause'],
    transformation: toNum,
  }),
  new QueryProperty({
    readPath: 'fatal',
    writePath: [0, '$match', 'incident', '$match', 'fatal'],
    transformation: function (param) {
      return param !== 'false'; 
    },
    isSpecified: function (param) {
      return param[this.readPath].indexOf(',') === -1;
    }
  }),
  new QueryProperty({
    readPath: 'occupation',
    writePath: [0, '$match', 'occupation', '$match', 'occupation'],
    transformation: toNum,
  }),
  new QueryProperty({
    readPath: 'grouping',
    writePath: [1, '$group', 'description'],
    transformation: function (grouping) {
      return '$' + urlEncodeToMongoLookUp[grouping];
    },
  }),
  new QueryProperty({
    readPath: 'grouping',
    writePath: [1, '$group', 'value'],
    transformation: function () { return { $sum: 1 }; },
  }),
];


generateQuery = function (rawQuery) {
  var mongoQuery = [];

  properties.forEach(function (prop) {
    prop.apply(rawQuery, mongoQuery);
  });

  return mongoQuery;
};


module.exports = Module = function (collection) {
  return function (rawQuery, callback) {
    collection.aggregate(generateQuery(rawQuery), callback);  
  };
};

Module.generateQuery = generateQuery;

0 个答案:

没有答案