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

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

  • 这是什么?
  • 它解决了什么问题?
  • 什么时候合适,什么时候不合适?
342650 次浏览

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

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

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

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

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

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

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

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

对象不需要类。JavaScript是一种基于原型语言。

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

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

实例继承自构造函数的prototype。所以给出上面的例子…

foo.bar; // 'bar'

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

obj = new Element();

假设你有这个函数:

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

如果您像这样将其称为独立函数:

Foo();

执行此函数将向window对象添加两个属性(AB)。它将其添加到window中,因为window是在您这样执行时调用函数的对象,而函数中的this是调用函数的对象。至少在JavaScript中。

现在,用new这样调用它:

var bar = new Foo();

当你在函数调用中添加new时,会创建一个新对象(只有var bar = new Object()),函数中的this指向你刚刚创建的新Object,而不是调用该函数的对象。所以bar现在是一个具有属性AB的对象。任何函数都可以是构造函数;只是它并不总是有意义。

它做了五件事:

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

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

new ConstructorFunction(arg1, arg2)

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

最困难的部分是第2点。每个对象(包括函数)都有一个名为[[原型]]的内部属性。它可以在对象创建时设置只有,可以使用新的Object.create,也可以基于文字(函数默认为Function.prototype、数字Number.prototype等)。它只能用Object.get原型(某个对象)读取。还有其他方法来设置或读取此值。

函数除了隐藏的[[原型]]属性外,还有一个名为原型的属性,您可以访问和修改它,为您创建的对象提供继承的属性和方法。


下面是一个例子:

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

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

除了丹尼尔·霍华德的回答之外,以下是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);

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

无论如何,独立于实现,所有遵循ECMAScript规范的JavaScript实现都将为您提供面向对象的语言。根据ES标准:

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

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

在ECMAScript中,我们不使用类,你可以从规范中读到:

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

有时代码比文字更容易:

var func1 = function (x) { this.x = x; }                   // Used with 'new' onlyvar func2 = function (x) { var z={}; z.x = x; return z; }  // Used both waysfunc1.prototype.y = 11;func2.prototype.y = 12;
A1 = new func1(1);  // Has A1.x  AND  A1.yA2 =     func1(1);  // Undefined ('this' refers to 'window')B1 = new func2(2);  // Has B1.x  ONLYB2 =     func2(2);  // Has B2.x  ONLY

对我来说,只要我不做原型,我就会使用函数2的风格,因为它在函数内外给了我更多的灵活性。

让初学者更好地理解

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

function Foo() {return this;}
var a = Foo();       // Returns the 'window' objectvar b = new Foo();   // Returns an empty object of foo
a instanceof Window;  // Truea instanceof Foo;     // False
b instanceof Window;  // Falseb instanceof Foo;     // True

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

请看看下面我对案件三的观察。它是关于当你在一个你正在新的 ing的函数中有一个明确的返回语句时会发生什么。看看下面的案例:

案例一

var Foo = function(){this.A = 1;this.B = 2;};console.log(Foo()); //prints undefinedconsole.log(window.A); //prints 1

上面是一个调用变量Foo指向的匿名函数的普通案例。当你调用这个函数时,它返回未定义。由于没有任何显式的返回语句,JavaScript解释器在函数末尾强行插入了一个return undefined;语句。所以上面的代码示例相当于:

var Foo = function(){this.A = 1;this.B = 2;return undefined;};console.log(Foo()); //prints undefinedconsole.log(window.A); //prints 1

当调用Foo函数时,窗口是默认调用对象(上下文这个),它获取新的一个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 objectconsole.log(bar.A); //prints 1

在这里,JavaScript解释器看到新的关键字,创建了一个新对象,该对象充当Foo指向的匿名函数的调用对象(上下文这个)。在这种情况下,一个B成为新创建对象的属性(代替窗口对象)。由于您没有任何明确的返回语句,JavaScript解释器强制插入一个返回语句来返回由于使用新的关键字而创建的新对象。

