This article will introduce you to the object prototype and prototype chain in JavaScript. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to everyone.
I believe everyone has used it like this map
:
let arr = [0, 1, 2] let doubleArr = arr.map(c => c * 2) console.log(doubleArr) // 0, 2, 4
I don’t know if you have ever thought about it, arr
itself does not set the map
attribute, so why can you use the map
function?
Print it out and see:
console.log(arr) // 0: 0 // 1: 1 // 2: 2 // length: 3 // __proto__: Array(0)
An object named __proto__
appears. If you expand it again, you will see that all Array objects can be used. function; of course we can also find the map
function in it, and this is exactly the arr.map
function called in the example:
console.log(arr.map === arr.__proto__.map) // true
appears here The __proto__
object is the so-called prototype object (Prototype).
Different from Java, C# and other class-based object-oriented languages, properties and methods are transferred by defining classes, creating instances, specifying inheritance, etc.; Javascript is a Based on prototype (Prototype) language, through the pre-established prototype object, when a new object is created, specify which prototype object the object's prototype should refer to.
When we call a property or method of an object, if the object itself does not have this property or method, JavaScript will automatically look for the method in its prototype, which is why we can callarr.map# directly. ## without causing the error.
object still has the __proto__
attribute: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">console.log(arr.__proto__) // Array 的 Prototype
console.log(arr.__proto__.__proto__) // Object 的 Prototype
console.log(arr.__proto__.__proto__.__proto__) // null</pre><div class="contentsignin">Copy after login</div></div>
In the above mechanism, the prototype will be bound every time the object is created. Since the object has a prototype, the object prototype itself is also an object, and naturally it is no exception; from this example we can see:
null
; Through this mechanism, objects can use the properties and methods in the prototype, and rely on the prototype chain to inherit in sequence layer by layer, so that the object can have the functions of all prototypes on the prototype chain. This is the operating mechanism behind JavaScript objects. Supplement: In JavaScript, the end of almost every prototype chain will be Object, and finally points to
null.
Using prototypesFirst create a new object constructor:
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: ƒ}
The above example creates a simple object constructor
Person(), and sets Define object properties. In the method of the object, since the method does not need to have its own copy for each object to avoid redundant memory consumption, the method of the object should be used as in the previous example of Array.prototype.map
Set to the prototype object (Person.prototype
) so that all objects created by this constructor can share these methods. Finally, create a new Person
object and obtain the prototype of the newly generated object through getPrototypeOf(obj)
. Q: Why not directly use
to obtain the prototype object?A: Because although
__proto__
is supported by almost all browsers, it is a non-standard attribute; getting the prototype of the object throughgetPrototypeOf
is the correct way.Reminder:
Person.prototypeis not the prototype of
Prototype inheritancePerson
, but the prototype of the new object created after the constructor is executed; never usein the constructor ##prototype
The attributes are confused with the prototypeof the
object!
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: ƒ}
Create here Get the prototype of the new object
Engineer, and specify Engineer.prototype to make its prototype inherit from
Person.prototype, and finally reset
Engineer.prototype.constructor, let the constructor point back to itself; this completes the most basic prototype inheritance.
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 中关于对象原型的说明,希望能帮你理解对象原型,在这个什么都是对象的语言中,充分理解并掌握对象原型,是成为专业码农必须要突破的关卡之一。
更多编程相关知识,请访问:编程课程!!
The above is the detailed content of Understanding object prototypes and prototype chains in JavaScript. For more information, please follow other related articles on the PHP Chinese website!