我最近一直在做很多研究,但还没有得到一个非常好的答案。我在某处读到当JavaScript引擎遇到函数语句时会创建一个新的Function()对象,这会让我相信它可能是一个对象的子对象(从而成为一个对象)。所以我给Douglas Crockford发了电子邮件,答案是:
不完全是因为一个功能 语句不会调用编译器。
但它会产生类似的结果。
另外,据我所知,除非已将实例化为新对象,否则不能在函数构造函数上调用成员。所以这不起作用:
function myFunction(){
this.myProperty = "Am I an object!";
}
myFunction.myProperty; // myFunction is not a function
myFunction().myProperty; // myFunction has no properties
然而,这将有效:
function myFunction(){
this.myProperty = "Am I an object!";
}
var myFunctionVar = new myFunction();
myFunctionVar.myProperty;
这只是一个语义问题......在整个编程世界中,对象何时真正成为一个对象,以及它如何映射到JavaScript?
答案 0 :(得分:13)
关于函数和构造函数没有什么神奇之处。 JavaScript中的所有对象都是......好吧,对象。但是有些对象比其他对象更特殊:即内置对象。差异主要在于以下几个方面:
null
和undefined
是特殊对象。任何在这些对象上使用方法或定义新方法的尝试都会导致异常。+
,-
,*
,/
。+
。()
和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
是我们从构造函数返回的“裸”对象。
答案 1 :(得分:12)
你的理解是错误的:
myFunction().myProperty; // myFunction has no properties
它不起作用的原因是因为“。myProperty”应用于“myFunction()”的返回值,而不是对象“myFunction”。即:
$ js
js> function a() { this.b=1;return {b: 2};}
js> a().b
2
js>
请记住,“()”是一名运营商。 “myFunction”与“myFunction()”不同。当使用new:
实例时,你不需要“返回”js> function a() { this.b=1;}
js> d = new a();
[object Object]
js> d.b;
1
答案 2 :(得分:5)
要回答您的具体问题,技术上的功能始终是对象。
例如,您可以随时执行此操作:
function foo(){
return 0;
}
foo.bar = 1;
alert(foo.bar); // shows "1"
当Javascript函数使用this
指针时,其行为与其他OOP语言中的类有点类似。它们可以实例化作为具有new关键字的对象:
function Foo(){
this.bar = 1;
}
var foo = new Foo();
alert(foo.bar); // shows "1"
现在,从其他OOP语言到Javascript的这种映射很快就会失败。例如,Javascript中实际上没有类这样的东西 - 对象使用原型链来进行继承。
如果您要在Javascript中进行任何重要的编程,我强烈推荐Crockford的Javascript: The Good Parts,那是您通过电子邮件发送的。
答案 3 :(得分:4)
Javascript的“全局”范围(至少在浏览器中)是window
对象。
这意味着当您执行this.myProperty = "foo"
并将该功能调用为普通myFunction()
时,您实际上正在设置window.myProperty = "foo"
myFunction().myProperty
的第二点是,您在这里查看myFunction()
的返回值,所以很自然地,它不会有任何属性,因为它返回null
你在考虑的是:
function myFunction()
{
myFunction.myProperty = "foo";
}
myFunction();
alert(myFunction.myProperty); // Alerts foo as expected
这几乎与
相同var myFunction = new Function('myFunction.myProperty = "foo";');
myFunction();
在new
上下文中使用它时,“返回值”是您的新对象,“this”指针将变为您的新对象,因此这可以按预期工作。
答案 4 :(得分:4)
事实上,职能是“一等公民”:他们是一个对象。
每个对象都有一个Prototype,但只能直接引用一个函数的原型。当使用函数对象作为参数调用new
时,使用函数对象的原型作为原型构造新对象,并在输入函数之前将this
设置为新对象。
所以你可以调用每个函数一个构造函数,即使它只留下this
。
有关于构造函数,原型等的非常好的教程......我个人从Object Oriented Programming in JavaScript学到了很多东西。它显示了“继承”其原型的函数的等价性,但使用this
来填充新对象的属性,以及使用特定原型的函数对象:
function newA() { this.prop1 = "one"; } // constructs a function object called newA
function newA_Too() {} // constructs a function object called newA_Too
newA_Too.prototype.prop1 = "one";
var A1 = new newA();
var A2 = new newA_Too();
// here A1 equals A2.
答案 5 :(得分:1)
首先,JavaScript与C ++ / Java的行为方式不同,因此您需要将这些想法抛出窗口,以便能够理解javascript的工作原理。
执行此行时:
var myFunctionVar = new myFunction();
然后this
内的myFunction()
引用您正在创建的这个新对象 - myFunctionVar
。因此这行代码:
this.myProperty = "Am I an object!";
基本上有
的结果 myFunctionVar.myProperty = "Am I an object!";
您可以帮助您查看new
运算符上的一些文档。在JS中,new
运算符实际上允许您从函数中创建对象 - 任何普通的旧函数。与new
运算符一起使用的函数没有什么特别之处,它将它标记为构造函数,就像在C ++或Java中一样。正如文档所说:
创建用户定义的对象类型需要两个步骤:
- 通过编写函数来定义对象类型。
- 使用new。
创建对象的实例 醇>
所以你用代码做了什么
function myFunction(){
this.myProperty = "Am I an object!";
}
是创建一个可用作构造函数的函数。代码myFunction.myProperty
失败的原因是没有名为myFunction
的引用。
答案 6 :(得分:0)
JavaScript基于ECMA脚本。它的规范使用原型模型为OOP。但是,ECMA脚本不强制执行严格的数据类型。 该对象需要实例化,原因与ECMA脚本需要一个“new”调用一样,该调用将为该属性分配内存,否则它将保持一个函数,如果你愿意,你可以调用它,在这种情况下,属性将初始化然后在函数结束时被销毁。
答案 7 :(得分:0)
只有在使用new关键字进行实例化时,该函数才会充当构造函数。
结果是一个可以使用“this”关键字访问成员属性的对象。当以任何其他方式使用该函数时,该方法中的this关键字没有任何意义。