向函数添加自定义属性

寻找合适的答案被证明是困难的,因为存在许多其他问题与我的关键字,所以我会问这里。

正如我们所知,JavaScript 中的函数是对象,它们有自己的属性和方法(更确切地说,是函数实例,从 Function.model 继承而来)。

我正在考虑为一个函数(方法)添加自定义属性,让我们跳过“为什么?”分道扬镳,直奔主题:

var something = {
myMethod: function () {
if (something.myMethod.someProperty === undefined) {
something.myMethod.someProperty = "test";
}
console.log(something.myMethod);
}
}

当使用 Firebug 的 DOM 资源管理器检查时,该属性按预期定义。然而,由于我并不认为自己是一个 JavaScript 专家,我有以下问题:

  1. 这种方法可以被认为是“正确的”和符合标准的吗?它可以在 Firefox 中工作,但是在网络浏览器中有许多东西是按照预期工作的,而且无论如何都不是标准的。
  2. 这种通过向对象添加新属性来修改对象的做法好吗?
68564 次浏览

对你的问题给出一个非常有意义的答案有点困难,因为你好像说过“这是我的解决方案,可以吗?”没有解释你要解决什么问题(你甚至明确表示你不打算解释“为什么”)。您的代码看起来是可以运行的有效 JavaScript,但它看起来也不是一种最佳的执行方式。

如果你解释了你真正想要达到的目标,你可能会得到一些关于更好的代码结构的建议。不过,我还是要给你一些答案:

这种方法可以被认为是“正确的”和符合标准的吗?它可以在 Firefox 中工作,但是在网络浏览器中有许多东西是按照预期工作的,而且无论如何都不是标准的。

函数是对象(如前所述) ,因此可以向它们添加属性。这实际上不是一个标准问题,因为它是所有浏览器都支持的 JavaScript 的核心部分。

这种通过向对象添加新属性来修改对象的做法好吗?

它是你的对象,你可以添加任何你喜欢的属性。对象的全部意义在于它们具有可以操作的属性。我无法想象一种不涉及修改对象的方法,包括添加、删除和更新属性和方法。

话虽如此,对我来说,在 myMethod函数中添加属性并没有什么意义,往 something对象中添加其他属性更为常见(如果调用正确,myMethod函数可以通过 this关键字访问 something的其他属性)。

如果您使用函数作为 构造函数,那么将 方法添加到关联的原型并向每个实例添加(非方法)属性通常是有意义的,但是您可以在适当的时候使用其中一种或两种方法。(注意,“方法”本质上只是一个碰巧引用函数的属性。)

您显示的特定代码不添加属性,它测试 someProperty属性 已经是否存在,如果存在,则为其分配一个新值。

您可能会从阅读 MDN 上的一些文章中获益:

我意识到自己已经晚了很多年,但是我想添加这个例子—— requjs 在 Definition ()函数上设置了一个名为“ AMD”的属性,这非常方便,因为 UMD 模式使用它来检测作用域中的 Definition ()函数实际上是一个 AMD Definition ()函数。

RequreJS 来源: http://requirejs.org/docs/release/2.1.9/comments/require.js

显示此用法的 UMD 模式: https://github.com/umdjs/umd/blob/master/templates/amdWeb.js

首先,必须认识到标准函数属性(参数、名称、调用者和长度)不能被覆盖。因此,忘记添加具有该名称的属性。

向函数添加自定义属性可以采用不同的方式,这些方式应该在每个浏览器中都可以使用。


向函数添加自定义属性

方法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);


方法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);


方法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);

这种策略的一个问题是,您需要至少运行一次函数来分配属性。对于许多功能,这显然不是您想要的。让我们考虑一下其他的选择。


方法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);

现在,在访问属性之前,不需要先运行函数。但是,缺点是您的属性感觉与您的函数脱节。


方法3: 用匿名函数包装函数:

