JavaScript随笔(二):函数只是一种对象

上一篇随笔提到JS中有两种数据类型:原始类型和对象类型,但是我们还没有提到函数。实际上函数也是一种对象,准确地说函数应该叫做函数对象。下面我们从对象开始说起。

1. 对象

最简单的对象,是这样的:

var obj = {};

我们创建了一个空对象。说它空是因为它没有任何自定义属性,但是实际上它还是有一些默认属性的,这些属性是从它的原型对象继承来的,比如constructor、toString和valueOf等。除此之外,ECMA-262中还定义了对象的一些内置属性,这些属性对JS语言来说是不可见的,也就是说跟我们的编程无关。但是简单地了解一下这些内置属性,对我们深入理解对象的内部机制是有益的。这些属性包括:[[Prototype]]、[[Extensible]]、[[Get]]、[[Put]]、[[HasProperty]]和[[Delete]]等,具体的含义在此不提,只需要知道,只要是对象就一定有这些属性。

2. 函数

接下来我们说一下函数。我们经常听人说“函数是JS中的一等公民,函数可以做参数,可以做返回值”。其实原因很简单,函数也是一种对象,是一种特殊的对象。既然是对象,当然是一等公民了。它只是比普通的对象多了一些特殊的性质,所以看起来跟普通对象有所不同。那么,函数与普通对象比有哪些不同呢?

(1) 从语言规范或者说从JS引擎的角度来看,函数除了前面提到的那些内置属性外,还多了一些内置属性,包括:

    • [[Code]]:函数的代码,即函数体里面的那些语句。
    • [[FormalParameters]]:函数的形参列表。
    • [[Scope]]:词法环境(Lexical Environment),它确定了函数执行时所处的环境。这个东西很重要,跟我们经常听说的作用域链闭包有关系。
    • [[Call]]:执行代码,即在调用此函数时执行的动作。
    • [[Construct]]:构造一个新的对象的动作。拥有这个内置属性的函数被称为构造函数,通过new操作符可以用它来产生新的对象。
    • [[HasInstance]]:用来检测一个对象是否是此函数构造的。

为了强调函数是一种对象,我们甚至可以把函数想象成对象的形式:

// 某个求较大值的函数的对象表示形式
var max = {
    _callable_: true,
    _args_: ['a', 'b'],
    _code_: 'return a > b ? a : b;'
};

(2) 由于函数是一种特殊的以及非常重要的对象,所以JS语言专门提供了特殊的语法来创建函数,即

// 函数声明
function sayHello() {
    console.log('Hello');   
}

// 函数表达式
var sayHello = function() {
    console.log('Hello');
}

当然,也可以用类似于创建普通对象的方式(即new+构造函数)来创建函数,即

var sayHello = new Function("console.log('Hello');");

是不是跟普通对象的方式很像啊:

var data = new Date('2015/09/10');

显然Function是一个非常特殊的构造函数,因为一般的构造函数只能创建普通对象,但是Function能创建一个函数对象。

(3) 函数是可调用的。我们通过函数调用表达式来调用一个函数,从而完成实现某种特定的功能,例如:

function add(x, y) {
    return x + y;
}

var z = add(3, 4);
console.log(z); // 输出7

这也是我们使用函数最常见的目的。

3. 构造函数

其实上面已经顺带把构造函数也讲了。构造函数是指可以利用 new 操作符来创建对象的函数,这个过程是通过内置属性[[Construct]]实现的。比如Object就是一个构造函数:

var obj = new Object();

实际上,我们在程序里使用函数声明、函数表达式或Function创建出来的函数都是即使普通函数也是构造函数。它们既可以被调用以得到返回值或产生某种效果,也可以用new操作符来创建对象。但是从本质上讲构造函数比普通函数要更特殊一些。

另外为了进行区分,在命名时,前者通常首字母小写,后者通常首字母大写。举一个构造函数的例子:

function Person(name, age) {
    this.name = name;
    this.age = age;    
}

可能有人要问了,是否存在不是构造函数的函数?当然是存在的,比如JS中的那些内置函数基本都属于这种情况。例如:

typeof parseInt; // 结果为"function"
parseInt('123'); // 结果为123
new parseInt(); // 抛出TypeError异常,提示function parseInt() { [native code] } is not a constructor 

可见parseInt可以作为一个函数来调用,但是不能作为构造函数来创建新的对象。

posted @ 2015-09-09 21:54  manxisuo  阅读(369)  评论(0编辑  收藏