题 JavaScript中的'new'关键字是什么?


new JavaScript中的关键字在第一次遇到时会非常混乱,因为人们倾向于认为JavaScript不是面向对象的编程语言。

  • 它是什么?
  • 它解决了什么问题?
  • 什么时候适当,什么时候不适合?

1559
2017-10-29 21:32


起源


另外,相关主题 - stackoverflow.com/questions/383402/... - Chetan Sastry


答案:


它做了5件事:

  1. 它创建了一个新对象。这个对象的类型很简单 目的
  2. 它设置这个新对象的内部,无法访问, [[原型]] (即 __proto__)属性是构造函数的外部,可访问, 原型 对象(每个函数对象自动拥有一个 原型 属性)。
  3. 它使 this 变量指向新创建的对象。
  4. 它执行构造函数,每次使用新创建的对象 this 提到了。
  5. 它返回新创建的对象,除非构造函数返回非null 对象参考。在这种情况下,将返回该对象引用。

注意: 构造函数 是指之后的功能 new 关键字,如

new ConstructorFunction(arg1, arg2)

完成此操作后,如果请求新对象的未定义属性,脚本将检查对象的属性 [[原型]] 相反的财产对象。这就是你如何在JavaScript中获得类似于传统类继承的东西。

关于这一点最困难的部分是第2点。每个对象(包括函数)都有这个内部属性 [[原型]]。它可以 只要 在对象创建时设置,或者 ,与 的Object.create,或基于文字(函数默认为Function.prototype,数字为Number.prototype等)。它只能用 Object.getPrototypeOf(someObject)。有 没有 设置或读取此值的其他方法。

功能,除了隐藏 [[原型]] 财产,也有一个叫做的财产 原型,您可以访问和修改,为您创建的对象提供继承的属性和方法。


这是一个例子:

ObjMaker = function() {this.a = 'first';};
// ObjMaker is just a function, there's nothing special about it that makes 
// it a constructor.

ObjMaker.prototype.b = 'second';
// like all functions, ObjMaker has an accessible prototype property that 
// we can alter. I just added a property called 'b' to it. Like 
// all objects, ObjMaker also has an inaccessible [[prototype]] property
// that we can't do anything with

obj1 = new ObjMaker();
// 3 things just happened.
// A new, empty object was created called obj1.  At first obj1 was the same
// as {}. The [[prototype]] property of obj1 was then set to the current
// object value of the ObjMaker.prototype (if ObjMaker.prototype is later
// assigned a new object value, obj1's [[prototype]] will not change, but you
// can alter the properties of ObjMaker.prototype to add to both the
// prototype and [[prototype]]). The ObjMaker function was executed, with
// obj1 in place of this... so obj1.a was set to 'first'.

obj1.a;
// returns 'first'
obj1.b;
// obj1 doesn't have a property called 'b', so JavaScript checks 
// its [[prototype]]. Its [[prototype]] is the same as ObjMaker.prototype
// ObjMaker.prototype has a property called 'b' with value 'second'
// returns 'second'

它类似于类继承,因为现在,您使用的任何对象 new ObjMaker() 也似乎继承了'b'属性。

如果你想要一个像子类的东西,那么你这样做:

SubObjMaker = function () {};
SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated!
// Because we used 'new', the [[prototype]] property of SubObjMaker.prototype
// is now set to the object value of ObjMaker.prototype.
// The modern way to do this is with Object.create(), which was added in ECMAScript 5:
// SubObjMaker.prototype = Object.create(ObjMaker.prototype);

SubObjMaker.prototype.c = 'third';  
obj2 = new SubObjMaker();
// [[prototype]] property of obj2 is now set to SubObjMaker.prototype
// Remember that the [[prototype]] property of SubObjMaker.prototype
// is ObjMaker.prototype. So now obj2 has a prototype chain!
// obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype

obj2.c;
// returns 'third', from SubObjMaker.prototype

obj2.b;
// returns 'second', from ObjMaker.prototype

obj2.a;
// returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype 
// was created with the ObjMaker function, which assigned a for us

在最终发现之前,我读了很多关于这个主题的垃圾 这一页,用漂亮的图表很好地解释了这一点。


1961
2017-10-29 22:22



