所以我通过构造参数将一个函数作为属性添加到我的类,就像这样。
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
_.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;