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

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


当前回答

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

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

其他回答

它有三个阶段:

1.创建:它创建一个新对象,并将此对象的[[prototype]]属性设置为构造函数的prototype属性。

2.执行:它指向新创建的对象并执行构造函数。

3.返回:在正常情况下,它将返回新创建的对象。但是,如果显式返回非空对象或函数,则会返回此值。需要提及的是,如果返回非空值,但它不是对象(例如Symbol value、undefined、NaN),则忽略该值并返回新创建的对象。

function myNew(constructor, ...args) {
  const obj = {}
  Object.setPrototypeOf(obj, constructor.prototype)
  
  const returnedVal = constructor.apply(obj, args)
  
  if (
    typeof returnedVal === 'function' 
    || (typeof returnedVal === 'object' && returnedVal !== null)) {
      return returnedVal
  }
  return obj
}

有关myNew的更多信息和测试,请阅读我的博客:https://medium.com/@magenta2127/how-does-the-new-operator-work-f7eaac692026

好吧,JavaScript本身可能因平台而异,因为它始终是原始规范ECMAScript(ES)的实现。

在任何情况下,与实现无关,所有遵循ECMAScript规范的JavaScript实现都将为您提供一种面向对象的语言。根据ES标准:

ECMAScript是一种面向对象的编程语言,用于执行计算和操纵计算对象在主机环境中。

现在我们已经同意JavaScript是ECMAScript的一种实现,因此它是一种面向对象的语言。在任何面向对象的语言中,新操作的定义都表示,这样的关键字用于从特定类型的类(包括匿名类型,例如C#)创建对象实例。

在ECMAScript中,我们不使用类,您可以从规范中了解到:

ECMAScript不使用C++、Smalltalk或Java中的类。相反,可以通过各种方式创建对象,包括通过文字符号或通过构造函数创建对象,然后执行代码,通过指定初始值初始化所有或部分对象其财产的值。每个构造函数都是一个函数属性名为―prototype‖,用于实现基于原型的继承和共享财产。对象由创建在新表达式中使用构造函数;例如,新Date(2009,11)创建一个新的Date对象。调用构造函数不使用new的结果取决于构造函数。例如,Date()生成当前日期和时间,而不是对象。

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

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

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