var doSomething = (function(args) {
var f = function() {
return 'Beep';
};
for (var 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);

将函数包装在匿名函数中,可以将属性收集到对象中,并使用循环在匿名函数中逐个添加这些属性。这样,您的属性感觉与您的功能更加相关。当需要从现有对象复制属性时,此技术也非常有用。但是,缺点是在定义函数时只能同时添加多个属性。另外,如果经常需要向函数中添加属性,那么也不会导致 DRY 代码。


方法4: 向函数中添加一个“扩展”函数,它将一个对象的属性一个接一个地添加到它自己:

var doSomething = function() {
return 'Beep';
};
    

doSomething.extend = function(args) {
for (var 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);

这样,您可以随时扩展多个属性和/或从另一个项目复制属性。但是,如果您经常这样做,那么您的代码就不是 DRY。


方法5: 制作一个通用的“扩展”函数:

var extend = function(obj, args) {
if (Array.isArray(args) || (args !== null && typeof args === 'object')) {
for (var 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);

遗传扩展函数允许更多的 DRY 方法,允许您将对象或任何项目添加到任何其他对象。


方法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);

这种技术不使用通用的“扩展”函数,而是允许您生成附加了“扩展”方法的函数。


方法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);

这种技术的一个很大的优点是,它使得向函数添加新属性变得非常容易,并且 DRY 和完全面向对象(OO)。而且,记忆力很好。然而,缺点是这不是未来的证据。以防将来的浏览器在函数原型中添加一个原生的“扩展”函数,这可能会破坏你的代码。


方法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);

运行一次函数,让它测试是否设置了它的一个属性。如果未设置,则设置属性并返回自身。如果设置了,则执行函数。如果包含一个“扩展”函数作为属性之一,以后可以执行该函数来添加新属性。


向对象添加自定义属性

尽管有所有这些选项,我还是建议不要向函数中添加属性。向对象添加属性要好得多!

就个人而言,我更喜欢具有以下语法的单例类。

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;
}
}
})();




console.log('keyValueStore.get("name") : ' + keyValueStore.get("name"));
console.log('keyValueStore.get("name2") : ' + keyValueStore.get("name2"));
try { console.log('keyValueStore.data["name"] : ' + keyValueStore.data["name"]) } catch { console.error('keyValueStore.data not defined') }
try { console.log('keyValueStore.data["name2"] : ' + keyValueStore.data["name2"]) } catch { console.error('keyValueStore.data not defined') }
keyValueStore.set("name", "Tom");
keyValueStore.set("name2", "John");
console.log('keyValueStore.get("name") : ' + keyValueStore.get("name"));
console.log('keyValueStore.get("name2") : ' + keyValueStore.get("name2"));
try { console.log('keyValueStore.data["name"] : ' + keyValueStore.data["name"]) } catch { console.error('keyValueStore.data not defined') }
try { console.log('keyValueStore.data["name2"] : ' + keyValueStore.data["name2"]) } catch { console.error('keyValueStore.data not defined') }

这种语法的一个优点是它同时允许公共变量和私有变量。例如,如何使“ 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;
}
}
})();


console.log('keyValueStore.get("name") : ' + keyValueStore.get("name"));
console.log('keyValueStore.get("name2") : ' + keyValueStore.get("name2"));
try { console.log('keyValueStore.data["name"] : ' + keyValueStore.data["name"]) } catch { console.error('keyValueStore.data not defined') }
try { console.log('keyValueStore.data["name2"] : ' + keyValueStore.data["name2"]) } catch { console.error('keyValueStore.data not defined') }
keyValueStore.set("name", "Tom");
keyValueStore.set("name2", "John");
console.log('keyValueStore.get("name") : ' + keyValueStore.get("name"));
console.log('keyValueStore.get("name2") : ' + keyValueStore.get("name2"));
try { console.log('keyValueStore.data["name"] : ' + keyValueStore.data["name"]) } catch { console.error('keyValueStore.data not defined') }
try { console.log('keyValueStore.data["name2"] : ' + keyValueStore.data["name2"]) } catch { console.error('keyValueStore.data not defined') }

但是您说您想要多个数据存储实例? 没问题!

