<script type="text/javascript">
var f = function() {
this.x = '1';
alert(this.s); // undefined
}
f.s = '2';
f();
alert(f.s); // the value's there
alert(f.x); // undefined
</script>
我似乎能够在一个用函数实例化的对象中保存属性,但我无法从函数中访问它们,也无法从外部访问函数变量......是否有一些特殊的技巧可以刺破面纱?
答案 0 :(得分:3)
this
指的是调用函数的上下文,而不是函数本身
您正在寻找arguments.callee
,它指的是当前正在执行的函数。
答案 1 :(得分:3)
首先,重要的是要意识到标准函数属性(参数,名称,调用者和长度)不能被覆盖。所以,忘记添加一个具有该名称的属性。
将自己的自定义属性添加到函数可以通过不同的方式完成,这些方法应该适用于每个浏览器。
方式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:在函数中添加一个'extend'函数,将对象的属性逐个添加到自身:
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 (isArray(args) || (args !== null && typeof args === 'object')) {
for (i in args) {
this[i] = args[i];
}
}
return obj;
}
var Job = 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对象并使用它将extend函数附加到函数:
var extendableFunction = (function() {
var extend = function(args) {
if (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
这种技术不是使用通用的'extend'函数,而是允许您生成附加了'extend'方法的函数。
方式6:向函数原型添加'extend'函数:
Function.prototype.extend = function(args) {
if (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
这项技术的一大优势在于它可以非常轻松地为函数添加新属性,以及完全OO。此外,它非常友好。然而,缺点是它不是未来的证据。如果未来的浏览器曾向Function原型添加本机“扩展”功能,这可能会破坏您的代码。
方式7:递归运行一次函数然后返回它:
var doSomething = (function f(arg1) {
if(f.name2 === undefined) {
f.name = 'Tom';
f.name2 = 'John';
f.extend = function(obj, args) {
if (isArray(args) || (args !== null && typeof args === 'object')) {
for (i in args) {
this[i] = args[i];
}
}
return obj;
};
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());
答案 2 :(得分:2)
var f = function() {
this.x = '1';
}
var eff = new f();
eff.s = '2';
alert(eff.s);
alert(eff.x);
我不确定我是否回答了你想知道的一切,但我认为你正在寻找new
来创建一个新的对象实例。
答案 3 :(得分:1)
> var f = function() {
> this.x = '1';
> alert(this.s); // undefined }
没有理由使用声明会做得更好的表达式。如果使用表达式,则在执行语句之前该函数将不可用。使用声明,该功能将在执行开始时立即可用,无论它在程序中的何处。
函数的此值在调用函数时设置,它的值取决于调用(忽略ES 5 bind 方法)。
> f.s = '2';
函数是对象,因此上面将向 f 函数对象添加 s 属性。
> f();
在没有合格路径的情况下调用 f 意味着在函数中, this 将引用全局对象。因此,alert(this.s)
行将返回undefined,因为全局对象没有 s 属性。
alert(f.s); // the value's there
即访问 f 的 属性,该属性已创建并分配了上述值。
> alert(f.x); // undefined
是。但是打电话给 f :
alert( x ) // 1
因为f()
执行以下行:
this.x = '1';
和此是全局/窗口对象,因此添加属性 x 并赋值为1.
答案 4 :(得分:1)
正如SLaks所指出的,您可以使用arguments.callee
来引用当前正在运行的函数。
var foo = function() {
return arguments.callee.x || 'foo';
}
foo(); // returns 'foo'
foo.x = 'bar';
foo(); // returns 'bar'
然而,(至少根据我的The Definitive Guide副本)在ECMAScript 5中使用arguments.callee
严格模式会引发TypeError
。另一种方法是使用命名而不是匿名函数:
function foo() {
return foo.x || 'foo';
}
foo(); // returns 'foo'
foo.x = 'bar';
foo(); // returns 'bar'
但请注意,函数体中对foo.x
的引用意味着如果您将foo重新指定给其他内容,则引用将指向新对象 - 请参阅this question。