在new
JavaScript中的关键字可能会相当混乱首次遇到它的时候,人们往往会认为JavaScript是不是面向对象的编程语言。
- 它是什么?
- 它解决什么问题?
- 什么时候合适,什么时候不合适?
在new
JavaScript中的关键字可能会相当混乱首次遇到它的时候,人们往往会认为JavaScript是不是面向对象的编程语言。
Answers:
它做五件事:
this
变量指向新创建的对象。this
提及时,它将使用新创建的对象执行构造函数。null
对象引用,否则它将返回新创建的对象。在这种情况下,将返回该对象引用。注意:构造函数指的是new
关键字后面的函数,如
new ConstructorFunction(arg1, arg2)
完成此操作后,如果请求新对象的未定义属性,脚本将检查该对象的 改为 [[prototype]]对象的属性。这就是您可以获得类似于JavaScript中传统类继承的方法的方法。
关于这一点最困难的部分是点号2。每个对象(包括函数)都具有称为[[prototype]]的内部属性。它可以只被在创建对象的时间与设置,无论是新的,具有的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
在最终找到此页面之前,我读了很多有关此主题的垃圾,并用漂亮的图表很好地说明了这一点。
ObjMaker
定义为返回值的函数,会发生什么变化?
new
存在,因此您不必编写工厂方法即可构造/复制函数/对象。它的意思是,“复制它,使其像其父类一样;有效且正确地进行;并存储内部仅对我(JS)可用的继承信息”。为此,它修改了prototype
新对象本来无法访问的内部,以不透明地封装了继承的成员,从而模仿了经典的OO继承链(在运行时不可修改)。您可以在不使用的情况下进行模拟new
,但是继承可以在运行时进行修改。好?坏?由你决定。
Notice that this pattern is deprecated!
。设置类原型的正确的最新模式是什么?
假设您具有以下功能:
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
隐式的方法。即使处于封闭状态,即使是匿名者。但是,在示例中,这是在window上进行的简单方法调用:Foo();
=> [default context].Foo();
=> window.Foo();
。在此表达式中window
是上下文(不仅是调用者,这无关紧要)。
除了丹尼尔·霍华德(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
?您能详细说明一下吗?
A.prototype = null;
在那种情况下new A()
,这将导致on对象,那就是内部原型指向该Object
对象:jsfiddle.net/Mk42Z
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
现在您可以阅读社区Wiki答案了:)
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 是一种面向对象的编程语言,它完全用于创建实例。它是基于原型的,而不是基于类的,但这并不意味着它不是面向对象的。
已经有一些非常好的答案,但是我要发布一个新的答案,以强调我对以下情况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;
在函数末尾强制插入一条语句。此处的窗口是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.
在这里,JavaScript解释器再次看到new
关键字创建了一个新对象,该对象充当所this
指向的匿名函数的调用对象(上下文)Foo
。再次,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的样式,因为它使我在函数内部和外部都有更多的灵活性。
B1 = new func2(2);
<-为什么不会这样B1.y
?
的new
关键字以JavaScript用于创建从一个构造函数的对象。该new
关键字的构造函数调用之前放置,并会做以下几件事:
this
关键字绑定到新创建的对象并执行构造函数function Dog (age) {
this.age = age;
}
const doggie = new Dog(12);
console.log(doggie);
console.log(doggie.__proto__ === Dog.prototype) // true
到底发生了什么:
const doggie
说:我们需要内存来声明变量。=
说:我们将使用=
new Dog(12)
。JS引擎看到new关键字,创建一个新对象并将原型设置为Dog.prototypethis
设置为新对象的值执行构造函数。在此步骤中,将年龄分配给新创建的小狗对象。的new
关键字创建使用函数作为构造对象的实例。例如:
var Foo = function() {};
Foo.prototype.bar = 'bar';
var foo = new Foo();
foo instanceof Foo; // true
实例继承自prototype
构造函数的。因此,以上述示例为例...
foo.bar; // 'bar'
每个平台的JavaScript差异很大,因为它始终是原始规范EcmaScript的实现。
无论如何,独立于实现的所有遵循EcmaScript规范权利的所有JavaScript实现都将为您提供面向对象的语言。根据ES标准:
ECMAScript是一种面向对象的编程语言,用于在主机环境中执行计算和操纵计算对象。
因此,现在我们已经同意JavaScript是EcmaScript的实现,因此它是一种面向对象的语言。new
在任何面向对象的语言中,操作的定义都说,该关键字用于从某种类型的类(包括匿名类型,如C#)中的类创建对象实例。
在EcmaScript中,我们不使用类,您可以从规范中读取:
ECMAScript不使用诸如C ++,Smalltalk或Java中的类。相反,可以通过各种方式创建对象,包括通过文字符号或通过创建对象的构造函数,然后执行通过将初始值分配给它们的属性来初始化全部或部分对象的代码。每个构造函数都是一个函数,具有名为“ prototype”的属性,该属性用于实现基于原型的继承和共享属性。通过
在新表达式中使用构造函数来创建对象。例如,new Date(2009,11)创建一个新的Date对象。不使用new调用构造函数的后果取决于构造函数。例如,Date()生成当前日期和时间的字符串表示形式,而不是对象。