var keyValueStore = (function() {
var countKVS = 0;
    

return (function kvs() {
return {
'data' : {},
'create' : function() { return new kvs(); },
'countInstances' : function() { return countKVS; },
'get' : function(key) { return this.data[key]; },
'set' : function(key, value) { this.data[key] = value; },
'delete' : function(key) { delete this.data[key]; },
'count' : function() {
var l = 0;
for (p in this.data) l++;
return l;
}
}
})();
})();


kvs = keyValueStore.create(), console.warn('-- KEY VALUE STORE CREATED --');
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs.countKVS);
kvs.set("Tom", "Baker"), console.warn('-- ADDING KEY VALUE PAIR "Tom, "Baker" --');
console.log('kvs.get("Tom") : ' + kvs.get("Tom"));
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs.countKVS);
kvs2 = keyValueStore.create(), console.warn('-- SECOND KEY VALUE STORE CREATED --');
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs2.countKVS);
console.log('kvs2.count() : ' + kvs2.count());
console.log('kvs2.data : ' + JSON.stringify(kvs2.data));
console.log('kvs2.countKVS : ' + kvs2.countKVS);
kvs.set("Daisy", "Hostess"), kvs2.set("Daisy", "Hostess"), console.warn('-- ADDING KEY VALUE PAIR "Daisy", "Hostess"  TO BOTH STORES --');
console.log('kvs.get("Tom") : ' + kvs.get("Tom"));
console.log('kvs.get("Daisy") : ' + kvs.get("Daisy"));
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs2.countKVS);
console.log('kvs2.get("Tom") : ' + kvs2.get("Tom"));
console.log('kvs2.get("Daisy") : ' + kvs2.get("Daisy"));
console.log('kvs2.count() : ' + kvs2.count());
console.log('kvs2.data : ' + JSON.stringify(kvs2.data));
console.log('kvs2.countKVS : ' + kvs2.countKVS);
kvs.delete('Daisy'), console.warn('-- DELETING KEY "Daisy" FROM FIRST STORE --');
console.log('kvs.get("Tom") : ' + kvs.get("Tom"));
console.log('kvs.get("Daisy") : ' + kvs.get("Daisy"));
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs2.countKVS);
console.log('kvs2.get("Tom") : ' + kvs2.get("Tom"));
console.log('kvs2.get("Daisy") : ' + kvs2.get("Daisy"));
console.log('kvs2.count() : ' + kvs2.count());
console.log('kvs2.data : ' + JSON.stringify(kvs2.data));
console.log('kvs2.countKVS : ' + kvs2.countKVS);

最后,您可以分离实例和单例属性,并使用实例的公共方法的原型。结果是以下语法:

var keyValueStore = (function() {
var countKVS = 0; // Singleton private properties
        

var kvs = function() {
countKVS++; // Increment 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]; },
'count' : function() {
var l = 0;
for (p in this.data) l++;
return l;
}
};
        

return  { // Singleton public properties
'create' : function() { return new kvs(); },
'countInstances' : function() { return countKVS; }
};
})();


