LINUX.ORG.RU

Конструктор, одинаково работающий как с new, так и без

 


0

1

Начал изучать js, прочитал там по прототипное наследование, про конструкторы, про то, как создавать объекты. И я уже кучу раз забывал писать new перед использованием конструктора. И вот я подумал, а нельзя ли создать такую функцию конструктор, чтобы она хоть с new, хоть без него, работала одинаково?

Ответ на: комментарий от anonymous

Зачем это? Он только для работы с DOM полезен. И то если document.selectЧетотам возвращает прямо нужный элемент, то jquery возвращает какую-то хрень. В общем мне джейквери пока не нужен, и лень разбираться.

hlebushek ★★
() автор топика

Можно. Проверьте в функции, является ли объект инстансам самого себя. Если да, то было new. Если нет, то верните объект с new.

Ещё можно не забывать писать new. Потому что это логично.

lexazloy
()
Ответ на: комментарий от lexazloy

Можно. Проверьте в функции, является ли объект инстансам самого себя. Если да, то было new. Если нет, то верните объект с new.

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

hlebushek ★★
() автор топика

MyClass = function(arg){
  if(this.__proto__ !== MyClass.prototype) return new MyClass(arg)
  this.arg = arg
}


a = MyClass(1)
console.log(a)

А лучше не используй new вообще, используй Object.create, и вещи на его основе. New — это жаба клоунада, он не нужен.

newquestion
()
Ответ на: комментарий от newquestion

__proto__ это нестандартное св-во. Правильно так:

function A() {
  if (!(this instanceof A))
    return new A();
  // ...
}

А лучше не используй new вообще, используй Object.create, и вещи на его основе.

Вот когда Object.create будет иметь тот же перформансы, что и конструкторы и без перестроений hudden class'ов на каждый чих, тогда и поговорим.

New — это жаба клоунада

да.

loyd
()
Ответ на: комментарий от newquestion

И начни уже ставить var/let. Про твой фетиш мы уже знаем.

loyd
()
Ответ на: комментарий от loyd

У тебя проблемы возникнут при наследовании классов с instanseOf, придется его вручную каждый раз перезаписывать. Проще забить на него вообще. А прото, хоть и нестандартное свойство, оно поддерживается сейчас везде. Этот способ проще и надежней. А о перформансе надо думать в последнюю очередь вообще, только если с ним проблемы возникнут. На практике, до подобных оптимизаций, о которых ты говоришь, никогда дело не дойдет.

newquestion
()

Некоторые уже существующие в JS вещи так и делают.

Deleted
()
Ответ на: комментарий от Deleted

ie — not supported

opera — not supported

safary — not supported

нахрен не нужная фича. да и вообще, проблема высосана из пальца. когда ты пишешь код, ты думаешь, что ты пишешь, что он делает этот код? если да, то с какого хрена ты должен забывать ставить new?

newquestion
()
Ответ на: комментарий от newquestion

У тебя проблемы возникнут при наследовании классов с instanseOf, придется его вручную каждый раз перезаписывать.

Пф, как будто с __proto__ не возникнет. А в общем случае проблема красиво не решается, да и не надо. Потому что конструкторы должны оставаться конструкторами, а функции — функциями.

А прото, хоть и нестандартное свойство, оно поддерживается сейчас везде.

Расскажи это IE10- и всяким небольшим интерпретаторам вроде Duktape.

Этот способ проще.

Чем это он проще? Ага, __proto__ и .prototype смотрятся куда лучше, чем instanceof, ну-ну.

и надежней

лолщито?

MyClass = function(arg){
  if(this.__proto__ !== MyClass.prototype) return new MyClass(arg)
  this.arg = arg
}

var __proto__ = MyClass.prototype;

MyClass(10)  // arg утёк в глобал

> arg
> 10
Я уже молчу про то, что это не работает в use strict коде (напомню специально для тебя: сегодня это де факто необходимо).

loyd
()
Ответ на: комментарий от newquestion

В отличие от __proto__. new.target в стандарте, так что вопрос времени. А про забывчивость new никто и не спорит.

loyd
()
Ответ на: комментарий от loyd

__proto__ тоже в ES6. Но эта часть стандарта называется

