[英]Why in JavaScript is a function considered both a constructor and an object?
我最近一直在做很多研究,但还没有得到一个非常好的答案。 我在某处读到当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?
关于函数和构造函数没有什么神奇之处。 JavaScript中的所有对象都是......好吧,对象。 但是有些对象比其他对象更特殊:即内置对象。 差异主要在于以下几个方面:
null
和undefined
是特殊对象。 任何在这些对象上使用方法或定义新方法的尝试都会导致异常。 +
, -
, *
, /
。 +
。 ()
和new
运算符。 后者拥有对如何利用先天的知识prototype
构造函数的性质,构造适当的内部链接原型的对象,并呼吁其建立的构造函数this
正常。 如果您查看ECMAScript标准( PDF ),您将看到所有这些“额外”功能被定义为方法和属性,但其中许多功能不能直接供程序员使用。 其中一些将在标准ES3.1的新版本中公开(截至2008年12月15日的草案: PDF )。 Firefox中已经公开了一个属性( __proto__
)。
现在我们可以直接回答您的问题。 是的,函数对象具有属性,我们可以随意添加/删除它们:
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(xa); // 42 console.log(xb); // 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(ya); // undefined console.log(yb); // 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
是我们从构造函数返回的“裸”对象。
你的理解是错误的:
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()”不同。 instanciang with new时你不需要“返回”:
js> function a() { this.b=1;}
js> d = new a();
[object Object]
js> d.b;
1
要回答您的具体问题,技术上的功能始终是对象。
例如,您可以随时执行此操作:
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中进行任何重要的编程,我强烈推荐您通过电子邮件发送的Javascript: Crockford 的Good Parts 。
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”指针将变为您的新对象,因此这可以按预期工作。
事实上,职能是“一等公民”:他们是一个对象。
每个对象都有一个Prototype,但只能直接引用一个函数的原型。 当new
被调用函数对象作为参数,一个新的对象是使用函数对象的原型为原型建造,并且this
进入功能之前设定为新的对象。
因此,您可以将每个函数调用为构造函数,即使它单独留下this
。
在构造函数,原型等方面有非常好的教程......我个人从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.
首先,JavaScript与C ++ / Java的行为方式不同,因此您需要将这些想法抛出窗口以便能够理解javascript的工作原理。
当这一行执行时:
var myFunctionVar = new myFunction();
然后myFunction()
内部的this
内容引用了你正在创建的这个新对象 - 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
引用 。
JavaScript基于ECMA脚本。 它的规范使用原型模型为OOP。 但是,ECMA脚本不强制执行严格的数据类型。 该对象需要实例化,原因与ECMA脚本需要一个“new”调用一样,该调用将为该属性分配内存,否则它将保持一个函数,如果你愿意,你可以调用它,在这种情况下,属性将初始化然后在函数结束时被销毁。
只有在使用new关键字进行实例化时,该函数才会充当构造函数。
结果是一个可以使用“this”关键字访问成员属性的对象。 当以任何其他方式使用该函数时,该方法中的this关键字没有任何意义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.