kvs = keyValueStore.create(), console.warn('-- KEY VALUE STORE CREATED --');
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs.countKVS);
kvs.set("Tom", "Baker"), console.warn('-- ADDING KEY VALUE PAIR "Tom, "Baker" --');
console.log('kvs.get("Tom") : ' + kvs.get("Tom"));
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs.countKVS);
kvs2 = keyValueStore.create(), console.warn('-- SECOND KEY VALUE STORE CREATED --');
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs2.countKVS);
console.log('kvs2.count() : ' + kvs2.count());
console.log('kvs2.data : ' + JSON.stringify(kvs2.data));
console.log('kvs2.countKVS : ' + kvs2.countKVS);
kvs.set("Daisy", "Hostess"), kvs2.set("Daisy", "Hostess"), console.warn('-- ADDING KEY VALUE PAIR "Daisy", "Hostess"  TO BOTH STORES --');
console.log('kvs.get("Tom") : ' + kvs.get("Tom"));
console.log('kvs.get("Daisy") : ' + kvs.get("Daisy"));
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs2.countKVS);
console.log('kvs2.get("Tom") : ' + kvs2.get("Tom"));
console.log('kvs2.get("Daisy") : ' + kvs2.get("Daisy"));
console.log('kvs2.count() : ' + kvs2.count());
console.log('kvs2.data : ' + JSON.stringify(kvs2.data));
console.log('kvs2.countKVS : ' + kvs2.countKVS);
kvs.delete('Daisy'), console.warn('-- DELETING KEY "Daisy" FROM FIRST STORE --');
console.log('kvs.get("Tom") : ' + kvs.get("Tom"));
console.log('kvs.get("Daisy") : ' + kvs.get("Daisy"));
console.log('keyValueStore.countInstances() : ' + keyValueStore.countInstances());
console.log('kvs.count() : ' + kvs.count());
console.log('kvs.data : ' + JSON.stringify(kvs.data));
console.log('kvs.countKVS : ' + kvs2.countKVS);
console.log('kvs2.get("Tom") : ' + kvs2.get("Tom"));
console.log('kvs2.get("Daisy") : ' + kvs2.get("Daisy"));
console.log('kvs2.count() : ' + kvs2.count());
console.log('kvs2.data : ' + JSON.stringify(kvs2.data));
console.log('kvs2.countKVS : ' + kvs2.countKVS);

使用这种语法,您可以拥有:

  • 一个对象的多个实例
  • 私人变量
  • 类变量

将属性附加到函数是 重载 ()操作符的一种漂亮的(可以说是缓慢的/黑客式的)方法,反过来通常用于实现 函子: 对象类型有一个真正重要的工作,其所有其他功能(如果有的话)只是一堆助手。您还可以将这些函数解释为基本上是一个“有状态”函数,其中状态是公共的(例如,大多数内联函数都具有私有状态,即来自本地作用域的状态)。

这个 JSFiddle 演示了我们如何使用带有自定义属性的函数和其他实用工具来实现 translator函数:

/**
* Creates a new translator function with some utility methods attached to it.
*/
var createTranslator = function(dict) {
var translator = function(word) {
return dict[word];
};


translator.isWordDefined = function(word) {
return dict.hasOwnProperty(word);
};


// Add more utilities to translator here...


return translator;
};




// create dictionary
var en2deDictionary = {
'banana': 'Banane',
'apple': 'Apfel'
};


// simple use case:
var translator = createTranslator(en2deDictionary);
var pre = $('<pre>');
$("body").append(pre);


pre.append(translator('banana') + '\n');
pre.append(translator('apple') + '\n');
pre.append(translator.isWordDefined('w00t') + '\n');

正如你所看到的,对于一个专门从事翻译工作的译者来说,这是一个完美的选择。当然,这些对象类型的例子还有很多,但是它们远不如具有多种功能的类型那么常见,比如经典的 UserAnimal Car等类型。对于这些类型,只有在极少数情况下才需要添加自定义属性。通常,您希望将这些类定义为更完整的类,并且通过 thisprototype可以访问它们的公共属性。

但我认为每个伟大的问题都需要简单的答案:

Yes and < strong > Yes *

通过将属性附加到函数,可以清理范围、提高可读性并增加逻辑内聚性。另一个好处是,您可以记录函数和变量之间的关系。我认为这是一个卓越的设计,比在范围上增加变量要好得多 some examples of attaching properties to instances of functions

在这里和这里创建了一些有趣的例子。 给你 还有这里


我认为值得注意的是,你可能不会经常看到这种情况。大多数开发者可能没有意识到这是可能的。有些人对每一个表演都很疯狂..。 但是我认为你可以遵循对象的规则,你会做得很好

如果只想向函数添加自定义属性,那么只需将这些属性添加到 Function.Prototype。例如:

