JavaScript中的新关键字在第一次遇到时可能会非常令人困惑,因为人们倾向于认为JavaScript不是一种面向对象的编程语言。

这是怎么一回事?它解决了什么问题?什么时候合适,什么时候不合适?


当前回答

JavaScript是一种面向对象的编程语言,它完全用于创建实例。它是基于原型的,而不是基于类的,但这并不意味着它不是面向对象的。

其他回答

new关键字使用函数作为构造函数创建对象的实例。例如:

var Foo = function() {};
Foo.prototype.bar = 'bar';

var foo = new Foo();
foo instanceof Foo; // true

实例继承自构造函数的原型。因此,在上面的例子中。。。

foo.bar; // 'bar'

除了丹尼尔·霍华德的回答之外,以下是new的作用(或者至少看起来是这样):

function New(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype;
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return res;
}

虽然

var obj = New(A, 1, 2);

相当于

var obj = new A(1, 2);

JavaScript是一种支持面向对象编程范式的动态编程语言,用于创建对象的新实例。

对象不需要类。JavaScript是一种基于原型的语言。

请看一下我对案例三的观察。这是关于当你在一个新创建的函数中有一个显式返回语句时会发生什么。查看以下案例:

案例一:

var Foo = function(){
  this.A = 1;
  this.B = 2;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1

上面是调用变量Foo指向的匿名函数的一个简单例子。调用此函数时,它返回undefined。由于没有任何显式的return语句,JavaScript解释器强制插入一个return undefined;语句。因此,上述代码示例相当于:

var Foo = function(){
  this.A = 1;
  this.B = 2;
  return undefined;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1

当调用Foo函数时,窗口是默认的调用对象(上下文对象),它获取新的A和B财产。

案例二:

var Foo = function(){
  this.A = 1;
  this.B = 2;
};
var bar = new Foo();
console.log(bar()); //illegal isn't pointing to a function but an object
console.log(bar.A); //prints 1

在这里,JavaScript解释器看到了新关键字,创建了一个新对象,作为Foo指向的匿名函数的调用对象(上下文this)。在这种情况下,A和B成为新创建对象的财产(代替窗口对象)。由于没有任何显式的return语句,JavaScript解释器会强制插入一个return语句,以返回由于使用new关键字而创建的新对象。

案例三:

var Foo = function(){
  this.A = 1;
  this.B = 2;
  return {C:20,D:30};
};
var bar = new Foo();
console.log(bar.C);//prints 20
console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.

在这里,JavaScript解释器再次看到新关键字,创建一个新对象,该对象充当Foo所指向的匿名函数的调用对象(上下文this)。同样,A和B成为新创建对象的财产。但这次您有一个显式的return语句,所以JavaScript解释器不会自己做任何事情。

在案例III中需要注意的是,由于新关键字而创建的对象从雷达中消失了。bar实际上指向一个完全不同的对象,而不是JavaScript解释器由于新关键字而创建的对象。

引用《JavaScript:最终指南》(第6版)第4章第62页中的David Flanagan:

计算对象创建表达式时,首先使用JavaScript创建一个新的空对象,就像该对象创建的对象一样初始值设定项{}。接下来,它使用指定的参数,将新对象作为this的值传递关键字。然后,函数可以使用它来初始化财产新创建的对象的。编写用作构造函数的函数不返回值,对象创建表达式的值是新创建和初始化的对象。如果构造函数返回一个对象值,该值将成为该对象的值创建表达式,并且丢弃新创建的对象。

其他信息:

上述案例代码片段中使用的函数在JavaScript世界中具有特殊名称,如下所示:

Case # Name
Case I Constructor function
Case II Constructor function
Case III Factory function

您可以在本线程中了解构造函数和工厂函数之间的区别。

案例III中的代码气味-工厂函数不应与我在上面的代码片段中显示的新关键字一起使用。我这么做只是为了解释这个概念。

假设您有此函数:

var Foo = function(){
  this.A = 1;
  this.B = 2;
};

如果将其作为独立函数调用,如下所示:

Foo();

执行此函数将向窗口对象(A和B)添加两个财产。它将其添加到窗口中,因为当您这样执行函数时,window是调用函数的对象,而函数中的this是调用该函数的对象。至少在JavaScript中。

现在,用新的:

var bar = new Foo();

当您向函数调用添加新对象时,将创建一个新对象(仅var bar=new object()),并且函数中的this指向您刚刚创建的新对象,而不是调用函数的对象。所以bar现在是一个具有财产A和B的对象。任何函数都可以是构造函数;这并不总是有意义的。