someserj, смотрите, если мы добавим в прототип С метод и попробуем его вызвать на объекте созданном конструктором D, то получим ошибку.
Строка: D.prototype = Object.create(C.prototype);
делает копию прототипа C и пишет его в прототип D. Копия потому, что если мы захотим расширить D.prototype без копирования, расширится C.prototype. После добавления этой строки вызов obj.foo() будет работать.
Вторая строка: D.prototype.constructor = D;
просто подменяет ссылку на конструктор. Чтобы у созданных объектов была правильная ссылка на конструтор.
someserj, лучше в прототип. Но можно и внутри. Смотрите пример с прототипом. Тут мы можем легко подменить значение counter.value в коде. Пример с методами в конструкторе. Тут value инкапсулирована, она является переменной в окружении(замыкании) созданном при вызове конструктора и недоступна из объекта-экземпляра. Ее подменить невозможно.
Другое дело, что редко когда такое может понадобиться, да и главный недостаток в том, что копии методов не берутся из прототипа, а создаются в каждом экземпляре.
Объявлять методы в конструкторе целесообразно только для инкапсуляции значений и других методов. Во всех других случаях лучше выносить в прототип.
someserj, как работает? Взгляните в консоль. Последний вывод. При использовании var значение инкапсулировано(находится в замыкании) и строка: counter.value = 100500;
его не переопределяет. При использовании this переопределяет.