DarthJS
@DarthJS

Где используются прототипы, наследование в JS приложениях?

Всем привет!
Немного наболело, пытаюсь продвинуться в профессиональном плане, но тут такое дело, на собеседованиях (AngularJS разработчика) очень часто спрашивают про
prototype, __proto__, inheritance, apply, call, bind
.
Я уже где-то года два работаю с AngularJS приложениями, как с большими существующими, так и самостоятельно с нуля развивал большие приложения, но никогда эти вещи не использовал, максимум apply, пару раз для arguments где-то в конфиге. А так получается учу для того, что чисто пройти собеседования.
Подскажите пожалуйста хоть на словах, может даже есть примеры подобных приложений на AgnularJS, Angular2, React, Vue, где используются
prototype, __proto__, inheritance, apply, call, bind
, особенно интересует наследование.
А то уже начинаю не понимать, зачем это спрашивать на собеседованиях и не использовать в проектах, будто для галочки, знаешь - молодец, берем!
Всем спасибо :)
  • Вопрос задан
  • 3214 просмотров
Пригласить эксперта
Ответы на вопрос 4
  • @MaxKorz
    Вы наверное шутите. Вы всегда используете prototype, когда используете классы
    class Car {
      constructor() {
        this.isRunning = false;
        this.engine = 'V8';
      }
      
      start() {
        this.isRunning = true;
      }
    }
    это синтаксический сахар над
    var Car = (function () {
        function Car() {
            this.isRunning = false;
            this.engine = 'V8';
        }
        Car.prototype.start = function () {
            this.isRunning = true;
        };
        return Car;
    }());


    bind нужен для передачи контекста. Самый банальный пример - стрелочные функции:
    this.setState({ loading: true });
    
    fetch('/').then(() => {
      this.setState({ loading: false });
    });
    это тоже самое, что
    this.setState({ loading: true });
    
    fetch('/').then(function() {
      this.setState({ loading: false });
    }.bind(this));

    пример из реального мира: https://jsfiddle.net/6L2wetcg/ при клике на кнопку alert выведет undefined, т.к. контекст вызова функции при клике отличается от того, в котором задается обработчик.
    Решение - использовать bind: https://jsfiddle.net/7u7qe61b/

    А как вы без наследования "два года" с angularJS приложениями работаете я не знаю.

    Банальный HttpService проверяющий авторизацию при запросах
    import {Injectable} from '@angular/core';
    import {Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers} from '@angular/http';
    import {Observable} from 'rxjs/Observable';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/catch';
    
    @Injectable()
    export class HttpService extends Http {
    
      constructor (backend: XHRBackend, options: RequestOptions) {
        let token = localStorage.getItem('auth_token'); // your custom token getter function here
        options.headers.set('Authorization', `Bearer ${token}`);
        super(backend, options);
      }
    
      request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
        let token = localStorage.getItem('auth_token');
        if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
          if (!options) {
            // let's make option object
            options = {headers: new Headers()};
          }
          options.headers.set('Authorization', `Bearer ${token}`);
        } else {
        // we have to add the token to the url object
          url.headers.set('Authorization', `Bearer ${token}`);
        }
        return super.request(url, options).catch(this.catchAuthError(this));
      }
    
      private catchAuthError (self: HttpService) {
        // we have to pass HttpService's own instance here as `self`
        return (res: Response) => {
          console.log(res);
          if (res.status === 401 || res.status === 403) {
            // if not authenticated
            console.log(res);
          }
          return Observable.throw(res);
        };
      }
    }

    Тем более, что весь react построен на наследовании от React.Component:
    export default class ComponentName extends React.Component


    Нужно коллекцию DOM элементов сделать массивом? Используем call
    var nodesArray = [].slice.call(document.querySelectorAll("div"));
    так же call используется для реализации наследования в es5 совместимом коде

    Нужно элегантно применить функцию к массиву? Используем apply
    var numbers = [1, 2, 3, 4];
    Math.max.apply(null, numbers) // 4
    Math.min.apply(null, numbers) // 1


    все эти вещи нужно знать не "для собеседования", а как раз чтобы продвинуться в профессиональном плане
    Ответ написан
  • yurakostin
    @yurakostin
    Front-end developer
    Здравствуйте.
    На самом деле всё проще.
    Не обижайтесь, но вы просто не так хорошо знаете сам javascript.

    1. prototype - ссылка на прототип объекта.
    Array.prototype, Number.prototype. В нём хранятся методы и свойства этого объекта, а также... (далее переходим к __proto__)

    2. __proto__ - тоже ссылка на прототип. Например, введите в консоли [] и раскройте ветвь. У вас всего два свойства. Одно - length - количество элементов в массиве. Другое - __proto__. А где же все методы, которые мы можем использовать с массивами filter, map, slice и так далее? Они лежат в __proto__. Более подробно здесь.

    3. inheritance соответственно - наследование. JS построен на прототипной парадигме (надеюсь, я тут не наврал). Array наследуется от Object. Это можно легко увидеть, посмотрев Array.prototype. Там вы увидите тот самый __proto__, являющийся ссылкой на Object.prototype. Вся инфа по ссылке выше.

    4, 5. call и apply постепенно уходят из обихода, но тем не менее про них важно знать и уметь ими пользоваться. Эти методы позволяют вызвать функцию в контексте, который вам необходим.

    Например вам нужно вызвать метод какого-то объекта, который работает с this в контексте другого объекта, у которого этого метода нет. Вы можете сделать следующее:
    var o_1 = {
    	name: 'Peter',
    	hello: function () {
    		console.log('Hello, ' + this.name);
    	}
    };
    
    var o_2 = {
    	name: 'Jane'
    };
    
    o_1.hello.call(o_2); // Фактически вы говорите "вызови метод такой-то для объекта такого-то"


    Для передачи аргументов в "заимствованную" функцию оба метода принимают аргументы, каждый по-своему.
    var o_1 = {
    	name: 'Peter',
    	hello: function () {
    		console.log('Hello, ' + this.name);
    	},
            sum: function (a, b) {
    		console.log(`${this.name} sum a and b to ${a + b}`);
    	}
    };
    
    var o_2 = {
    	name: 'Jane'
    };
    
    o_1.sum.call(o_2, 2, 4);
    o_1.sum.apply(o_2, [1, 2]);


    Отличие между этими двумя методами в том, как они принимают аргументы, которые попадут в функцию.
    call принимает список аргументов, начиная со второго, а apply, соответственно, принимает массив.

    Также, ничто не мешает вам вызвать функцию, которая не нуждается в контексте, для этого первым аргументом можно передать null.

    var o = {
    	sum: function (a, b) {
    		console.log(a + b);
    	}
    }
    o.sum.call(null, 1, 2);
    o.sum.apply(null, [1, 2]);


    Подробнее тут.

    6. bind тоже довольно простая штука. Отчасти он похож на предыдущие два метода, за исключением того, что он не вызывает функцию сразу же.
    Основная его задача - вернуть функцию, которая будет вызвана для нужного вам контекста.

    var o = {
    	a: 1,
    	b: 2,
    	sum: function () {
    		console.log(this.a + this.b);
    	}
    };
    
    var o2 = {
    	a: 10,
    	b: 20
    };
    
    var o2Sum = o.sum.bind(o2);
    
    o2Sum();


    Также с помощью bind можно каррировать функции.
    Всё есть здесь

    PS: надеюсь, код не содержит ошибок и я нигде не налажал и всё правильно рассказал.
    Ответ написан
  • @kulakoff
    Vue.js developing
    Если вы пользуетесь фреймворками, то они как правило, уже предлагают способы разбиения и инкапсулирования кода в модули, компоненты, диктуют принципы взаимодействия, наследования и т.д. И знание этих слов и их применение вам может понадобится не так часто) Но если вы захотите написать свой фреймворк или библиотеку, то без них вам не обойтись. Вообще это базовые конструкции js, не удивительно, что про них спрашивают)
    Ответ написан
  • @Arik
    как я понимаю: есть классы, есть методы класса, если просто задать методы классу, то будет копи-паст (грубо говоря) методов на каждый созданный объект, прототипное наследование это как бы общий буфер для методов/свойств. Выходит сколько бы объектов не создали, при обращении к методу/свойству все объекты будут ссылать в одно место, если конечно не переназначили метод/свойство у конкретного экземпляра класса. "apply, call, bind" целые статьи посвящают, по сути это просто вызов функции с возможность передать аргументы и смены контекста вызова
    Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы
Вакансии с Моего Круга Все вакансии
Заказы с Фрилансим Все заказы