Included in the (normative) annex for additional ECMAScript legacy features for Web browsers (note that the specification codifies what is already in implementations).

x3al ★★★★★
()
Ответ на: комментарий от loyd

А в общем случае проблема красиво не решается

она просто не возникает, если не использовать быдлостайл.

var __proto__ = MyClass.prototype;

если ты так сделаешь, у тебя все рассыпется. Никто так в здравом уме делать не будет. Я тоже могу кинуть синтетику, где твой *общепринятый способ* обосрется

MyClass = function(arg){
  if(!(this instanceof MyClass)) return new MyClass(arg)
  this.arg = arg
}

__proto__ = MyClass()

MyClass.call(__proto__, 10)

console.log(arg) // 10, петросян в недоумении
хреновому танцору всегда что-нибудь будет мешать, возможно даже яйца.

newquestion
()
Ответ на: комментарий от newquestion

Ну и что же более вероятно: что мой конструктор вызовут через .call вот таким образом (напомню, что конструкторы для call/apply вызывать надо иначе), или шутник какой-то сделает #define true false^W^W^W, __proto__ = 10; Ну и про strict уже написал, на этом /thread

loyd
()
Ответ на: комментарий от loyd

Я за то, чтобы не искать сложности, там где их нет. Если пишет человек, который плохо знаком с языком, он все равно найдет где обосраться. Поэтому, я за то, чтобы не использовать вообще этот классовый интерфейс, со всеми new instanseof и прочим говном. Сам Айк говорит, что это были неудачные решения, продиктованные маркетинговыми интересами. мне нравиться подход, принятый в Io


MyClass := Object clone
MySubClass := MyClass clone
myInstanсe := MySubClass clone

можно так и на JS писать. Вот к этому надо и стремиться — избавиться от мусора, а его наоборот тащат в язык, и форсят его использование.

newquestion
()
Ответ на: комментарий от newquestion

Справедливости ради, возможность субклассить ошибки — офигенна, ради одного этого можно пользовать es6 там, где оно нативно.

x3al ★★★★★
()
Ответ на: комментарий от newquestion

Io это, конечно, хорошо, я и сам когда-то болел идеей тотального Object.create() (точнее чего-то вроде Object::clone аля Io), вот только такая динамичность, на самом деле, нужна довольно редко и классическое классовое ООП структурированнее.

loyd
()
Ответ на: комментарий от x3al

Дык, субкласс ошибок и так неплохо работал (особенно с хромовским Error.captureStackTrace). А вот сабклассить всякие Array это да, неплохо.

loyd
()
Ответ на: комментарий от loyd

Конкретно Array же почти нигде нельзя, не? Но в любом случае это — классовое ООП.

В Firefox есть ещё catch (e if e instanceof WhateverError), с которым всё веселее, но это ничерта не стандарт (хотя можно сделать подобное в пределах стандарта).

x3al ★★★★★
()

Обычно делают так, если надо: https://github.com/nodejs/node/blob/master/lib/http.js#L51

Еще поставь eslint, он избавляет от кучи досадных ошибок. Например показывает пропущенные new, когда вызывают функцию у которой имя с большой буквы.

Vit ★★★★★
()
Ответ на: комментарий от x3al

Пока семантика классов в ES6 нигде не работает вроде. Но по стандарту, наследовать built-in теперь можно (ибо this создаётся теперь в конце цепочки, а не в операторе new).

В Firefox есть ещё catch

Ну, простой catch(e) с instanceof внутри, конечно, неплохо бы присахарить, но в целом работает же.

loyd
()
Ответ на: комментарий от x3al

Конкретно Array же почти нигде нельзя, не?

В Io можно это делать запросто, как 2 пальца обоссать, как любой другой объект. В JS тоже можно, если постараться, там есть кое-какие синтаксические ограничения.

newquestion
()
Ответ на: комментарий от newquestion
trueArr = [1];
arr.length = 0; 
trueArr.length = 0;
console.log(arr); // undefined Object { 0: 1, length: 0 }
console.log(trueArr); // undefined Array [  ]

Тебе объяснить, сколько всего завязано на length или сам в курсе?

x3al ★★★★★
()
Ответ на: комментарий от newquestion

