Heim >Web-Frontend >js-Tutorial >Objektprototypen und Prototypketten in JavaScript verstehen
Dieser Artikel führt Sie in den Objektprototyp und die Prototypenkette in JavaScript ein. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.
Ich glaube, jeder hat map
so verwendet: map
:
let arr = [0, 1, 2] let doubleArr = arr.map(c => c * 2) console.log(doubleArr) // 0, 2, 4
不知道你有没有想过, arr
本身并没有设定 map
属性,那为什么可以用 map
这个函数呢?
把它打印出来看看:
console.log(arr) // 0: 0 // 1: 1 // 2: 2 // length: 3 // __proto__: Array(0)
出现了一个名为 __proto__
的对象,如果再将其展开,就会看到所有 Array 对象可以使用的函数;当然我们也可以在其中找到 map
函数,而这也正是例子中所调用的 arr.map
这个函数:
console.log(arr.map === arr.__proto__.map) // true
这里出现的 __proto__
对象,也就是所谓的 原型对象(Prototype) 。
不同于 Java、C# 等基于类(Class) 的面向对象语言,通过定义类、创建实例、指定继承等方式来传递属性及方法;Javascript 则是个基于原型(Prototype)的对语言 ,通过预先建立出的原型对象,当新对象建立时指定对象的原型要参照哪个原型对象。
而当我们调用对象的属性或方法时,若对象本身没有这个属性或方法,JavaScript 会自动寻找它原型中的方法,这也就是为什么可以直接调用 arr.map
而不会出错的原因。
你可能已经发现在前面的例子中,__proto__
对象里仍然有 __proto__
属性:
console.log(arr.__proto__) // Array 的 Prototype console.log(arr.__proto__.__proto__) // Object 的 Prototype console.log(arr.__proto__.__proto__.__proto__) // null
在上述机制中,每当对象建立时都会绑定原型,既然对象都有原型,对象原型本身也是对象,自然也不例外;由这个例子中我们可以看出:
arr
是数组实例,原型是 Arrayarr.__proto__
是数组的原型,原型是 Objectarr.__proto__.__proto__
是对象的原型,原型是 null
arr.__proto__.__proto__.__proto__
是 null,没有任何属性由于每个对象都有原型,这样就形成了一个关联一个、层层相互依赖的从属关系,我们把它们叫做原型链(Prototype Chain) ;通过这种机制,让对象得以使用原型中的属性和方法,并凭借原型链一层一层的按顺序继承,让对象能拥有原型链上所有原型的功能,这就是 JavaScript 对象背后的运作机制。
补充:在 JavaScript 中,几乎每个原型链的末端都会是 Object,并最后指向到 null
。
说了这么多,该来点代码了,接下来就来练习一下原型的建立、设定及修改吧。
先来创建一个新的对象构造函数:
function Person(name) { this.name = name }Person.prototype.hello = function () { console.log(`Hello ${this.name}.`) }let gary = new Person('Gary') gary.hello() // Hello Gary.Object.getPrototypeOf(gary) // {hello: ƒ, constructor: ƒ}
上面的例子创建了一个简单的对象构造函数 Person()
,并在构造函数中设定对象属性。对象的方法中,由于方法不需要让每个对象都独自拥有一份,以避免造成冗余的内存消耗,应该要像前面 Array.prototype.map
的例子那样把对象的方法设定给原型对象(Person.prototype
),让这个构造函数创建出来的对象都可以共用这些方法。最后建立一个新的 Person
对象,并通过 getPrototypeOf(obj)
获取新产生对象的原型。
Q:为什么不直接用__proto__
获取原型对象?
A:因为虽然__proto__
被几乎所有的浏览器支持,但它是非标准属性;通过getPrototypeOf
取得对象的原型是正确的方法。提醒:
Person.prototype
不是Person
的原型,而是构造函数执行后所建立的新对象的原型;千万不要把构造函数的prototype
属性与对象的原型搞混!
接着再创建新的对象原型,并继承 Person
:
function Engineer(name, skill) { Person.call(this, name) this.skill = skill } Engineer.prototype = Object.create(Person.prototype) Engineer.prototype.constructor = Engineerlet alice = new Engineer('Alice', 'JavaScript') alice.hello() // Hello Alice. console.log(alice.skill) // JavaScriptObject.getPrototypeOf(alice) // Person {constructor: ƒ}
这里建立了新的对象 Engineer
的原型,并通过 Engineer.prototype
的指定,让它的原型继承自 Person.prototype
,最后再重新设定 Engineer.prototype.constructor
Object.defineProperty(Vue.prototype, '$date', { value: dateTimeFormat }) // 之后就可以这样用 vm.$date(dateObj)Ich weiß nicht, ob Sie darüber nachgedacht haben, aber
arr
selbst ist nicht auf das Attribut map
festgelegt. Warum können wir dann die Funktion map
verwenden? 🎜🎜Drucken Sie es aus und sehen Sie: 🎜Person.prototype.hello = function () { console.log(`Bye ${this.name}.`) }gary.hello() // Bye Gary. alice.hello() // Bye Alice.🎜Ein Objekt mit dem Namen
__proto__
wird angezeigt. Wenn Sie es erweitern, sehen Sie alle Funktionen, die von Array-Objekten verwendet werden können map
-Funktion darin, und das ist genau die arr.map
-Funktion, die im Beispiel aufgerufen wird: 🎜class Person { constructor (name){ this.name = name } // 方法会自动放到 Person.prototype hello() { console.log(`Hello ${this.name}.`) } }class Engineer extends Person { constructor (name, skill){ super(name) // 调用 Person 的构造函数 this.skill = skill } }let alice = new Engineer('Alice', 'JavaScript') alice.hello() // Hello Alice.Object.getPrototypeOf(alice) // Person {constructor: ƒ}🎜
__proto__
erscheint hier Objekt, auch bekannt als 🎜Prototyp 🎜. 🎜🎜Im Gegensatz zu Java, C# und anderen 🎜klassenbasierten objektorientierten Sprachen, die Eigenschaften und Methoden übertragen, indem sie Klassen definieren, Instanzen erstellen, Vererbung angeben usw.; Javascript ist eine 🎜prototypbasierte Sprache🎜 durch die Vor- Wenn ein neues Objekt erstellt wird, geben Sie an, auf welches Prototypobjekt sich der Prototyp des Objekts beziehen soll. 🎜🎜Wenn wir eine Eigenschaft oder Methode eines Objekts aufrufen und das Objekt selbst nicht über diese Eigenschaft oder Methode verfügt, sucht JavaScript automatisch nach der Methode in seinem Prototyp, weshalb wir arr.map aufrufen können code> direkt und Gründe, die nicht schief gehen können. 🎜🎜🎜Prototypenkette🎜🎜🎜Möglicherweise haben Sie im vorherigen Beispiel entdeckt, dass das <code>__proto__
-Objekt immer noch das __proto__
-Attribut hat: 🎜rrreee🎜Im obigen Mechanismus wann immer Prototypen sind bei ihrer Erstellung an Objekte gebunden, daher ist der Objektprototyp selbst auch ein Objekt, sodass wir in diesem Beispiel natürlich keine Ausnahme sehen können: 🎜arr ist eine Array-Instanz, der Prototyp ist Array
arr.__proto__
ist der Prototyp des Arrays, der Prototyp ist Objectarr .__proto__.__proto__
Es ist der Prototyp des Objekts. Der Prototyp ist null
arr.__proto__.__proto__.__proto__
null und hat keine AttributeErgänzung: In JavaScript ist das Ende fast jeder Prototypkette Object und zeigt schließlich auf null
.
🎜🎜Prototypen verwenden🎜🎜🎜 Nachdem wir so viel gesagt haben, ist es an der Zeit, uns mit etwas Code zu befassen. Als Nächstes üben wir das Erstellen, Festlegen und Ändern von Prototypen. 🎜🎜Erstellen Sie zunächst einen neuen Objektkonstruktor: 🎜rrreee🎜Das obige Beispiel erstellt einen einfachen Objektkonstruktor Person()
und legt die Objekteigenschaften im Konstruktor fest. Da in der Methode des Objekts keine separate Kopie jedes Objekts erforderlich ist, um redundanten Speicherverbrauch zu vermeiden, sollte das Objekt wie im vorherigen Beispiel von Array.prototype.map
verwendet werden Methoden werden auf das Prototypobjekt (Person.prototype
) festgelegt, sodass von diesem Konstruktor erstellte Objekte diese Methoden gemeinsam nutzen können. Erstellen Sie abschließend ein neues Person
-Objekt und erhalten Sie den Prototyp des neu generierten Objekts über getPrototypeOf(obj)
. 🎜F: Warum nicht direkt__proto__
verwenden, um das Prototypobjekt zu erhalten?
A: Obwohl__proto__
von fast allen Browsern unterstützt wird, ist es ein nicht standardmäßiges Attribut, den Prototyp des Objekts übergetPrototypeOf
zu erhalten . 🎜Erinnerung:Person.prototype
ist nicht der Prototyp vonPerson
, sondern der Prototyp des neuen Objekts, das nach der Ausführung des Konstruktors erstellt wurde, ersetzt niemals den 🎜von Konstruktor Die Prototyp
-Eigenschaft 🎜 wird mit dem Prototyp des 🎜 Objekts 🎜 verwechselt! 🎜
Person
: 🎜rrreee🎜Hier wird ein neues Objekt Engineers Prototyp und geben Sie ihn durch <code>Engineer.prototype
an, damit sein Prototyp von Person.prototype
erbt, und setzen Sie schließlich den Engineer.prototype
zurück >, lassen Sie den Konstruktor auf sich selbst verweisen; dies vervollständigt die grundlegendste Prototypenvererbung. 🎜Q:为什么需要重新设定constructor
?
A:这边功过Object.create
复制了Person.prototype
的全部属性,连同constructor
属性都会被覆盖掉,如果constructor
属性错误,在做instanceof
判断时会产生错误的结果;因此这边设定继承时需要再次将constructor
重新指定回构造函数本身。
原型的引用、继承是直接参照到原型对象上,并非是在每个对象都复制一份原型;因此可以利用这个特性,在原型上增加自定义的属性和方法,让所有该类型的对象都能得到新方法;许多针对旧版浏览器的 Polyfill 就是这样实现的。
例如我们在写 Vue 项目的时候,可能都有做过类似的操作,把共用性高的属性方法放到 Vue.prototype
中:
Object.defineProperty(Vue.prototype, '$date', { value: dateTimeFormat }) // 之后就可以这样用 vm.$date(dateObj)
这样的确很方便,但也要提醒开大家,当我们在做原型修改的时候要特别小心。接着刚才的例子,如果尝试对 Person
原型中的方法做个修改:
Person.prototype.hello = function () { console.log(`Bye ${this.name}.`) }gary.hello() // Bye Gary. alice.hello() // Bye Alice.
如结果所示,当对象原型做修改时,所有原型链上有这个原型的对象,通通都会受到影响,不管对象是在修改前还是修改后创建的。
建议大家除非是 Polyfill,否则应该要极力避免对原生对象的原型进行修改,防止造成可能的意外结果。
看完前面这一大段,是不是觉得心很累?别担心,从 ES6 开始添加了 Class
语法糖,使开发者体验提升了很多。下面把前面的例子用 Class
重构一下:
class Person { constructor (name){ this.name = name } // 方法会自动放到 Person.prototype hello() { console.log(`Hello ${this.name}.`) } }class Engineer extends Person { constructor (name, skill){ super(name) // 调用 Person 的构造函数 this.skill = skill } }let alice = new Engineer('Alice', 'JavaScript') alice.hello() // Hello Alice.Object.getPrototypeOf(alice) // Person {constructor: ƒ}
很方便,同样的功能,代码的可读性却提高了不少,繁琐的设定也都能交给语法自动帮你完成。不过方便的语法背后,底层仍然是对象原型及原型链。
以上是 JavaScript 中关于对象原型的说明,希望能帮你理解对象原型,在这个什么都是对象的语言中,充分理解并掌握对象原型,是成为专业码农必须要突破的关卡之一。
更多编程相关知识,请访问:编程课程!!
Das obige ist der detaillierte Inhalt vonObjektprototypen und Prototypketten in JavaScript verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!