ChatGPT解决这个技术问题 Extra ChatGPT

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

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

它是什么?

它解决了哪些问题?

什么时候合适,什么时候不合适?


2
28 revs, 23 users 56%

它做了 5 件事:

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

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

new ConstructorFunction(arg1, arg2)

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

最困难的部分是第 2 点。每个对象(包括函数)都有这个称为 [[prototype]] 的内部属性。它只能在对象创建时设置,可以使用 new、Object.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

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


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

假设你有这个功能:

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


取决于执行上下文。就我而言(Qt 脚本),它只是一个全局对象。
这会导致更多的内存使用吗?
因为 window 是调用函数的对象 - 必须是:因为 window 是包含函数的对象。
@Taurus 在网络浏览器中,非方法函数将隐含地成为 window 的方法。即使在封闭中,即使匿名。但是,在示例中,它是对窗口的简单方法调用:Foo(); => [default context].Foo(); => window.Foo();。在这个表达式中,windowcontext(不仅是 caller,这无关紧要)。
@Taurus 基本上是的。然而,在 ECMA 6 和 7 中,事情更加复杂(参见 lambdas、classes 等)。
b
basilikum

除了 Daniel Howard 的回答之外,以下是 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 比英语更容易理解:v
优秀的答案。我有一个小问题:func.prototype 怎么可能是 null?你能详细说明一下吗?
@tomp 你可以覆盖原型属性,只需编写 A.prototype = null; 在这种情况下 new A() 将导致对象,即内部原型指向 Object 对象:jsfiddle.net/Mk42Z
typeof 检查可能是错误的,因为宿主对象可能产生不同于“对象”或“函数”的东西。要测试某物是否是对象,我更喜欢 Object(ret) === ret
@Oriol 谢谢你的评论。你说的是真的,任何实际的测试都应该以更稳健的方式进行。但是,我认为对于这个概念性答案,typeof 测试只是更容易理解幕后发生的事情。
C
Community

让初学者更好地理解它

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

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

现在您可以阅读community wiki answer :)


好答案。此外 - 省略 return this; 会产生相同的输出。
A
Adrian Thompson Phillips

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

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

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

var john = new Person('John');

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

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

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


函数构造函数像类一样使用,没有 class 关键字,但您几乎可以做同样的事情。
有一个 class 关键字 - class 保留供将来使用
顺便说一句,这就是为什么您使用 .className 而不是 .class 来设置 CSS 类
h
hyperslug

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


我喜欢说 JavaScript 似乎比所有那些基于类的语言更加面向对象。在 JavaScript 中,您编写的所有内容都会立即成为对象,但在基于类的语言中,您首先编写声明,然后才创建类的特定实例(对象)。 JavaScript 原型似乎模糊地提醒了基于类的语言的所有 VTABLE 东西。
W
Willem van der Veen

概括:

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

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

例子:

功能狗(年龄){ this.age =年龄; } 常量 doggie = new Dog(12);控制台日志(小狗); console.log(Object.getPrototypeOf(doggie) === Dog.prototype) // true

究竟会发生什么:

const doggie 说:我们需要内存来声明一个变量。赋值运算符 = 表示:我们将使用 = 之后的表达式初始化这个变量。表达式是 new Dog(12)。 JS 引擎看到 new 关键字,创建一个新对象并将原型设置为 Dog.prototype 执行构造函数,并将 this 值设置为新对象。在这一步中,年龄被分配给新创建的小狗对象。新创建的对象被返回并分配给变量 doggie。


R
RBT

已经有一些非常好的答案,但我发布了一个新答案,以强调我对下面案例 III 的观察,即当您在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; 语句。这里的 window 是调用对象(上下文 this),它获取新的 AB 属性。

