函数和构造函数没有什么神奇之处。JavaScript 中的所有对象都是……嗯,对象。但是有些对象比其他对象更特殊:即内置对象。区别主要在于以下几个方面:
- 对象的一般处理。例子:
- 数字和字符串是不可变的(⇒ 常量)。没有定义任何方法来在内部更改它们——结果总是产生新的对象。虽然它们有一些先天方法,但您无法更改它们或添加新方法。任何这样做的尝试都将被忽略。
null
并且undefined
是特殊的对象。任何对这些对象使用方法或定义新方法的尝试都会导致异常。
- 适用的运算符。JavaScript 不允许(重新)定义运算符,所以我们坚持使用可用的。
- 数字对算术运算符有一种特殊的方式:
+
, -
, *
, /
。
- 字符串有一种特殊的方式来处理连接运算符:
+
.
- 函数有一种特殊的方式来处理“调用”操作符:
()
和new
操作符。后者具有关于如何使用构造函数的prototype
属性、构造具有与原型的适当内部链接的对象以及调用构造函数对其this
正确设置的先天知识。
如果您查看 ECMAScript 标准 ( PDF ),您会发现所有这些“额外”功能都被定义为方法和属性,但其中许多功能不能直接供程序员使用。其中一些将在标准 ES3.1 的新修订版中公开(截至 2008 年 12 月 15 日的草案:PDF)。一个属性 ( __proto__
)已在 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
都是我们从构造函数返回的“裸”对象。