首先,重要的是要认识到标准函数属性(参数、名称、调用者和长度)不能被覆盖。因此,忘记添加具有该名称的属性。
可以通过不同的方式将您自己的自定义属性添加到函数中,这应该适用于每个浏览器。
将您自己的自定义属性添加到函数
方式1:在运行函数时添加属性:
var doSomething = function() {
doSomething.name = 'Tom';
doSomething.name2 = 'John';
return 'Beep';
};
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name :
doSomething.name2 : undefined
doSomething() : Beep
doSomething.name :
doSomething.name2 : John
方式1(替代语法):
function doSomething() {
doSomething.name = 'Tom';
doSomething.name2 = 'John';
return 'Beep';
};
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name : doSomething
doSomething.name2 : undefined
doSomething() : Beep
doSomething.name : doSomething
doSomething.name2 : John
方式1(第二种替代语法):
var doSomething = function f() {
f.name = 'Tom';
f.name2 = 'John';
return 'Beep';
};
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name : f
doSomething.name2 : undefined
doSomething() : Beep
doSomething.name : f
doSomething.name2 : John
这种策略的一个问题是您需要至少运行一次函数来分配属性。对于许多功能,这显然不是您想要的。因此,让我们考虑其他选项。
方式2:定义函数后添加属性:
function doSomething() {
return 'Beep';
};
doSomething.name = 'Tom';
doSomething.name2 = 'John';
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name : doSomething
doSomething.name2 : John
doSomething() : Beep
doSomething.name : doSomething
doSomething.name2 : John
现在,您无需先运行函数即可访问您的属性。但是,缺点是您的属性感觉与您的功能脱节。
方式3:将您的函数包装在匿名函数中:
var doSomething = (function(args) {
var f = function() {
return 'Beep';
};
for (i in args) {
f[i] = args[i];
}
return f;
}({
'name': 'Tom',
'name2': 'John'
}));
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name :
doSomething.name2 : John
doSomething() : Beep
doSomething.name :
doSomething.name2 : John
将您的函数包装在匿名函数中,您可以将属性收集到一个对象中,并使用循环将这些属性一一添加到匿名函数中。这样,您的属性感觉与您的功能更相关。当您的属性需要从现有对象复制时,此技术也非常有用。然而,一个缺点是您在定义函数时只能同时添加多个属性。此外,如果向函数添加属性是您经常想要做的事情,它不会完全导致 DRY 代码。
方式4:向您的函数添加一个“扩展”函数,将对象的属性一一添加到自身:
var doSomething = function() {
return 'Beep';
};
doSomething.extend = function(args) {
for (i in args) {
this[i] = args[i];
}
return this;
}
doSomething.extend({
'name': 'Tom',
'name2': 'John'
});
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name :
doSomething.name2 : John
doSomething() : Beep
doSomething.name :
doSomething.name2 : John
这样,您可以随时扩展多个属性和/或从另一个项目复制属性。但是,如果这是您经常执行的操作,那么您的代码也不是 DRY。
方式5:制作一个通用的“扩展”功能:
var extend = function(obj, args) {
if (Array.isArray(args) || (args !== null && typeof args === 'object')) {
for (i in args) {
obj[i] = args[i];
}
}
return obj;
}
var doSomething = extend(
function() {
return 'Beep';
}, {
'name': 'Tom',
'name2': 'John'
}
);
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name :
doSomething.name2 : John
doSomething() : Beep
doSomething.name :
doSomething.name2 : John
遗传扩展功能允许使用更 DRY 的方法,允许您将对象或任何项目添加到任何其他对象。
方式 6:创建一个可扩展函数对象并使用它将扩展函数附加到函数:
var extendableFunction = (function() {
var extend = function(args) {
if (Array.isArray(args) || (args !== null && typeof args === 'object')) {
for (i in args) {
this[i] = args[i];
}
}
return this;
};
var ef = function(v, obj) {
v.extend = extend;
return v.extend(obj);
};
ef.create = function(v, args) {
return new this(v, args);
};
return ef;
})();
var doSomething = extendableFunction.create(
function() {
return 'Beep';
}, {
'name': 'Tom',
'name2': 'John'
}
);
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name :
doSomething.name2 : John
doSomething() : Beep
doSomething.name :
doSomething.name2 : John
该技术不是使用通用的“扩展”函数,而是允许您生成附加了“扩展”方法的函数。
方式 7:向函数原型添加一个“扩展”函数:
Function.prototype.extend = function(args) {
if (Array.isArray(args) || (args !== null && typeof args === 'object')) {
for (i in args) {
this[i] = args[i];
}
}
return this;
};
var doSomething = function() {
return 'Beep';
}.extend({
name : 'Tom',
name2 : 'John'
});
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name :
doSomething.name2 : John
doSomething() : Beep
doSomething.name :
doSomething.name2 : John
这种技术的一个很大的优势是它使得向函数添加新属性变得非常容易、干燥并且完全面向对象。此外,它对内存非常友好。然而,一个缺点是它不是很有未来的证据。万一未来的浏览器曾经向函数原型添加原生“扩展”函数,这可能会破坏你的代码。
方式8:递归运行一次函数,然后返回它:
var doSomething = (function f(arg1) {
if(f.name2 === undefined) {
f.name = 'Tom';
f.name2 = 'John';
f.extend = function(args) {
if (Array.isArray(args) || (args !== null && typeof args === 'object')) {
for (i in args) {
this[i] = args[i];
}
}
return this;
};
return f;
} else {
return 'Beep';
}
})();
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
console.log('doSomething() : ' + doSomething());
console.log('doSomething.name : ' + doSomething.name);
console.log('doSomething.name2 : ' + doSomething.name2);
输出 :
doSomething.name : f
doSomething.name2 : John
doSomething() : Beep
doSomething.name : f
doSomething.name2 : John
运行一次函数并让它测试是否设置了它的一个属性。如果未设置,则设置属性并返回自身。如果设置,则执行该功能。如果您包含“扩展”功能作为属性之一,您可以稍后执行该功能以添加新属性。
将您自己的自定义属性添加到对象
尽管有所有这些选项,但我仍然建议不要向函数添加属性。向对象添加属性要好得多!
就个人而言,我更喜欢具有以下语法的单例类。
var keyValueStore = (function() {
return {
'data' : {},
'get' : function(key) { return keyValueStore.data[key]; },
'set' : function(key, value) { keyValueStore.data[key] = value; },
'delete' : function(key) { delete keyValueStore.data[key]; },
'getLength' : function() {
var l = 0;
for (p in keyValueStore.data) l++;
return l;
}
}
})();
这种语法的一个优点是它允许公共和私有变量。例如,这是您将“数据”变量设为私有的方式:
var keyValueStore = (function() {
var data = {};
return {
'get' : function(key) { return data[key]; },
'set' : function(key, value) { data[key] = value; },
'delete' : function(key) { delete data[key]; },
'getLength' : function() {
var l = 0;
for (p in data) l++;
return l;
}
}
})();
但是你想要多个数据存储实例,你说?没问题!
var keyValueStore = (function() {
var count = -1;
return (function kvs() {
count++;
return {
'data' : {},
'create' : function() { return new kvs(); },
'count' : function() { return count; },
'get' : function(key) { return this.data[key]; },
'set' : function(key, value) { this.data[key] = value; },
'delete' : function(key) { delete this.data[key]; },
'getLength' : function() {
var l = 0;
for (p in this.data) l++;
return l;
}
}
})();
})();
最后,您可以分离实例和单例属性,并为实例的公共方法使用原型。这导致以下语法:
var keyValueStore = (function() {
var count = 0; // Singleton private properties
var kvs = function() {
count++; // Instance private properties
this.data = {}; // Instance public properties
};
kvs.prototype = { // Instance public properties
'get' : function(key) { return this.data[key]; },
'set' : function(key, value) { this.data[key] = value; },
'delete' : function(key) { delete this.data[key]; },
'getLength' : function() {
var l = 0;
for (p in this.data) l++;
return l;
}
};
return { // Singleton public properties
'create' : function() { return new kvs(); },
'count' : function() { return count; }
};
})();
使用此语法,您可以拥有:
你像这样使用它:
kvs = keyValueStore.create();
kvs.set('Tom', "Baker");
kvs.set('Daisy', "Hostess");
var profession_of_daisy = kvs.get('Daisy');
kvs.delete('Daisy');
console.log(keyValueStore.count());