只是想补充一点:实际上有一种通过__proto__访问内部[[prototype]]的方法。然而,这是非标准的,并且仅由相对较新的浏览器(而不是所有浏览器)支持。有一种标准化的方法,即Object.getPrototypeOf(obj),但它是Ecmascript3.1,并且本身仅支持新的浏览器 - 再次。通常建议不要使用该属性,因为内部的内容变得非常复杂。 - Blub
问题:如果发生了什么不同 ObjMaker 被定义为一个返回值的函数? - Jim Blackler
@LonelyPixel new 存在 所以你没必要 编写工厂方法来构造/复制函数/对象。这意味着,“复制它,使它就像它的父级'类';高效且正确地执行;并存储只有我自己可以访问的继承信息,JS,内部”。为此,它会修改其他无法访问的内部 prototype 不透明地封装继承成员的新对象,模仿经典的OO继承链(不是运行时可修改的)。你可以不用来模拟这个 new,但继承将是运行时可修改的。好?坏?由你决定。 - Arcane Engineer
要添加的小点:对构造函数的调用,当以new关键字开头时,会自动返回创建的对象;没有必要在构造函数中显式返回它。 - charlie roberts
有一个说明 Notice that this pattern is deprecated!。设置类原型的正确最新模式是什么? - Tom Pažourek


假设你有这个功能:

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

如果你将其称为独立函数,如下所示:

Foo();

执行此函数将添加两个属性 window 对象(A 和 B)。它将它添加到 window 因为 window 是这样执行它时调用函数的对象,和 this 在函数中是调用函数的对象。至少在Javascript中。

现在,这样称呼它 new

var bar = new Foo();

添加时会发生什么 new 函数调用是创建一个新对象(只是 var bar = new Object())那个 this 在功能内指向新的 Object 你刚刚创建的,而不是调用函数的对象。所以 bar 现在是具有属性的对象 A 和 B。任何函数都可以是构造函数,它并不总是有意义的。


359
2018-06-20 23:46



取决于执行上下文。就我而言(Qt脚本),它只是一个全局对象。 - Maxym
这会导致更多的内存使用吗? - Jürgen Paul
因为window是调用函数的对象  - 必须是:因为窗口是对象 包含 功能。 - Dávid Horváth
@Taurus在Web浏览器中,非方法函数将是一种方法 window 含蓄。即使在关闭,即使匿名。但是,在示例中,它是一个简单的窗口方法调用: Foo(); => [default context].Foo(); => window.Foo();。在这个表达中 window 是个 上下文 (不仅是 呼叫者,这没关系)。 - Dávid Horváth
@Taurus基本上是的。然而,在ECMA 6和7中,事情更复杂(参见lambda,类等)。 - Dávid Horváth


除了丹尼尔霍华德的回答,这是什么 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);

146
2018-05-27 09:23



我发现javascript比英文更容易理解:v - damphat
很好的答案。我有一个小问题:怎么可能 func.prototype 成为 null?你能详细说明一下吗? - Tom Pažourek
@tomp你可以简单地写一下覆盖原型属性 A.prototype = null; 在这种情况下 new A() 将导致对象,那是内部原型指向的 Object 目的: jsfiddle.net/Mk42Z - basilikum
类型检查可能是错误的,因为主机对象可能产生与“对象”或“功能”不同的东西。为了测试某物是否是物体,我更喜欢 Object(ret) === ret。 - Oriol
@Oriol感谢您的评论。你说的是真的,任何实际的测试都应该以更健壮的方式完成。但是,我认为对于这个概念性的答案, typeof 测试只是让你更容易理解幕后发生的事情。 - basilikum


让初学者更好地理解它

在浏览器控制台中尝试以下代码。

function Foo() { 
    return this; 
}

var a = Foo();       //returns window object
var b = new Foo();   //returns empty object of foo

a instanceof Window;  // true
a instanceof Foo;     // false

b instanceof Window;  // false
b instanceof Foo;     // true

现在你可以阅读社区wiki的答案:)


85
2017-10-29 21:34



好答案。另外 - 退出 return this; 产生相同的输出。 - Nelu
很高兴指出为什么会这样。 - Florian Leitgeb


所以它可能不适合创作   对象的实例

它完全用于此。你定义一个函数构造函数,如下所示:

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

