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

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

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

它做了5件事情:

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

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

 new ConstructorFunction(arg1, arg2) 

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

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

函数除了隐藏的[[prototype]]属性外,还有一个名为prototype的属性,正因为如此,您可以访问和修改,为您创build的对象提供inheritance的属性和方法。


这里是一个例子:

 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' 

这就像类inheritance,因为现在,使用new ObjMaker()创build的任何对象也将inheritance“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 

在阅读这个主题之前,我读了一大堆垃圾,最后find了这个页面 。

假设你有这个function:

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

如果你把它作为一个独立的函数来调用,像这样:

 Foo(); 

执行此函数将为window对象( AB )添加两个属性。 它将它添加到window因为window是当你像这样执行时调用函数的对象,而函数中的这个对象是调用该函数的对象。 至less在Javascript中。

现在,用new名称来称呼它:

 var bar = new Foo(); 

当你给函数调用添加new时候,会发生什么呢?一个新的对象被创build(只是var bar = new Object() ),而且this函数指向刚创build的新Object ,而不是被调用的对象function。 所以bar现在是属性AB的对象。 任何函数都可以是一个构造函数,它并不总是有意义的。

除了霍华德的回答,以下是new (或者至less似乎是这样做的):

 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); 

让初学者更好地理解它

在控制台中试用下面的代码。

 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 

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

所以它可能不是创build对象的实例

这完全是为了这个。 你可以这样定义一个函数构造函数:

 function Person(name) { this.name = name; } var john = new Person('John'); 

然而,ECMAScript带来的额外好处是可以使用.prototype属性进行扩展,所以我们可以做类似…

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

由此构造函数创build的所有对象现在都有一个getName因为它们有权访问原型链。

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

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

类不是必需的对象 – JavaScript是基于原型的语言。

有时代码比文字更容易:

 var func1 = function (x) { this.x = x; } // used with 'new' only var func2 = function (x) { var z={}; zx = 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的风格,因为它给了我更多的灵活性内外function。

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

 obj = new Element(); 

那么,每个平台上的JavaScript可能会有很大差异,因为它始终是原始规范EcmaScript的实现。

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

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

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

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

ECMAScript不使用C ++,Smalltalk或Java中的类。 相反,可以通过各种方式创build对象,包括通过文字符号或通过创build对象的构造函数,然后执行代码,通过为其属性分配初始值来初始化全部或部分对象。 每个构造函数都有一个名为“prototype”的属性,用于实现基于原型的inheritance和共享属性。 对象是由…创build的
在新的expression式中使用构造函数; 例如,新date(2009,11)创build一个新的date对象。 在不使用new的情况下调用构造函数会产生依赖于构造函数的后果。 例如,Date()产生当前date和时间的string表示,而不是一个对象。

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

 var Foo = function() {}; Foo.prototype.bar = 'bar'; var foo = new Foo(); foo instanceof Foo; // true 

实例从构造函数的prototypeinheritance。 所以给了上面的例子…

 foo.bar; // 'bar' 

已经有一些非常好的答案,但是我发布了一个新的强调我对下面的情况三的观察,当你在一个new的函数中有一个明确的return语句时会发生什么。 看看下面的例子:

案例一

 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; 声明在函数的最后。 这里的窗口是获取新的AB属性的调用对象(contextual this )。

案例二

 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关键字创build一个新的对象,作为Foo指向的匿名函数的调用对象(contextual this )。 在这种情况下, AB成为新创build的对象(代替窗口对象)的属性。 由于您没有任何显式的return语句,所以JavaScript解释器强制插入return语句以返回由于使用new关键字而创build的新对象。

案例三

 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关键字,创build一个新对象,作为Foo指向的匿名函数的调用对象(contextual this )。 AB再次成为新创build的对象的属性。 但是这次你有一个明确的return语句,所以JavaScript解释器不会做任何事情。

情况三中要注意的是,由于new关键字而产生的对象从你的雷达中丢失了。 bar实际上是指向一个完全不同的对象,而不是JavaScript解释器由于new关键字而创build的对象。