Function.prototype.SomeNewProperty = function () {//Do something awesome here...}

向函数对象添加属性或方法是完全可以接受的。这种事经常发生。JQuery/$对象就是这样的一个例子。这是一个附加了很多方法的函数。

当属性被添加到构造函数中时,它们被称为“ static”属性,可以在没有类实例的情况下调用。例如 Object.create。

我没有足够的名声来写评论,所以我在这里说: 通常认为扩展内置对象的原型是不好的做法,特别是如果你的代码必须与其他人的代码进行比较。它可能产生难以预测的后果,而且很难追踪。

我同意这是一个可能有多个答案的难题,所以我宁愿举一个不同的例子:

假设有一个由生成器填充的 JavaScript Array:

var arr = [...new Array(10).keys()];

就是

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

现在我们想要将它映射到一个新的数组-同样长度,应用一些函数,这样我们就可以使用原生的 map函数属性:

arr = arr.map((value,index) => ++value)

我们刚刚做了一个 value=value+1并返回,所以现在数组看起来像

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

好,现在假设有一个 JavaScript Object

var obj=new Object()

它的定义与前面的数组类似(出于某种疯狂的原因) :

arr.forEach((value,index) => obj[value]=value)

也就是说。

{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}

在这一点上,我们不能应用相同的 map方法,因为它不是为 Object定义的,所以我们必须将它定义为 Object的一个新的 prototype:

Object.defineProperty(Object.prototype, 'mapObject', {
value: function(f, ctx) {
ctx = ctx || this;
var self = this, result = {};
Object.keys(self).forEach(function(k) {
result[k] = f.call(ctx, self[k], k, self);
});
return result;
}
});

在这一点上,我们可以像之前处理数组一样:

obj=obj.mapObject((value,key) => ++value )

因此,我们有:

{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}

您可以看到,我们只更新了值:

[...Object.keys(obj)]
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

然后我们可以返回到输出数组:

[...Object.keys(obj).map(k=>obj[k])]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

这就是它的作用:

// Array.map
var arr = [...new Array(10).keys()];
console.log("array", arr)
arr = arr.map((value, index) => ++value)
console.log("mapped array", arr)
// new property
Object.defineProperty(Object.prototype, 'mapObject', {
value: function(f, ctx) {
ctx = ctx || this;
var self = this,
result = {};
Object.keys(self).forEach(function(k) {
result[k] = f.call(ctx, self[k], k, self);
});
return result;
}
});


// Object.mapObject
var obj = new Object()
arr = [...new Array(10).keys()];
arr.forEach((value, index) => obj[value] = value)
console.log("object", obj)
obj = obj.mapObject((value, key) => ++value)
console.log("mapped object", obj)
console.log("object keys", [...Object.keys(obj)])
console.log("object values", [...Object.keys(obj).map(k => obj[k])])

可能增加约翰斯莱格斯伟大的答案

有没有可能在约翰 · 斯莱格斯之后:

方法2: 在定义函数之后添加属性

添加 方式2.5

function doSomething() {
doSomething.prop = "Bundy";
doSomething.doSomethingElse = function() {
alert("Why Hello There! ;)");


};


let num = 3;
while(num > 0) {
alert(num);
num--;
}
}


sayHi();
sayHi.doSomethingElse();
alert(doSomething.prop);


var ref = doSomething;


ref();
ref.doSomethingElse();
alert(ref.prop);

为了完整起见,直接在函数声明中同时放入“变量”属性和函数属性。因此,避免它被“断开”。保留函数的内部默认工作方式(一个简单的循环) ,以表明它仍然可以工作。没有吗?

test = (function() {
var a = function() {
console.log("test is ok");
};
a.prop = "property is ok";
a.method = function(x, y) {
return x + y;
}
return a
})()


test();
console.log(test.prop);
console.log(test.method(3, 4));

或者,您必须使用 getter 和 setter

var person = {
firstName: 'Jimmy',
lastName: 'Smith',
get fullName() {
return this.firstName + ' ' + this.lastName;
},
set fullName(name) {
var words = name.toString().split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
}
console.log(person.firstName);
console.log(person.lastName);
console.log(person.fullName);
person.fullName = "Tom Jones";
console.log(person.fullName);