JavaScript 中的 new
关键字在第一次遇到时可能会非常混乱,因为人们倾向于认为 JavaScript 不是一种面向对象的编程语言。
它是什么?
它解决了哪些问题?
什么时候合适,什么时候不合适?
它做了 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 之前,我阅读了大量关于这个主题的垃圾,其中用漂亮的图表很好地解释了这一点。
假设你有这个功能:
var Foo = function(){
this.A = 1;
this.B = 2;
};
如果您将其称为独立函数,如下所示:
Foo();
执行此函数将向 window
对象添加两个属性(A
和 B
)。它将它添加到 window
中,因为 window
是在您这样执行时调用该函数的对象,而函数中的 this
是调用该函数的对象。至少在 Javascript 中。
现在,用 new
像这样调用它:
var bar = new Foo();
将 new
添加到函数调用时会发生什么情况,即创建了一个新对象(仅 var bar = new Object()
),并且函数中的 this
指向您刚刚创建的新 Object
,而不是指向该对象调用函数。因此 bar
现在是具有属性 A
和 B
的对象。任何函数都可以是构造函数,但这并不总是有意义的。
window
的方法。即使在封闭中,即使匿名。但是,在示例中,它是对窗口的简单方法调用:Foo();
=> [default context].Foo();
=> window.Foo();
。在这个表达式中,window
是 context(不仅是 caller,这无关紧要)。
除了 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);
func.prototype
怎么可能是 null
?你能详细说明一下吗?
Object(ret) === ret
。
typeof
测试只是更容易理解幕后发生的事情。
让初学者更好地理解它
在浏览器控制台中尝试以下代码。
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;
会产生相同的输出。
所以它可能不是用于创建对象的实例
它正是为此而使用的。您定义一个函数构造函数,如下所示:
function Person(name) {
this.name = name;
}
var john = new Person('John');
然而,ECMAScript 的额外好处是您可以使用 .prototype
属性进行扩展,因此我们可以执行类似...
Person.prototype.getName = function() { return this.name; }
从该构造函数创建的所有对象现在都将具有 getName
,因为它们可以访问原型链。
class
关键字,但您几乎可以做同样的事情。
JavaScript 是一种面向对象的编程语言,它完全用于创建实例。它是基于原型的,而不是基于类的,但这并不意味着它不是面向对象的。
概括:
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。
已经有一些非常好的答案,但我发布了一个新答案,以强调我对下面案例 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
),它获取新的 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
关键字创建了一个新对象,该对象充当 Foo
指向的匿名函数的调用对象(上下文 this
)。在这种情况下,A
和 B
成为新创建对象的属性(代替 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
)。同样,A
和 B
成为新创建对象的属性。但是这一次你有一个明确的 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
关键字一起使用。我故意这样做只是为了解释这篇文章中的概念。
new
关键字更改函数正在运行的上下文并返回指向该上下文的指针。
当您不使用 new
关键字时,函数 Vehicle()
运行的上下文与您调用 Vehicle
函数的上下文相同。 this
关键字将引用相同的上下文。当您使用 new Vehicle()
时,会创建一个新的上下文,因此函数内的关键字 this
指的是新的上下文。你得到的回报是新创建的上下文。
有时代码比文字更容易:
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
?
" 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 中的所有方法。
当您想要创建具有相同属性和方法的多个相似对象时,构造函数很有用。这样您就不会分配额外的内存,因此您的代码将更有效地运行。
new
关键字用于创建新的对象实例。是的,javascript 是一种动态编程语言,它支持面向对象的编程范式。关于对象命名的约定是,对于应该由 new 关键字实例化的对象,始终使用大写字母。
obj = new Element();
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
new
关键字使用函数作为构造函数来创建对象实例。例如:
var Foo = function() {};
Foo.prototype.bar = 'bar';
var foo = new Foo();
foo instanceof Foo; // true
实例继承自构造函数的 prototype
。所以给出上面的例子......
foo.bar; // 'bar'
好吧,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() 生成当前日期和时间的字符串表示,而不是对象。
ObjMaker
定义为返回值的函数,会发生什么不同?new
存在因此您不必编写工厂方法来构造/复制函数/对象。它的意思是,“复制它,让它就像它的父类‘类’一样;高效而正确地这样做;并存储只有我,JS,内部才能访问的继承信息”。为此,它修改了新对象原本无法访问的内部prototype
以不透明地封装继承的成员,模仿经典的 OO 继承链(运行时不可修改)。您可以在没有new
的情况下对此进行模拟,但继承将是运行时可修改的。好的?坏的?由你决定。Notice that this pattern is deprecated!
。设置类原型的正确最新模式是什么?