Там твой наивный пример разбирается первым. Я экономлю своё и твоё время, а то ты сейчас начнёшь велосипеды писать, а оно не_нужно.

loyd
()
Ответ на: комментарий от newquestion

Тебе только что привели ссылку, где объясняется, почему это проблему решить невозможно в рамках ES5.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Просто переопредели length с помощью Object.defineProperty, назначь геттер и сеттер, с поведением, которое тебе нужно, или просто определи его как функцию. Но, на самом деле, оно и не так нужно. Эти экзотические вещи особо не используются. Базовый функционал работает.

newquestion
()
Ответ на: комментарий от newquestion

Эти экзотические вещи особо не используются.

То есть ты не знаешь, как работает javascript.

To determine from which position to start inserting elements into, push retrieves a value of array’s “length”.

Больше за тебя я не читаю ту ссылку. Там объясняется, почему и геттер/сеттер тоже не работают. Это базовый функционал.

x3al ★★★★★
()
Ответ на: комментарий от newquestion

Я бы всё-таки советовал по ссылочке-то пройтись и осведомиться, почему это сеттер/геттер для length это не решение проблемы.

loyd
()
Ответ на: комментарий от newquestion

Не работает и ты опять шлангуешь. Говорят же: прочитай.

MyArray = Object.create(Array.prototype)

MyArray.foo = "bar"

arr = Object.create(MyArray)
arr[100]=0;
arr.push(1);
console.log(arr) // Object { 0: 1, 100: 0, length: 1 }

</thread>

x3al ★★★★★
()
Ответ на: комментарий от newquestion

То есть ты просто не умеешь читать по-английски? Это объясняет, почему ты веришь русской википедии и отказываешься проходить по ссылке.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Чтобы определить, с какой позиции начинать вставку элементов, пуш переопределяет значение массива «length»

Что не так? речь о пуше ведь идет. пуш и поп работают нормально, если назначается что-то вроде arr.foo = «bar», length не меняется. Основной функционал работает. А то что ты тут показываешь, вставка в произвольную позицию, и прочее говно — это почти не используется.

newquestion
()
Ответ на: комментарий от x3al

Даже вот такие вот выкрутасы прокатывают.

arr["a"] = "b"
arr.push(1)
arr.push(1)

console.log(arr) 

//{ '0': 1, '1': 1, a: 'b', length: 2 }
числовые индексы сдвигаются влево, как и положено. Я не проверял, но думаю, почти все будет работать. А для твоей экзотики допишешь отдельный метод, делов то. Если она действительно нужна тебе. Покажи, кстати, юзкейс, зачем ты это используешь?

newquestion
()

И я уже кучу раз забывал писать new перед использованием конструктора

Это избирательно касается ключевого слова new или скобки, операторы, вызовы методов и прочее ты тоже забываешь?

ya-betmen ★★★★★
()
Ответ на: комментарий от x3al

Кстати, нашел я способ пофиксить твою проблему

MyArray = Object.create(Array.prototype)

MyArray.foo = "bar"

arr = Object.create(MyArray)

toRealArray = function(src){
  var newArr = []
  for(var i in src){
    if(!src.hasOwnProperty(i)) break
    newArr[i] = src[i]
  }
  newArr.__proto__ = src.__proto__
  return newArr
}

arr = toRealArray(arr)

arr[100] = 1

console.log(arr.length) 

//101

newquestion
()
Ответ на: комментарий от x3al

Еще проще

MyArray = []
MyArray.__proto__ = Array.prototype
MyArray.foo = "bar"

arr = []
arr.__proto__ = MyArray

arr.push(1)

console.log(arr, arr.length)
arr[3] = 1
console.log(arr, arr.length)

//[ 1 ] 1
//[ 1, , , 1 ] 4

Так вообще все работает искаропки.

</thread>

newquestion
()
Ответ на: комментарий от x3al

Все равно это можно навелосипедить при желании, только придется использовать конструкторы. Через theObject.constructor.prototype ты сможешь получить __proto__

newquestion
()
Ответ на: комментарий от newquestion

constructor тем более нет в ES5 и нет практически нигде за вычетом хрома42 и nightly жирнолиса.

x3al ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.