Прежде всего, важно понимать, что стандартные свойства функции (аргументы, имя, вызывающая сторона и длина) не могут быть перезаписаны. Поэтому забудьте о добавлении свойства с таким именем.
Добавление собственных пользовательских свойств в функцию может быть выполнено различными способами, которые должны работать в любом браузере.
Добавление собственных пользовательских свойств в функцию
Способ 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: добавить функцию 'extension' в вашу функцию, которая добавляет свойства объекта к себе один за другим:
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
Таким образом, вы можете в любое время расширить несколько свойств и / или скопировать свойства из другого проекта. Опять же, однако, ваш код не СУХОЙ, если вы делаете это чаще.
Способ 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
Генетическая функция расширения позволяет использовать более СУХОЙ подход, позволяя вам добавить объект или любой проект к любому другому объекту.
Способ 6: Создать объект extendedableFunction и использовать его для присоединения функции расширения к функции:
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
Вместо того, чтобы использовать обобщенную функцию 'extension', этот метод позволяет вам генерировать функции, к которым прикреплен метод 'extend'.
Способ 7: Добавить функцию 'extension' в прототип функции:
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
Большим преимуществом этого метода является то, что он делает добавление новых свойств в функцию очень простым и СУХИМЫМ, а также полностью ОО. Кроме того, это довольно дружелюбно для памяти. Недостатком, однако, является то, что это не очень будущее. В случае, если будущие браузеры когда-либо добавят встроенную функцию 'extension' в прототип Function, это может нарушить ваш код.
Способ 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
Запустите функцию один раз и проверьте, установлено ли одно из ее свойств. Если не установлено, установите свойства и верните себя. Если установлено, выполните функцию. Если в качестве одного из свойств вы включили функцию 'extension', вы можете позже выполнить ее, чтобы добавить новые свойства.
Добавление собственных свойств к объекту
Несмотря на все эти параметры, я бы все же рекомендовал не добавлять свойства в функцию.Гораздо лучше добавлять свойства к объектам!
Лично я предпочитаю одноэлементные классы со следующим синтаксисом.
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;
}
}
})();
Преимущество этого синтаксиса состоит в том, что он допускает как публичные, так и приватныепеременные.Например, вот как вы делаете приватную переменную data:
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());