案例二:

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 关键字创建了一个新对象,该对象充当 Foo 指向的匿名函数的调用对象(上下文 this)。在这种情况下,AB 成为新创建对象的属性(代替 window 对象)。由于您没有任何明确的 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 解释器创建了一个新对象,该对象充当 Foo 指向的匿名函数的调用对象(上下文 this)。同样,AB 成为新创建对象的属性。但是这一次你有一个明确的 return 语句,所以 JavaScript 解释器将自己做任何事情。

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

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

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

附加信息

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

Case # 名称 Case I 构造函数 Case II 构造函数 Case III 工厂函数

您可以在 this 线程中了解构造函数和工厂函数之间的区别。

关于案例 III 的注意事项 - 工厂函数不应与我在上面的代码片段中显示的 new 关键字一起使用。我故意这样做只是为了解释这篇文章中的概念。


您的案例 3,是 gr8 观察
S
Seth

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

对象不需要类 - Javascript 是一种prototype based语言。


J
Juzer Ali

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

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


就范围而言,这是一个非常有见地的答案。 Gr8 除了答案。
r
rsbkk

有时代码比文字更容易:

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


B1 = new func2(2); <- 为什么没有 B1.y
@sunny_dev 我不是 JS 专家,但可能是因为 func2 直接返回一个值(z 对象),而不是使用内部值工作/返回(this)
Y
Yilmaz
 " 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 链接到一个原型”但是 __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 中的所有方法。

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


abi anlatim guzel tesekkurler +1 ledim de,什么是 JS 中的构造函数和类?
我必须用英文写,否则会被认为是骗局:) 课堂就像工厂。想象一下汽车厂。每辆车都有自己的属性和方法:比如颜色,有 4 个轮子,有一个马达等。所以构造器是你建造汽车的地方,就像工厂的生产单元一样。每当您创建一辆新车时,该车的特定属性将在构造函数中构建。例如,并非所有汽车都有相同的颜色。所以我们在构建或启动汽车时传递颜色。所以每辆车都会有颜色,所以会在构造函数中指定
构造函数中的属性将存储在汽车对象或汽车实例中。假设您构建了 1000 个汽车实例,这将占用太多空间。因此,每辆车共有的属性是在构造函数之外指定的。例如,每辆车都有 4 个轮子。所以它存储在原型中。属性存储在原型中,而不是存储在每个汽车对象中。相反,它将存储在一个地方,您将在需要时使用它。这称为原型继承。我希望我的解释足够清楚:)
S
Seth

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

obj = new Element();

k
khizer

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

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

Imp!! 函数是 functions are first-class objects

函数 = 函数 + 对象组合

FunctionName.prototype = { 共享子对象 }

{
  // other properties
  prototype: {  
   // shared space which automatically gets [[prototype]] linkage 
      when "new" keyword is used on creating instance of "Constructor 
      Function"
  }
}

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

手动创建对象,例如 newObj。在 JS 规范 [[prototype]](即 proto)中使用 proto(又名:dunder proto)创建隐藏键,引用并将属性分配给 newObj 返回的 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 有助于自动执行此过程:

新对象字面量由以下标识创建:{} 引用并将属性分配给此隐藏键 创建 [[prototype]](即 proto)到 Function.prototype 共享空间。此对象的隐式返回 {}

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


e
eyelidlessness

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

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

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

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

foo.bar; // 'bar'

new 关键字基本上已经将函数关联为构造函数;你不需要返回任何东西。你可以这样做: function foo(x) { this.bar = x; } var obj = 新的 foo(10);警报(obj.bar);
除非出于某种目的,您不需要从构造函数返回对象。例如,如果您必须返回一个特定的对象实例而不是每次都创建一个新对象(无论出于何种原因)。但是,在您的示例中,这是完全没有必要的。
嗯,这是一个例子。您可以返回一个对象。在这个场景中使用了许多模式,我提供了一个作为“例如”,因此我的词是“例如”。
J
João Pinho

好吧,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() 生成当前日期和时间的字符串表示,而不是对象。