Прежде всего, важно понимать, что стандартные свойства функции (аргументы, имя, вызывающий абонент и длина) не могут быть перезаписаны. Итак, забудьте о добавлении свойства с таким именем.
Добавление собственных настраиваемых свойств к функции может выполняться разными способами, которые должны работать в каждом браузере.
Добавление собственных настраиваемых свойств к функции
Способ 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
Обернув вашу функцию анонимной функцией, вы можете собрать свои атрибуты в объект и использовать цикл для добавления этих атрибутов один за другим в анонимную функцию. Таким образом, ваши атрибуты будут более связаны с вашей функцией. Этот метод также очень полезен, когда ваши атрибуты нужно скопировать из существующего объекта. Однако недостатком является то, что вы можете добавлять только несколько атрибутов одновременно, когда вы определяете свою функцию. Кроме того, это не совсем приведет к появлению СУХОГО кода, если вы часто хотите добавлять свойства к функции.
Способ 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
Таким образом, вы можете в любое время расширить несколько свойств и / или скопировать свойства из другого проекта. Однако, опять же, ваш код не СУХИЙ, если вы делаете это чаще.
Способ 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. Создайте объект extendableFunction и используйте его для присоединения функции расширения к функции:
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;
}
}
})();
Преимущество этого синтаксиса в том, что он позволяет использовать как открытые, так и частные переменные. Например, вот как вы делаете частную переменную 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());
person
John Slegers
schedule
22.12.2013