函数和构造函数没有什么神奇之处。 JavaScript 中的所有对象都是……嗯,对象。但是有些对象比其他对象更特殊:即内置对象。区别主要在于以下几个方面:
- 对象的一般处理。例子:
- 数字和字符串是不可变的(⇒ 常量)。没有定义任何方法来在内部更改它们——结果总是产生新的对象。虽然它们有一些先天方法,但您无法更改它们或添加新方法。任何这样做的尝试都将被忽略。
-
null 和 undefined 是特殊对象。对这些对象使用方法或定义新方法的任何尝试都会导致异常。
- 适用的运算符。 JavaScript 不允许(重新)定义运算符,所以我们坚持使用可用的。
- 数字与算术运算符有一种特殊的方式:
+、-、*、/。
- 字符串有一种特殊的方式来处理连接运算符:
+。
- 函数有一种特殊的方式来处理“调用”运算符:
() 和new 运算符。后者拥有关于如何使用构造函数的prototype 属性、构造具有与原型的适当内部链接的对象以及调用构造函数正确设置this 的先天知识。
如果您查看 ECMAScript 标准 (PDF),您会发现所有这些“额外”功能都被定义为方法和属性,但其中许多功能不能直接供程序员使用。其中一些将在标准 ES3.1 的新修订版中公开(截至 2008 年 12 月 15 日的草案:PDF)。一个属性 (__proto__) 是 already exposed in Firefox。
现在我们可以直接回答您的问题。是的,函数对象有属性,我们可以随意添加/删除它们:
var fun = function(){/* ... */};
fun.foo = 2;
console.log(fun.foo); // 2
fun.bar = "Ha!";
console.log(fun.bar); // Ha!
函数实际上做什么并不重要——它永远不会发挥作用,因为我们没有调用它!现在让我们定义它:
fun = function(){ this.life = 42; };
它本身不是一个构造函数,它是一个对其上下文进行操作的函数。我们可以轻松地提供它:
var context = {ford: "perfect"};
// now let's call our function on our context
fun.call(context);
// it didn't create new object, it modified the context:
console.log(context.ford); // perfect
console.log(context.life); // 42
console.log(context instanceof fun); // false
如您所见,它为现有对象添加了一个属性。
为了将我们的函数用作构造函数,我们必须使用new 运算符:
var baz = new fun();
// new empty object was created, and fun() was executed on it:
console.log(baz.life); // 42
console.log(baz instanceof fun); // true
如您所见,new 使我们的函数成为构造函数。以下操作由new完成:
- 已创建新的空对象 (
{})。
- 其内部原型属性设置为
fun.prototype。在我们的例子中,它将是一个空对象 ({}),因为我们没有以任何方式对其进行修改。
-
fun() 是用这个新对象作为上下文调用的。
由我们的函数来修改新对象。通常它设置对象的属性,但它可以为所欲为。
有趣的琐事:
-
因为构造函数只是一个我们可以计算的对象:
var A = function(val){ this.a = val; };
var B = function(val){ this.b = val; };
var C = function(flag){ return flag ? A : B; };
// now let's create an object:
var x = new (C(true))(42);
// what kind of object is that?
console.log(x instanceof C); // false
console.log(x instanceof B); // false
console.log(x instanceof A); // true
// it is of A
// let's inspect it
console.log(x.a); // 42
console.log(x.b); // undefined
// now let's create another object:
var y = new (C(false))(33);
// what kind of object is that?
console.log(y instanceof C); // false
console.log(y instanceof B); // true
console.log(y instanceof A); // false
// it is of B
// let's inspect it
console.log(y.a); // undefined
console.log(y.b); // 33
// cool, heh?
-
构造函数可以返回一个覆盖新创建对象的值:
var A = function(flag){
if(flag){
// let's return something completely different
return {ford: "perfect"};
}
// let's modify the object
this.life = 42;
};
// now let's create two objects:
var x = new A(false);
var y = new A(true);
// let's inspect x
console.log(x instanceof A); // true
console.log(x.ford); // undefined
console.log(x.life); // 42
// let's inspect y
console.log(y instanceof A); // false
console.log(y.ford); // perfect
console.log(y.life); // undefined
如您所见,x 是带有原型的A,而y 是我们从构造函数返回的“裸”对象。