当前位置:  开发笔记 > 编程语言 > 正文

为什么JavaScript中的函数既是构造函数又是对象?

如何解决《为什么JavaScript中的函数既是构造函数又是对象?》经验,为你挑选了3个好方法。

我最近一直在做很多研究,但还没有得到一个非常好的答案.我在某处读到当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?



1> Eugene Lazut..:

关于函数和构造函数没有什么神奇之处.JavaScript中的所有对象都是......好吧,对象.但是有些对象比其他对象更特殊:即内置对象.差异主要在于以下几个方面:

    物体的一般处理.例子:

    数字和字符串是不可变的(⇒常量).没有定义任何方法在内部更改它们 - 总是生成新对象作为结果.虽然它们有一些固有的方法,但您无法更改它们或添加新方法.任何这样做的尝试都将被忽略.

    null并且undefined是特殊对象.任何在这些对象上使用方法或定义新方法的尝试都会导致异常.

    适用的运营商.JavaScript不允许(重新)定义运算符,因此我们坚持使用可用的东西.

    数字与算术运算符一种特殊的方式:+,-,*,/.

    字符串有一种特殊的方式来处理连接运算符:+.

    函数有一种特殊的方式来处理"call"操作符:()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

正如你所看到的那样xA原型和所有,而y我们是从构造函数返回的"裸"对象.



2> niXar..:

你的理解是错误的:

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


刚刚做了"yum install js"; 它实际上是spidermonkey,Firefox js引擎编译为命令行可执行文件.如果使用较小的操作系统,则可以始终使用Java版本的rhino.

3> Triptych..:

要回答您的具体问题,技术上的功能始终是对象.

例如,您可以随时执行此操作:

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.

推荐阅读
携手相约幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有