JavaScript 中的“new”关键字是什么?

IT技术 javascript new-operator
2021-01-03 16:32:08

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

  • 它是什么?
  • 它解决了哪些问题?
  • 什么时候合适,什么时候不合适?
6个回答

它做了 5 件事:

  1. 它创建一个新对象。这个对象的类型只是object
  2. 它将这个新对象的内部、不可访问的[[prototype]](即__proto__)属性设置为构造函数的外部、可访问的原型对象(每个函数对象自动具有一个原型属性)。
  3. 它使this变量指向新创建的对象。
  4. 它执行构造函数,每当this提到时使用新创建的对象
  5. 它返回新创建的对象,除非构造函数返回一个非null对象引用。在这种情况下,将返回该对象引用。

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

new ConstructorFunction(arg1, arg2)

完成此操作后,如果请求新对象的未定义属性,脚本将改为检查对象的[[prototype]]对象以获取该属性。这就是您如何获得类似于 JavaScript 中传统类继承的东西。

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

函数,除了隐藏的[[prototype]]属性,还有一个叫做prototype的属性,你可以访问和修改这个属性,为你创建的对象提供继承的属性和方法。


下面是一个例子:

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

在最终找到这个页面之前,我阅读了大量关于这个主题的垃圾,其中用漂亮的图表很好地解释了这一点。

@LonelyPixelnew存在,因此您不必编写工厂方法来构造/复制函数/对象。它的意思是,“复制它,使它像它的父类一样;高效正确地这样做;并存储只有我,JS,内部才能访问的继承信息”。为此,它修改了prototype新对象原本无法访问的内部,以不透明地封装继承的成员,模仿经典的 OO 继承链(在运行时不可修改)。您可以在没有 的情况下模拟这一点new,但继承将是运行时可修改的。好的?坏的?由你决定。
2021-02-11 16:32:08
有一张纸条说Notice that this pattern is deprecated!设置类原型的正确最新模式是什么?
2021-02-19 16:32:08
补充一点:对构造函数的调用,当前面有 new 关键字时,自动返回创建的对象;无需从构造函数中显式返回它。
2021-02-26 16:32:08
只是想补充一点:实际上有一种方法可以通过 __proto__ 访问内部 [[prototype]]。然而,这是非标准的,并且仅支持相对较新的浏览器(并非所有浏览器)。有一种标准化的方法即将出现,即 Object.getPrototypeOf(obj),但它是 Ecmascript3.1,并且本身仅在新浏览器上受支持 - 再次。虽然通常建议不要使用该属性,但里面的东西很快就会变得复杂。
2021-02-28 16:32:08
问题:如果ObjMaker定义为一个返回值的函数,会有什么不同
2021-03-04 16:32:08

假设你有这个功能:

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

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

Foo();

执行此函数将向window对象(AB添加两个属性它将它添加到 thewindow因为window当你这样执行它时调用该函数的对象,并且this在一个函数中是调用该函数的对象。至少在 Javascript 中。

现在,这样称呼它new

var bar = new Foo();

当您添加new到函数调用时会发生什么是创建了一个新对象(只是var bar = new Object()),并且this函数内的 指向Object您刚刚创建的新对象,而不是调用该函数的对象。所以,bar现在与属性的对象AB任何函数都可以是构造函数,只是并不总是有意义。

@Taurus 在 Web 浏览器中,非方法函数将是一种window隐式方法即使在关闭,即使匿名。但是,在示例中,它是对 window: Foo();=> [default context].Foo();=>的简单方法调用window.Foo();在这个表达式中window上下文(不仅是caller,这无关紧要)。
2021-02-12 16:32:08
因为 window 是调用该函数的对象- 必须是:因为 window 是包含该函数的对象
2021-02-15 16:32:08
取决于执行上下文。就我而言(Qt 脚本),它只是一个全局对象。
2021-02-19 16:32:08
这会导致更多的内存使用吗?
2021-02-20 16:32:08
@Taurus 基本上是的。然而,在 ECMA 6 和 7 中,事情更加复杂(参见 lambda、类等)。
2021-03-04 16:32:08

除了丹尼尔霍华德的回答,这里是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);
@tomp 您可以通过简单地编写来覆盖原型属性A.prototype = null;在这种情况下new A()将导致对象,即内部原型指向该Object对象:jsfiddle.net/Mk42Z
2021-02-18 16:32:08
typeof 检查可能是错误的,因为宿主对象可能会产生与“对象”或“函数”不同的东西。为了测试某物是否是一个对象,我更喜欢Object(ret) === ret.
2021-02-22 16:32:08
优秀的答案。我有一个小问题:怎么可能func.prototypenull你能详细说明一下吗?
2021-02-23 16:32:08
我发现javascript比英语更容易理解:v
2021-02-28 16:32:08
@Oriol 感谢您的评论。您所说的是真的,任何实际测试都应该以更可靠的方式进行。但是,我认为对于这个概念性的答案,typeof测试只是让人们更容易理解幕后发生的事情。
2021-03-01 16:32:08

为了初学者更好地理解它

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

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

现在您可以阅读社区维基答案:)

好答案。此外 - 省略会return this;产生相同的输出。
2021-03-02 16:32:08

所以它可能不是用于创建对象的实例

它正是为此而使用的。您可以像这样定义一个函数构造函数:

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

var john = new Person('John');

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

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

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

顺便说一句,这就是为什么您使用 .className 而不是 .class 来设置 CSS 类的原因
2021-02-13 16:32:08
函数构造函数就像类一样使用,没有class关键字,但您几乎可以做同样的事情。
2021-02-17 16:32:08
kindof 有一个 class 关键字 - class 保留供将来使用
2021-03-08 16:32:08