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

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


当前回答

假设您有此函数:

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的对象。任何函数都可以是构造函数;这并不总是有意义的。

其他回答

JavaScript不是面向对象编程(OOP)语言。因此,JavaScript中的查找过程使用委托过程,也称为原型委托或原型继承。

如果您试图从一个没有的对象中获取属性的值,JavaScript引擎会查找该对象的原型(以及它的原型,一步一步)。它是原型链,直到链结束为null,即Object.prototype==null(标准对象原型)。

此时,如果未定义属性或方法,则返回undefined。

重要的函数是一类对象。

函数=函数+对象组合框

FunctionName.prototype={共享子对象}

{
  // other properties
  prototype: {
   // shared space which automatically gets [[prototype]] linkage
      when "new" keyword is used on creating instance of "Constructor
      Function"
  }
}

因此,使用新关键字。,

手动创建对象,例如newObj。使用JavaScript规范[[prototype]](即proto)中的proto(AKA:dunderproto)创建隐藏键引用并将财产分配给newObj返回newObj对象。

所有操作都是手动完成的。

function CreateObj(value1, value2) {
  const newObj = {};
  newObj.property1 = value1;
  newObj.property2 = value2;
  return newObj;
}
var obj = CreateObj(10,20);

obj.__proto__ === Object.prototype;              // true
Object.getPrototypeOf(obj) === Object.prototype // true

JavaScript关键字new有助于自动化此过程:

创建了一个新的对象文本,其标识如下:{}引用并为此分配财产隐藏键创建[[prototype]](即proto)到Function.prototype共享空间。此对象{}的隐式返回

function CreateObj(value1, value2) {
  this.property1 = value1;
  this.property2 = value2;
}

var obj = new CreateObj(10,20);
obj.__proto__ === CreateObj.prototype             // true
Object.getPrototypeOf(obj) == CreateObj.prototype // true

调用不带new关键字的构造函数:

=>此:窗口

function CreateObj(value1, value2) {
  var isWindowObj = this === window;
  console.log("Is Pointing to Window Object", isWindowObj);
  this.property1 = value1;
  this.property2 = value2;
}
var obj = new CreateObj(10,20); // Is Pointing to Window Object false
var obj = CreateObj(10,20); // Is Pointing to Window Object true
window.property1; // 10
window.property2; // 20

new关键字更改运行函数的上下文,并返回指向该上下文的指针。

如果不使用new关键字,则运行函数Vehicle()的上下文与调用Vehicle函数的上下文相同。this关键字将引用相同的上下文。当您使用new Vehicle()时,将创建一个新上下文,因此函数中的关键字this将引用新上下文。您得到的回报是新创建的上下文。

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

案例一:

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

所以它可能不是为了创造对象的实例

它正是为了这个。您可以这样定义函数构造函数:

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

var john = new Person('John');

然而,ECMAScript的额外好处是您可以使用.prototype属性进行扩展,因此我们可以执行以下操作。。。

Person.prototype.getName = function() { return this.name; }

从该构造函数创建的所有对象现在都将有一个getName,因为它们可以访问原型链。

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