Prototypes JS et faits peu connus

Introduction lyrique 



Ayant à nouveau reçu un tas de questions sur les prototypes lors de la prochaine interview, je me suis rendu compte que j'avais un peu oublié les subtilités du prototypage, et j'ai décidé de rafraîchir mes connaissances. Je suis tombé sur un tas d'articles qui ont été écrits soit sur l'inspiration de l'auteur, comment il "ressent" les prototypes, soit l'article portait sur une partie distincte du sujet et ne donnait pas une image complète de ce qui se passait. 



Il s'avère qu'il y a beaucoup de choses non évidentes de l'ancien temps d'ES5 et même d'ES6 dont je n'avais pas entendu parler. Il s'est également avéré que la sortie de la console du navigateur pouvait ne pas correspondre à la réalité.



Qu'est-ce qu'un prototype



L'objet dans JS a ses propres propriétés héritées, par exemple, dans ce code:



var foo = { bar: 1 };
foo.bar === 1 // true
typeof foo.toString === "function" // true


l'objet fooa sa propre propriété baravec une valeur 1, mais il a également d'autres propriétés telles que toString. Pour comprendre comment un objet fooobtient une nouvelle propriété toString, voyons en quoi consiste l'objet:





Le fait est qu'un objet a une référence à un autre objet prototype. Lors de l'accès à un champ foo.toString, une recherche d'une telle propriété est d'abord effectuée à partir de l'objet lui-même, puis à partir de son prototype, le prototype de son prototype, et ainsi de suite jusqu'à la fin de la chaîne de prototypes. C'est comme une liste d'objets liés individuellement, où l'objet et ses objets prototypes sont vérifiés tour à tour. C'est ainsi que l'héritage des propriétés est implémenté, par exemple (presque, mais plus à ce sujet plus tard) tout objet a des méthodes valueOfet toString.



 



, constructor __proto__. constructor -, , __proto__ ( null, ). ., .



constructor 



constructor – , : 



const a = {};
a.constructor === Object // true


, , : 



object.constructor(object.arg)


, , , . constructor , writable , , , .



 



, , JS . , , [[SlotName]]. [[Prototype]] - ( null, ).





- , [[Prototype]] JS , . , __proto__, , JS .



,



__proto__ [[Prototype]] Object.prototype:





- __proto__ . __proto__ , . __proto__ :



const foo = {};
foo.toString(); //  toString()   Object.prototype   '[object Object]',   
foo.__proto__ = null; //    null
foo.toString(); //      TypeError: foo.toString is not a function
foo.__proto__ = Object.prototype; //   
foo.toString(); //   ,  TypeError: foo.toString is not a function


? , __proto__ Object.prototype, foo. - Object.prototype, __proto__ .

. :





var baz = { test: "test" };
var foo = { bar: 1 };
foo.__proto__ = baz;


Chrome foo :





baz Object.prototype:



baz.__proto__ = null;


Chrome :





Object.prototype baz __proto__ undefined foo, Chrome __proto__ . [[Prototype]], __proto__, , .





: .



: __proto__ Object.setPrototypeOf.



var myProto = { name: "Jake" };
var foo = {};
Object.setPrototypeOf(foo, myProto);
foo.__proto__ = myProto;


, , .

[[Extensible]] , . , false : Object.freeze, Object.seal, Object.preventExtensions. :



const obj = {};
Object.preventExtensions(obj);
Object.setPrototypeOf(obj, Function.prototype); // TypeError: #<Object> is not extensible


. .

:



const foo = Object.create(myPrototype);


Object.create, __proto__:



const foo = { __proto__: myPrototype };


:



const f = function () {}
f.prototype = myPrototype;
const foo = new f();


new, . , new prototype , .. [[Prototype]], .





.



function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

const user = new Person('John', 'Doe');


Person , :





Person.prototype? , prototype (note 3), prototype , . , :



Person.prototype.fullName = function () {
    return this.firstName + ' ' + this.lastName;
}




user.fullName() "John Doe".



new 



new . new :



  1. self

  2. prototype self

  3. self this

  4. self ,



, new :



function custom_new(constructor, args) {
    // https://stackoverflow.com/questions/31538010/test-if-a-variable-is-a-primitive-rather-than-an-object
    function isPrimitive(val) {
        return val !== Object(val);
    }
    const self = Object.create({});
    const constructorValue = constructor.apply(self, args) || self;
    return isPrimitive(constructorValue) ? self : constructorValue;
}
custom_new(Person, ['John', 'Doe'])


ES6 new new.target, , new, :



function Foo() {
    console.log(new.target === Foo);
}
Foo(); // false
new Foo(); // true


new.target undefined , new;





, Student Person.



  1. Student Person

  2. `Student.prototype` `Person`

  3. `Student.prototype`



function Student(firstName, lastName, grade) {
    Person.call(this, firstName, lastName);
    this.grade = grade;
}

//  1
Student.prototype = Object.create(Person.prototype, {
    constructor: {
        value:Student,
        enumerable: false,
        writable: true
    }
});
//  2
Object.setPrototypeOf(Student.prototype, Person.prototype);

Student.prototype.isGraduated = function() {
    return this.grade === 0;
}

const student = new Student('Judy', 'Doe', 7);




( , .. this ), ( )

1 , .. Object.setPrototypeOf .



 



, , Person Student: 



class Person {
    constructor(firstName, lastName) {  
        this.firstName = firstName; 
        this.lastName = lastName;
    }

    fullName() {
        return this.firstName + ' ' + this.lastName;
    }
}

class Student extends Person {
    constructor(firstName, lastName, grade) {
        super(firstName, lastName);
        this.grade = grade;
    }

    isGraduated() {
        return this.grade === 0;
    }
}


, : 



  • , new





prototype .



P.S.



Il serait naïf d'attendre qu'un article réponde à toutes les questions. Si vous avez des questions intéressantes, des excursions dans l'histoire, des déclarations raisonnées ou sans fondement selon lesquelles j'ai tout fait de mal, ou des corrections d'erreurs, écrivez dans les commentaires. 




All Articles