案件三

var Foo = function(){this.A = 1;this.B = 2;return {C:20,D:30};};var bar = new Foo();console.log(bar.C);//prints 20console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.

这里,JavaScript解释器再次看到新的关键字,创建了一个新对象,该对象充当Foo指向的匿名函数的调用对象(上下文这个)。同样,一个B成为新创建对象的属性。但是这次你有一个明确的返回语句,所以JavaScript解释器将没有自己做任何事情。

案件三中需要注意的是,由于新的关键字而创建的对象从您的雷达中消失了。酒吧实际上指向一个完全不同的对象,而不是JavaScript解释器由于新的关键字而创建的对象。

引用大卫·弗拉纳根的话JavaScript:权威指南(第6版),第4章,第62页:

当评估对象创建表达式时,JavaScript首先创建一个新的空对象,就像对象创建的对象一样初始化器{}。接下来,它使用指定的参数,将新对象作为this的值传递关键字。然后该函数可以使用它来初始化属性新创建的对象。为用作构造函数而编写的函数不返回值,对象创建表达式的值是新创建和初始化的对象。如果构造函数返回一个对象值,该值成为对象的值创建表达式并丢弃新创建的对象。

补充资料:

上述案例的代码片段中使用的函数在JavaScript世界中具有特殊名称,如下所示:

案例#姓名
案例一构造函数
案件二构造函数
案件三工厂函数

您可以在这个线程中阅读构造函数和工厂函数之间的区别。

案件III中的代码气味-工厂函数应该没有新的关键字一起使用,我在上面的代码片段中显示了这一点。我故意这样做只是为了解释这个概念。

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

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

总结:

new关键字在javascript中用于从构造函数创建对象。new关键字必须放在构造函数调用之前,并将执行以下操作:

  1. 创建一个新对象
  2. 将此对象的原型设置为构造函数的原型属性
  3. this关键字绑定到新创建的对象并执行构造函数
  4. 返回新创建的对象

示例:

function Dog (age) {this.age = age;}
const doggie = new Dog(12);
console.log(doggie);console.log(Object.getPrototypeOf(doggie) === Dog.prototype) // true

究竟发生了什么:

  1. const doggie表示:我们需要内存来声明变量。
  2. 辅助运算符=表示:我们将使用=之后的表达式初始化这个变量
  3. 表达式是new Dog(12)。JS引擎看到new关键字,创建一个新对象并将原型设置为Dog.prototype
  4. 构造函数在执行时将this值设置为新对象。在这一步中,年龄被分配给新创建的doggie对象。
  5. 新创建的对象被返回并分配给变量doggie。
 " Every object (including functions) has this internal property called [[prototype]]"

每个函数都有一个原型。类型对象,自动设置为使用该函数创建的对象的原型。

您可以轻松查看:

const a = { name: "something" };console.log(a.prototype); // undefined because it is not directly accessible
const b = function () {console.log("somethign");};
console.log(b.prototype); // returns b {}

但是每个函数和对象都有__proto__属性,它指向该对象或函数的原型。__proto__prototype是两个不同的术语。我想我们可以这样评论:“每个对象都通过原型链接到原型”但是__proto__在javascript中不存在。这个属性是由浏览器添加的,只是为了帮助调试。

console.log(a.__proto__); // returns {}console.log(b.__proto__); // returns [Function]

你们可以很容易地在终端上检查这个。那么什么是构造函数。

function CreateObject(name,age){this.name=name;this.age =age}

首先要注意的5件事:

1-当使用new调用构造函数时,调用函数的内部[[Construct]]方法来创建一个新的实例对象并分配内存。

2-我们不使用return关键字。new将处理它。

3-函数名称大写,因此当开发人员看到您的代码时,他们可以理解他们必须使用new关键字。

