ECMAScript, being a highly abstract object-oriented programming language, operates on objects. There are also primitives, but they, when required, are also converted into objects. An object is a collection of properties that also has a prototype object associated with it. The prototype is either an object, or null.In JavaScript, there are no familiar classes, but there are constructor functions that spawn objects according to certain algorithms (see Operator new).
var Grandfather = function () {}; // Grandfather Grandfather.prototype.color = 'green'; var Father = function () {}; // Father Father.prototype = new Grandfather(); // , var Son = function () {}; // Son Son.prototype = new Father(); // var u = new Grandfather(); // "" Grandfather var f = new Father(); // "" Father var s = new Son(); // "" Son // console.log([u.color, f.color, s.color]); // ["green", "green", "green"] // Grandfather.prototype.color = 'blue'; console.log([u.color, f.color, s.color]); // ["blue", "blue", "blue"] // Father.prototype.color = 'green'; // : // Grandfather.prototype.color = 'green'; console.log([u.color, f.color, s.color]); // ["blue", "green", "green"] // Grandfather.prototype.color = 'blue'; console.log([u.color, f.color, s.color]); // ["blue", "green", "green"] // s.color = 'black'; // , console.log([u.color, f.color, s.color]); // ["blue", "green", "black"] var SonsSon = function () {}; // SonsSon SonsSon.prototype = new Son(); // var ss = new SonsSon(); // "" SonsSon // console.log([u.color, f.color, s.color, ss.color]); // ["blue", "green", "black", "green"]
A prototype chain is a finite chain of objects that is used to organize inheritance and shared properties.
function getProperty(obj, prop) { if (obj.hasOwnProperty(prop)) return obj[prop] else if (obj.__proto__ !== null) return getProperty(obj.__proto__, prop) else return undefined }
var Point = { x: 0, y: 0, print: function () { console.log(this.x, this.y); } }; var p = {x: 10, __proto__: Point}; // 'x' : /* px */ getProperty(p, 'x'); // 10 // 'y' __proto__ - Point /* py */ getProperty(p, 'y'); // 0 // print __proto__ - Point /* p.print() */ getProperty(p, 'print').call(p); // 10 0
Why I used call
, but did not call the received function directly, is described below.Point
has another property, yes, this is our reference to the prototype of the parent __proto__
, which in the case of Point
points to Object.prototype
. /* SonsSon <- Son <---- Father <- Grandfather <-- Object <-- null */ console.log(ss.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__ === null);
function Point(x, y) { // Point this.x = x; this.y = y; } Point.prototype = { // print: function () { console.log(this.x, this.y); } }; var p = new Point(10, 20); // p.print(); // 10 20
new F(arguments...)
) and performs the following actions:__proto__
property that refers to F.prototype
F
constructor in which this
is the previously created objectNew
function that emulates the behavior of the new
operator: function New (F, args) { /*1*/ var n = {'__proto__': F.prototype}; /*2*/ F.apply(n, args); /*3*/ return n; }
function Point(x, y) { // Point this.x = x; this.y = y; } Point.prototype = { // print: function () { console.log(this.x, this.y); } }; var p1 = new Point(10, 20); p1.print(); // 10 20 console.log(p1 instanceof Point); // true // : var p2 = New(Point, [10, 20]); p2.print(); // 10 20 console.log(p2 instanceof Point); // true
Father.prototype = new Grandfather()
: var Grandfather = function () {}; // Grandfather Grandfather.prototype.color = 'green'; var Father = function () {}; // Father Father.prototype = new Grandfather(); // , var Son = function () {}; // Son Son.prototype = new Father(); //
new Grandfather()
: Father.prototype = { __proto__: { // Grandfather color: 'green', __proto__: Object.prototype } };
new Father()
we get the following object (we will immediately expand the object): Son.prototype = { __proto__: { // Father __proto__: { // Grandfather color: 'green', __proto__: Object.prototype } } }
{ color: 'black', // __proto__: { // Son __proto__: { // Father color: 'green', // __proto__: { // Grandfather color: 'blue', // __proto__: Object.prototype } } } }
Father.prototype = new Grandfather()
not the best way to build a chain of prototypes?Grandfather
constructor, which can mix extra properties and call extra methods, for example alert
. To work around this problem, use the fake constructor: function inherit (object, parent) { function F(){}; // F.prototype = parent.prototype; // object.prototype = new F(); // return object; // };
var Grandfather = function () {}; // Grandfather Grandfather.prototype.color = 'green'; var Father = function () {}; // Father inherit(Father, Grandfather); //
Grandfather
Designer will not be executed. If we all need to do the Grandfather constructor, then call it using call or appy. var Father = function () { // Father Grandfather.call(this); };
if (p instanceof Point) { // ... }
instanceof
operator is very closely related to chained prototypes. It uses exactly the prototype chain for rendering a verdict, and does not check whether the given object “p” is generated by the “Point” constructor. In this moment there is often confusion.instanceof
operator operates on two objects — obj and constructor: ( obj instanceof constructor
). Starting from constructor.prototype, it runs through a chain of prototypes and tests the following equality obj.__proto__ === constructor.prototype
, if it is true, then it returns true. function isInstanceOf(obj, constructor) { if (obj.__proto__ === constructor.prototype) return true; else if (obj.__proto__ !== null) return isInstanceOf(obj.__proto__, constructor) else return false }
function Point(x, y) { // Point this.x = x; this.y = y; } Point.prototype = { // print: function () { console.log(this.x, this.y); } }; var p = new Point(10, 20); // /* {} instanceof Object */ console.log(isInstanceOf({}, Object)); // true /* p instanceof Point */ console.log(isInstanceOf(p, Point)); // true /* p instanceof Object */ console.log(isInstanceOf(p, Object)); // true , Object (Point.__proto__ === Object.prototype) /* p instanceof Array */ console.log(isInstanceOf(p, Array)); // false , Array
Many are used to the fact that the this keyword in programming languages ​​is closely related to object-oriented programming, that is, it points to the current object generated by the constructor. In ECMAScript, this is not limited to the definition of the object being spawned.In JavaScript, the value of this is determined by the caller on the form of the call. The rule that determines what will be in this is (I will explain in simple terms):
new, call, apply, bind, with, try catch
), then the value of this will be the object that stands before the point to the left of the method name. var foo = { bar: function () { console.log(this); } }; var bar = foo.bar; bar(); // this === global (2) foo.bar(); // this === foo (1) (foo.bar)(); // this === foo (1) // - (foo.bar = foo.bar)(); // this === global (3) (false || foo.bar)(); // this === global (3) (foo.bar, foo.bar)(); // this === global (3) function foo() { function bar() { console.log(this); } bar(); // this === global (2) }
getProperty(p, 'print').call(p)
because of this rule I manually specified the value of this. Otherwise, the print function would receive as this - window.this: new, call, apply, bind, with, try catch
(everything is more or less clear with them, I will not touch it).Source: https://habr.com/ru/post/120193/
All Articles