最初遇到 JavaScript 时, new
关键字可能会造成很大的混乱,因为人们倾向于认为 JavaScript 并非面向对象的编程语言。
它做五件事:
this
变量指向新创建的对象。 this
被提及。 null
对象引用。在这种情况下,将返回该对象引用。 注意: 构造函数是指new
关键字之后的函数,例如
new ConstructorFunction(arg1, arg2)
完成此操作后,如果请求新对象的未定义属性,则脚本将改为检查对象的[[prototype]]对象的属性。这就是您可以获得类似于 JavaScript 中传统类继承的方法的方法。
关于这一点最困难的部分是点号 2。每个对象(包括函数)都具有称为[[prototype]] 的内部属性。 只能在对象创建时使用new , Object.create或基于文字(功能默认为 Function.prototype,数字为 Number.prototype 等)进行设置。只能使用Object.getPrototypeOf(someObject)读取它。 没有其他方法可以设置或读取该值。
除了隐藏的[[prototype]]属性外,函数还具有一个称为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
假设您具有以下功能:
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
的对象。任何函数都可以是构造函数,但这并不总是很有意义。
除了丹尼尔 · 霍华德的答案之外,以下是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);
在浏览器控制台中尝试以下代码。
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 答案了 :)
所以它可能不是用于创建对象的实例
正是用于此目的。您可以这样定义一个函数构造函数:
function Person(name) {
this.name = name;
}
var john = new Person('John');
但是 ECMAScript 的额外好处是您可以使用.prototype
属性进行扩展,因此我们可以执行以下操作:
Person.prototype.getName = function() { return this.name; }
由于可以访问此原型链,因此从此构造函数创建的所有对象现在都将具有getName
。
JavaScript 是一种面向对象的编程语言,它完全用于创建实例。它是基于原型的,而不是基于类的,但这并不意味着它不是面向对象的。
Javascript 是一种动态编程语言,它支持面向对象的编程范例,并且用于创建对象的新实例。
类对于对象不是必需的 - Javascript 是一种基于原型的语言。
目前已经有一些非常伟大的答案,但我发布一个新的强调我的观察案例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;
的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
成为新创建的对象(代替窗口对象)的属性。由于您没有任何显式的 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.
在这里,JavaScript 解释器再次看到new
关键字创建了一个新对象,该对象充当Foo
指向的匿名函数的调用对象(与this
相关)。同样, A
和B
成为新创建对象的属性。但是这次您有一个显式的 return 语句,因此 JavaScript 解释器将不会做任何事情。
在情况III 中要注意的是,由于new
关键字而创建的对象已从雷达中丢失。 bar
实际上指向一个完全不同的对象,该对象不是 JavaScript 解释器由于new
关键字而创建的对象。
引用 Java Scripit 的 David Flanagan:《权威指南》(第 6 版),第二章。 4,第 62 页:
计算对象创建表达式时,JavaScript 首先创建一个新的空对象,就像对象初始化程序 {} 创建的对象一样。接下来,它将使用指定的参数调用指定的函数,并将新对象作为 this 关键字的值传递。然后,函数可以使用它来初始化新创建的对象的属性。编写用作构造函数的函数不会返回值,并且对象创建表达式的值是新创建和初始化的对象。如果构造函数确实返回了对象值,则该值将成为对象创建表达式的值,并且新创建的对象将被丢弃。
- 其他信息 -
上述情况的代码段中使用的函数在 JS world 中具有特殊名称,如下所示:
情况一和二 - 构造函数
情况三 - 工厂功能。工厂函数不应与new
关键字一起使用,因为我已经在当前线程中解释了这个概念。
您可以在此线程中了解它们之间的区别。
有时候代码比文字容易:
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 的样式,因为它使我在函数内部和外部都有更多的灵活性。
new
关键字更改在其下运行函数的上下文,并返回指向该上下文的指针。
当您不使用new
关键字时,运行Vehicle()
函数的上下文与您调用Vehicle
函数的上下文相同。 this
关键字将引用相同的上下文。当您使用new Vehicle()
,将创建一个新上下文,因此函数内的关键字this
引用了新上下文。您得到的是新创建的上下文。