JavaScript中的新关键字在第一次遇到时可能会非常令人困惑,因为人们倾向于认为JavaScript不是一种面向对象的编程语言。
这是怎么一回事?它解决了什么问题?什么时候合适,什么时候不合适?
JavaScript中的新关键字在第一次遇到时可能会非常令人困惑,因为人们倾向于认为JavaScript不是一种面向对象的编程语言。
这是怎么一回事?它解决了什么问题?什么时候合适,什么时候不合适?
所以它可能不是为了创造对象的实例
它正是为了这个。您可以这样定义函数构造函数:
function Person(name) {
this.name = name;
}
var john = new Person('John');
然而,ECMAScript的额外好处是您可以使用.prototype属性进行扩展,因此我们可以执行以下操作。。。
Person.prototype.getName = function() { return this.name; }
从该构造函数创建的所有对象现在都将有一个getName,因为它们可以访问原型链。
new关键字使用函数作为构造函数创建对象的实例。例如:
var Foo = function() {};
Foo.prototype.bar = 'bar';
var foo = new Foo();
foo instanceof Foo; // true
实例继承自构造函数的原型。因此,在上面的例子中。。。
foo.bar; // 'bar'
new关键字用于创建新的对象实例。是的,JavaScript是一种动态编程语言,它支持面向对象的编程范式。关于对象命名的约定是:对于应该由new关键字实例化的对象,始终使用大写字母。
obj = new Element();
假设您有此函数:
var Foo = function(){
this.A = 1;
this.B = 2;
};
如果将其作为独立函数调用,如下所示:
Foo();
执行此函数将向窗口对象(A和B)添加两个财产。它将其添加到窗口中,因为当您这样执行函数时,window是调用函数的对象,而函数中的this是调用该函数的对象。至少在JavaScript中。
现在,用新的:
var bar = new Foo();
当您向函数调用添加新对象时,将创建一个新对象(仅var bar=new object()),并且函数中的this指向您刚刚创建的新对象,而不是调用函数的对象。所以bar现在是一个具有财产A和B的对象。任何函数都可以是构造函数;这并不总是有意义的。
它做5件事:
它将创建一个新对象。此对象的类型只是对象。它将此新对象的内部、不可访问的[[prototype]](即__proto__)属性设置为构造函数的外部、可访问的原型对象(每个函数对象都自动具有原型属性)。它使该变量指向新创建的对象。每当提到这一点时,它都会使用新创建的对象来执行构造函数。它返回新创建的对象,除非构造函数返回非空对象引用。在这种情况下,将返回该对象引用。
注意:构造函数是指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'
这类似于类继承,因为现在,使用新的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
在最后找到这一页之前,我读了大量关于这一主题的垃圾,在那里用漂亮的图表很好地解释了这一点。
除了丹尼尔·霍华德的回答之外,以下是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本身可能因平台而异,因为它始终是原始规范ECMAScript(ES)的实现。
在任何情况下,与实现无关,所有遵循ECMAScript规范的JavaScript实现都将为您提供一种面向对象的语言。根据ES标准:
ECMAScript是一种面向对象的编程语言,用于执行计算和操纵计算对象在主机环境中。
现在我们已经同意JavaScript是ECMAScript的一种实现,因此它是一种面向对象的语言。在任何面向对象的语言中,新操作的定义都表示,这样的关键字用于从特定类型的类(包括匿名类型,例如C#)创建对象实例。
在ECMAScript中,我们不使用类,您可以从规范中了解到:
ECMAScript不使用C++、Smalltalk或Java中的类。相反,可以通过各种方式创建对象,包括通过文字符号或通过构造函数创建对象,然后执行代码,通过指定初始值初始化所有或部分对象其财产的值。每个构造函数都是一个函数属性名为―prototype‖,用于实现基于原型的继承和共享财产。对象由创建在新表达式中使用构造函数;例如,新Date(2009,11)创建一个新的Date对象。调用构造函数不使用new的结果取决于构造函数。例如,Date()生成当前日期和时间,而不是对象。
有时代码比文字更容易:
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的样式,因为它给了我函数内外更多的灵活性。
让初学者更好地理解它
在浏览器控制台中尝试以下代码。
function Foo() {
return this;
}
var a = Foo(); // Returns the 'window' object
var b = new Foo(); // Returns an empty object of foo
a instanceof Window; // True
a instanceof Foo; // False
b instanceof Window; // False
b instanceof Foo; // True
现在您可以阅读社区wiki答案:)
请看一下我对案例三的观察。这是关于当你在一个新创建的函数中有一个显式返回语句时会发生什么。查看以下案例:
案例一:
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;语句。因此,上述代码示例相当于:
var Foo = function(){
this.A = 1;
this.B = 2;
return undefined;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1
当调用Foo函数时,窗口是默认的调用对象(上下文对象),它获取新的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解释器看到了新关键字,创建了一个新对象,作为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解释器再次看到新关键字,创建一个新对象,该对象充当Foo所指向的匿名函数的调用对象(上下文this)。同样,A和B成为新创建对象的财产。但这次您有一个显式的return语句,所以JavaScript解释器不会自己做任何事情。
在案例III中需要注意的是,由于新关键字而创建的对象从雷达中消失了。bar实际上指向一个完全不同的对象,而不是JavaScript解释器由于新关键字而创建的对象。
引用《JavaScript:最终指南》(第6版)第4章第62页中的David Flanagan:
计算对象创建表达式时,首先使用JavaScript创建一个新的空对象,就像该对象创建的对象一样初始值设定项{}。接下来,它使用指定的参数,将新对象作为this的值传递关键字。然后,函数可以使用它来初始化财产新创建的对象的。编写用作构造函数的函数不返回值,对象创建表达式的值是新创建和初始化的对象。如果构造函数返回一个对象值,该值将成为该对象的值创建表达式,并且丢弃新创建的对象。
其他信息:
上述案例代码片段中使用的函数在JavaScript世界中具有特殊名称,如下所示:
Case # | Name |
---|---|
Case I | Constructor function |
Case II | Constructor function |
Case III | Factory function |
您可以在本线程中了解构造函数和工厂函数之间的区别。
案例III中的代码气味-工厂函数不应与我在上面的代码片段中显示的新关键字一起使用。我这么做只是为了解释这个概念。
new关键字更改运行函数的上下文,并返回指向该上下文的指针。
如果不使用new关键字,则运行函数Vehicle()的上下文与调用Vehicle函数的上下文相同。this关键字将引用相同的上下文。当您使用new Vehicle()时,将创建一个新上下文,因此函数中的关键字this将引用新上下文。您得到的回报是新创建的上下文。
摘要:
new关键字在JavaScript中用于从构造函数创建对象。new关键字必须放置在构造函数调用之前,并将执行以下操作:
创建新对象将此对象的原型设置为构造函数的原型属性将this关键字绑定到新创建的对象并执行构造函数返回新创建的对象
例子:
功能狗(年龄){this.age=年龄;}const doggie=新狗(12);console.log(doggie);console.log(Object.getPrototypeOf(doggie)==Dog.prototype)//true
具体情况:
const doggie说:我们需要内存来声明变量。赋值运算符=表示:我们将使用=表达式是new Dog(12)。JavaScript引擎看到新关键字,创建一个新对象,并将原型设置为Dog.prototype执行构造函数时,将此值设置为新对象。在此步骤中,将年龄指定给新创建的小狗对象。新创建的对象被返回并分配给变量doggie。
每个函数都有一个原型对象,该对象自动设置为使用该函数创建的对象的原型。
你们可以轻松检查:
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链接到原型”,但__proto__在JavaScript中并不存在。浏览器添加此属性只是为了帮助调试。
console.log(a.__proto__); // Returns {}
console.log(b.__proto__); // Returns [Function]
你们可以很容易地在终端上检查这个。那么什么是构造函数?
function CreateObject(name, age) {
this.name = name;
this.age = age
}
首先要注意的五件事:
当使用new调用构造函数函数时,调用函数的内部[[Construct]]方法来创建新的实例对象并分配内存。我们没有使用return关键字。new将处理它。函数的名称是大写的,因此当开发人员看到您的代码时,他们可以理解他们必须使用新的关键字。我们不使用箭头函数。因为这个参数的值是在创建箭头函数的那个一刻获取的,即“窗口”。箭头函数的作用域是词汇化的,而不是动态的。这里的词汇是本地的意思。箭头函数携带其本地“this”值。与常规函数不同,箭头函数永远不能用new关键字调用,因为它们没有[[Construct]]方法。箭头函数也不存在原型属性。const me=新建CreateObject(“yilmaz”,“21”)new调用该函数,然后创建一个空对象{},然后添加值为“name”的“name”键和值为参数“age”的“age”键。
当我们调用函数时,将使用“this”和“arguments”创建一个新的执行上下文,这就是为什么“new”可以访问这些arguments的原因。
默认情况下,构造函数中的这个将指向“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 isn't any 'myJob' key
一开始我说每个函数都有一个“原型”属性,包括构造函数。我们可以将方法添加到构造函数的原型中,这样从该函数创建的每个对象都可以访问它。
CreateObject.prototype.myActions = function() { /* Define something */ }
现在,“me”对象可以使用“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中的所有方法。
当您想要创建多个具有相同财产和方法的类似对象时,构造函数非常有用。这样,您就不会分配额外的内存,因此代码将更高效地运行。
JavaScript不是面向对象编程(OOP)语言。因此,JavaScript中的查找过程使用委托过程,也称为原型委托或原型继承。
如果您试图从一个没有的对象中获取属性的值,JavaScript引擎会查找该对象的原型(以及它的原型,一步一步)。它是原型链,直到链结束为null,即Object.prototype==null(标准对象原型)。
此时,如果未定义属性或方法,则返回undefined。
重要的函数是一类对象。
函数=函数+对象组合框
FunctionName.prototype={共享子对象}
{
// other properties
prototype: {
// shared space which automatically gets [[prototype]] linkage
when "new" keyword is used on creating instance of "Constructor
Function"
}
}
因此,使用新关键字。,
手动创建对象,例如newObj。使用JavaScript规范[[prototype]](即proto)中的proto(AKA:dunderproto)创建隐藏键引用并将财产分配给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关键字的构造函数:
=>此:窗口
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
它有三个阶段:
1.创建:它创建一个新对象,并将此对象的[[prototype]]属性设置为构造函数的prototype属性。
2.执行:它指向新创建的对象并执行构造函数。
3.返回:在正常情况下,它将返回新创建的对象。但是,如果显式返回非空对象或函数,则会返回此值。需要提及的是,如果返回非空值,但它不是对象(例如Symbol value、undefined、NaN),则忽略该值并返回新创建的对象。
function myNew(constructor, ...args) {
const obj = {}
Object.setPrototypeOf(obj, constructor.prototype)
const returnedVal = constructor.apply(obj, args)
if (
typeof returnedVal === 'function'
|| (typeof returnedVal === 'object' && returnedVal !== null)) {
return returnedVal
}
return obj
}
有关myNew的更多信息和测试,请阅读我的博客:https://medium.com/@magenta2127/how-does-the-new-operator-work-f7eaac692026