函数和构造函数并没有什么神奇的地方。在JavaScript中,所有的对象都是对象。但是有些对象比其他对象更特别:即内置对象。区别主要在以下方面:
- General treatment of objects. Examples:
- Numbers and Strings are immutable (⇒ constants). No methods are defined to change them internally — new objects are always produced as the result. While they have some innate methods, you cannot change them, or add new methods. Any attempts to do so will be ignored.
null
and undefined
are special objects. Any attempt to use a method on these objects or define new methods causes an exception.
- Applicable operators. JavaScript doesn t allow to (re)define operators, so we stuck with what s available.
- Numbers have a special way with arithmetic operators:
+
, -
, *
, /
.
- Strings have a special way to handle the concatenation operator:
+
.
- Functions have a special way to handle the "call" operator:
()
, and the new
operator. The latter has the innate knowledge on how to use the prototype
property of the constructor, construct an object with proper internal links to the prototype, and call the constructor function on it setting up this
correctly.
如果您查看 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
执行的:
- New empty object (
{}
) was created.
- Its internal prototype property was set to
fun.prototype
. In our case it will be an empty object ({}
) because we didn t modify it in any way.
fun()
was called with this new object as a context.
这取决于我们的功能来修改新对象。通常它会设置对象的属性,但它可以做任何它想做的事情。
有趣的小知识:
因为构造器只是一个对象,我们可以计算它:
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
是我们从构造函数返回的“裸对象”。