4-我们不使用箭头函数。因为this参数的值是在创建箭头函数的那一刻被拾取的,这是“窗口”。箭头函数是词法范围的,而不是动态的。这里的词法意味着本地。箭头函数携带其本地“this”值。

5-与常规函数不同,箭头函数永远不能使用new关键字调用,因为它们没有[[Construct]]方法。箭头函数也不存在原型属性。

const me=new CreateObject("yilmaz","21")

new调用该函数,然后创建一个空对象{},然后添加值为“name”的“name”键和值为“age”的“age”键。

当我们调用一个函数时,一个新的执行上下文被创建为“this”和“参数”,这就是为什么“new”可以访问这些参数。

默认情况下,构造函数中的this将指向“窗口”对象,但new会更改它。“this”指向创建的空对象{},然后将属性添加到新创建的对象中。如果您有任何定义没有“this”属性的变量,则不会添加到对象中。

function CreateObject(name,age){this.name=name;this.age =age;const myJob="developer"}

由于没有任何内容引用新创建的对象,因此myJobs属性不会添加到对象中。

   const me= {name:"yilmaz",age:21} // there is no myJob key

一开始我说每个函数都有“原型”属性,包括构造函数。我们可以向构造函数的原型添加方法,因此从该函数创建的每个对象都可以访问它。

 CreateObject.prototype.myActions=function(){ //define something}

现在“me”对象可以使用“myActions”方法。

JavaScript具有内置的构造函数:Function、Boolean、Number、String…

如果我创建

const a = new Number(5);console.log(a);  // [Number: 5]console.log(typeof a); // object

使用new创建的任何内容都具有对象类型。现在“a”可以访问存储在Number.prototype中的所有方法。如果我定义

const b = 5;console.log(a === b);//false

即使b是原始类型,当它被创建时,javascript会自动用Number()包装它,所以b可以访问Number.prototype.中的所有方法

当您想创建具有相同属性和方法的多个类似对象时,构造函数很有用。这样您就不会分配额外的内存,因此您的代码将更有效地运行。

JavaScript不是面向对象程序设计(OOP)语言,因此向上看进程在JavaScript中使用`授权程序'也称为原型委托或原型继承。

如果你试图从一个没有属性的对象中获取属性的值,那么JavaScript引擎查看对象的原型(及其原型,一次上一步)它是原型链,直到链结束到<强>空,即Object.prototype==null(标准对象原型)。此时,如果未定义属性或方法,则返回未定义

小恶魔!!函数是函数是第一类对象

函数=函数+对象组合

FunctionName.prototype={共享子对象}

{// other propertiesprototype: {// shared space which automatically gets [[prototype]] linkagewhen "new" keyword is used on creating instance of "ConstructorFunction"}}

因此,使用new关键字,一些手动完成的任务,例如

  1. 手动创建对象,例如newObj。
  2. 在JS规范[[原型]](即原型)中使用原型(又名:dunder proto)创建隐藏债券
  3. 引用和分配属性到newObj
  4. 返回newObj对象。

一切都是手动完成的。

function CreateObj(value1, value2) {const newObj = {};newObj.property1 = value1;newObj.property2 = value2;return newObj;}var obj = CreateObj(10,20);
obj.__proto__ === Object.prototype;              // trueObject.getPrototypeOf(obj) === Object.prototype // true

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

  1. 新对象文字由this:{}创建
  2. 引用和分配属性到this
  3. 隐藏键创建[[原型]](即原型以Function.prototype共享空间。
  4. this对象{}的隐式返回
function CreateObj(value1, value2) {this.property1 = value1;this.property2 = value2;}
var obj = new CreateObj(10,20);obj.__proto__ === CreateObj.prototype             // trueObject.getPrototypeOf(obj) == CreateObj.prototype // true

在没有new关键字的情况下调用构造函数:

=>this: Window

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 falsevar obj = CreateObj(10,20); // Is Pointing to Window Object truewindow.property1; // 10window.property2; // 20