1869

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

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

16 回答 16

2237

它做了 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]]属性外,还有一个称为原型的属性,您可以访问和修改它,为您创建的对象提供继承的属性和方法。


这是一个例子:

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

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

于 2010-09-07T12:30:58.123 回答
436

假设你有这个功能:

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是具有属性A和的对象B。任何函数都可以是构造函数,但这并不总是有意义的。

于 2009-10-29T22:22:15.150 回答
174

除了丹尼尔霍华德的回答,这里是什么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);
于 2013-06-20T23:46:55.443 回答
130

让初学者更好地理解它

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

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 答案:)

于 2015-05-27T09:23:53.560 回答
39

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

它正是为此而使用的。您定义一个函数构造函数,如下所示:

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

var john = new Person('John');

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

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

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

于 2009-10-29T21:34:36.350 回答
29

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

于 2009-10-29T21:36:51.950 回答
21

概括:

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设置为新对象的值执行。在这一步中,年龄被分配给新创建的小狗对象。
  5. 新创建的对象被返回并分配给变量 doggie。
于 2018-08-31T08:22:51.433 回答
17

已经有一些非常棒的答案,但我发布了一个新答案,以强调我对下面案例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;在函数末尾插入一条语句。这里 window 是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关键字创建了一个新对象,该对象充当.this指向的匿名函数的调用对象(上下文)Foo。在这种情况下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.

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

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

引用 JavaScripit 中的 David Flanagan:权威指南(第 6 版),Ch。4,第 62 页:

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

---附加信息---

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

案例 I 和 II - 构造函数

案例 III - 工厂功能。工厂函数应该与new我用来解释当前线程中的概念的关键字一起使用。

您可以在此线程中了解它们之间的区别。

于 2017-10-05T02:28:26.533 回答
14

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

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

于 2009-10-29T21:37:07.170 回答
9

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

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

于 2017-11-27T13:13:55.503 回答
7

有时代码比文字更容易:

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 的风格,因为它在函数内部和外部给了我更多的灵活性。

于 2015-05-16T07:21:57.490 回答
5
 " 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是 2 个不同的术语。我认为我们可以做出这样的评论:“每个对象都通过原型链接到原型__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”和“arguments”创建一个新的执行上下文,这就是“new”可以访问这些参数的原因。

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

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

myJob 属性不会添加到对象中,因为没有引用新创建的对象。

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

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

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

现在“我”对象可以使用“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

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

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

于 2020-07-06T02:58:21.817 回答
3

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

obj = new Element();
于 2009-10-29T21:38:25.597 回答
2

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

var Foo = function() {};
Foo.prototype.bar = 'bar';

var foo = new Foo();
foo instanceof Foo; // true

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

foo.bar; // 'bar'
于 2009-10-29T21:37:40.093 回答
2

好吧,JavaScript per si 可能因平台而异,因为它始终是原始规范 EcmaScript 的实现。

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

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

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

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

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

于 2015-05-13T23:05:28.037 回答
2

Javascript 不是面向对象的编程 (OOP) 语言,因此 javascript 中的LOOK UP过程使用“委托过程”,也称为原型委托或原型继承。

如果您尝试从一个对象中获取属性的值,但它没有,JavaScript 引擎会查看对象的原型(及其原型,一次上一步),它是原型链,直到链结束为nullObject.prototype == null(标准对象原型)。此时,如果未定义属性或方法,则返回未定义

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

  1. 手动创建对象,例如 newObj。
  2. 在 JS 规范[[prototype]](即proto)中使用proto(又名: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;              // true
Object.getPrototypeOf(obj) === Object.prototype // true

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

  1. 新的对象字面量由以下标识创建this:{}
  2. 引用和分配属性到this
  3. 隐藏键 Creation [[prototype]](即proto到 Function.prototype 共享空间。
  4. this对象 {}的隐式返回
function CreateObj(value1, value2) {
  this.property1 = value1;
  this.property2 = value2;
}

var obj = new CreateObj(10,20);
obj.__proto__ === CreateObj.prototype             // true
Object.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 false
var obj = CreateObj(10,20); // Is Pointing to Window Object true
window.property1; // 10
window.property2; // 20

于 2020-07-20T18:09:55.217 回答