var john = new Person('John');

然而,ECMAScript的额外好处是你可以扩展 .prototype 财产,所以我们可以做...

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

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


33
2017-10-29 21:36



函数构造函数就像类一样使用,没有 class 关键字,但你几乎可以做同样的事情。 - meder omuraliev
kindof是一个类关键字 - 类保留供将来使用 - Greg
顺便说一句,这就是你使用.className而不是.class来设置CSS类的原因 - Greg
按惯例,它应该是大写的人。 - eomeroff


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


26
2017-10-29 21:37



我想说JavaScript似乎比所有那些基于类的语言更加面向对象。在JavaScript中,您立即编写的所有内容都将成为一个对象,但在基于类的语言中,您首先要编写声明,然后才能创建类的特定实例(对象)。 JavaScript原型似乎模糊地提醒所有基于类的语言的VTABLE东西。 - JustAMartin


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

对象不需要类 - Javascript是一个 基于原型 语言。


13
2018-05-16 07:21





有时代码比单词更容易:

var func1 = function (x) { this.x = x; }                    // used with 'new' only
var func2 = function (x) { var z={}; z.x = x; return z; }   // used both ways
func1.prototype.y = 11;
func2.prototype.y = 12;

A1 = new func1(1);      // has A1.x  AND  A1.y
A2 =     func1(1);      // undefined ('this' refers to 'window')
B1 = new func2(2);      // has B1.x  ONLY
B2 =     func2(2);      // has B2.x  ONLY

对我来说,只要我没有原型,我就使用func2的风格,因为它让我在函数内外有了更多的灵活性。


3
2017-10-29 21:38



B1 = new func2(2);  < - 为什么这不会 B1.y  ? - sunny_dev
@sunny_dev我不是JS专家,但可能是因为 FUNC2 直接返回一个值(z对象),而不是使用内部值(this)工作/返回 - Eagle


new keyword用于创建新对象实例。是的,javascript是一种动态编程语言,它支持面向对象的编程范例。关于对象命名的约定是,对于应该由new关键字实例化的对象,总是使用大写字母。

obj = new Element();

2
2017-10-05 02:28





已经有一些非常好的答案,但我发布了一个新的答案,以强调我对案例的观察 III 下面讲一下当你在一个函数中有一个显式的return语句时会发生什么 new升起看看下面的案例:

案例一

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; 声明在函数的最后。这里窗口是调用对象(contextual this)获得新的 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解释器看到了 new keyword创建一个新对象,它充当调用对象(contextual this)由匿名函数指出 Foo。在这种情况下 A 和 B成为新创建的对象的属性(代替窗口对象)。由于您没有任何显式的return语句,因此JavaScript解释器强制插入return语句以返回由于使用而创建的新对象 new 关键词。

案例III

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解释器 new keyword创建一个新对象,它充当调用对象(contextual this)由匿名函数指出 Foo。再次, A 和 B 成为新创建的对象的属性。但这次你有一个明确的return语句,所以JavaScript解释器会  做任何自己的事情。

要注意的事情 III 是由于创建的对象 new 关键字从你的雷达中丢失了。 bar 实际上是指向一个完全不同的对象,这个对象不是JavaScript解释器创建的对象 new 关键词。


2
2018-05-13 23:05





每个JavaScript的JavaScript可能因平台而异,因为它始终是原始规范EcmaScript的实现。

在任何情况下,独立于实现的所有遵循EcmaScript规范的JavaScript实现都将为您提供面向对象的语言。根据ES标准:

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

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

在EcmaScript中,我们不使用类,因为您可以从规范中读取:

ECMAScript不使用诸如C ++,Smalltalk或Java中的类。相反,可以以各种方式创建对象,包括via   一个文字符号或通过构造函数创建对象然后执行代码,通过分配初始化来初始化它们的全部或部分   值的属性。每个构造函数都是一个函数   财产命名 -       原型‖用于实现基于原型的继承和共享属性。对象是由。创建的
  在新表达式中使用构造函数;例如,新的   Date(2009,11)创建一个新的Date对象。调用构造函数   不使用new会产生依赖于构造函数的后果。   例如,Date()生成一个字符串表示   当前日期和时间而不是对象。


1
2017-11-27 13:13