Я знаю, что это будет работать:
function Foo() {};
Foo.prototype.talk = function () {
alert('hello~\n');
};
var a = new Foo;
a.talk(); // 'hello~\n'
Но если я хочу позвонить
Foo.talk() // this will not work
Foo.prototype.talk() // this works correctly
Я нахожу несколько способов заставить Foo.talk
работать,
Foo.__proto__ = Foo.prototype
Foo.talk = Foo.prototype.talk
Есть ли другие способы сделать это? Я не знаю, правильно ли это делать. Используете ли вы методы класса или статические методы в вашем коде JavaScript?
javascript
oop
lostyzd
источник
источник
Foo.talk = function ...
Foo.walk = function() {}
не повлияет на его экземпляры, так как он не входит в цепочку прототипов. Существует ли кросс-браузерный метод для[[prototype]]
указания функции на нееprototype
?this
указатель.Ответы:
Прежде всего, помните, что JavaScript - это в первую очередь прототипный язык , а не язык на основе классов 1 .
Foo
это не класс, это функция, которая является объектом. Вы можете создать экземпляр объекта из этой функции, используяnew
ключевое слово, которое позволит вам создать нечто похожее на класс на стандартном языке ООП.Я бы посоветовал игнорировать
__proto__
большую часть времени, потому что он плохо поддерживает кросс-браузер, и вместо этого сосредоточиться на изучении того, какprototype
работаетЕсли у вас есть экземпляр объекта, созданный из функции 2, и вы обращаетесь к одному из его членов (методы, атрибуты, свойства, константы и т. Д.) Любым способом, доступ будет течь вниз по иерархии прототипа, пока он (а) не найдет участник, или (b) не находит другого прототипа.
Иерархия начинается с объекта, который был вызван, а затем ищет его объект-прототип. Если объект-прототип имеет прототип, он повторяется, если прототип не существует,
undefined
возвращается.Например:
Мне кажется, что вы, по крайней мере, в некоторой степени уже поняли эти "основные" части, но я должен сделать их явными, чтобы быть уверенным
В JavaScript все является объектом 3 .
все является объектом.
function Foo(){}
не просто определяет новую функцию, она определяет новый объект функции, к которому можно получить доступ, используяFoo
.Вот почему вы можете получить доступ
Foo
к прототипу с помощьюFoo.prototype
.То , что вы также можете сделать , это установить дополнительные функции по
Foo
:Эта новая функция может быть доступна с помощью:
Я надеюсь, что к настоящему времени вы заметили сходство функций в функциональном объекте и статическом методе.
Представьте,
f = new Foo();
что вы создаете экземпляр класса,Foo.prototype.bar = function(){...}
определяете общий метод для класса иFoo.baz = function(){...}
определяете открытый статический метод для класса.ECMAScript 2015 представил множество синтаксических символов для этих видов объявлений, чтобы их было проще реализовать, а также было легче читать. Поэтому предыдущий пример можно записать так:
который позволяет
bar
называться:и
baz
называться как:1:
class
было «Future Reserved Word» в спецификации ECMAScript 5 , но ES6 вводит возможность определять классы, используяclass
ключевое слово.2: по сути, экземпляр класса, созданный конструктором, но есть много нюансов, которые я не хочу вводить в заблуждение
3: примитивные значения, которые включают
undefined
,null
логические, числа и строки, технически не являются объектами, потому что они являются низкоуровневыми реализациями языка. Булевы числа, числа и строки по-прежнему взаимодействуют с цепочкой прототипов, как если бы они были объектами, поэтому для целей этого ответа легче считать их «объектами», даже если они не совсем.источник
Foo.talk()
. Вы можете назначить это в конструкторе, если хотите:this.talk = Foo.talk
- или, как вы заметили, присваиваяFoo.prototype.talk = Foo.talk
. Но я не уверен, что это хорошая идея - в принципе, методы экземпляра должны быть специфическими для экземпляра.Foo.talk()
просто вызывает функцию пространства имен. Вы бы использовали его в ситуациях, подобных тому, как статические методы вызываются в языках ООП, таких как Java / C #. Хорошим примером варианта использования будет функция, подобнаяArray.isArray()
.Foo.talk = function ()...
не будет доступно для подклассов на собственном имени класса. Это можно обойти, «расширив» подклассы, но я также все еще ищу более элегантный способ.Вы можете достичь этого, как показано ниже:
Теперь вы можете вызвать функцию «talk», как показано ниже:
Вы можете сделать это, потому что в JavaScript функции также являются объектами.
источник
Вызов статического метода из экземпляра:
Проект простого класса Javascript: https://github.com/reduardo7/sjsClass
источник
Clazz.staticMethod
но он показывает вам, как связать эти статические методы из экземпляра объекта. Это особенно полезно в таких средах, как Node.js, где при использовании require у вас может не быть прямого доступа к исходному конструктору. Единственное, что я хотел бы добавить,this.constructor.staticMethod.apply(this, arguments);
constructor: (a) -> @constructor.add @
(ну, почти, во всяком случае)Вот хороший пример, чтобы продемонстрировать, как Javascript работает с переменными и методами static / instance.
источник
this
.this
ключевому словуКроме того, теперь можно делать
class
и сstatic
дам
источник
a.talk()
не работает. Принятый ответ говорит, что цепочка прототипов должна найти его, верно? Но это не тот случайЯ использую пространства имен:
И использовать это:
Или
источник
ES6 поддерживает сейчас
class
иstatic
ключевые слова, такие как очарование:источник
Если вам нужно написать статические методы в ES5, я нашел для этого отличный учебник:
см. @ https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/
источник
Просто дополнительные заметки. Используя класс ES6, Когда мы создаем статические методы ... движок Javacsript устанавливает атрибут дескриптора немного, отличный от "статического" метода старой школы
он устанавливает внутренний атрибут (свойство дескриптора) для brand () в
по сравнению с
который устанавливает внутренний атрибут для brand () в
Посмотрите, что для перечисляемого установлено значение false для статического метода в ES6.
это означает, что вы не можете использовать цикл for для проверки объекта
Статический метод в ES6 обрабатывается как личное свойство другого класса (имя, длина, конструктор), за исключением того, что статический метод все еще доступен для записи, поэтому дескриптор доступный для записи установлен в значение true
{ writable: true }
. это также означает, что мы можем переопределить егоисточник
Когда вы пытаетесь вызвать
Foo.talk
, то JS пытается найти функциюtalk
через__proto__
и, конечно же , он не может быть найден.Foo.__proto__
естьFunction.prototype
.источник
Вызовы статических методов выполняются непосредственно в классе и не могут быть вызваны в экземплярах класса. Статические методы часто используются для создания функции полезности
Довольно четкое описание
Взято непосредственно с mozilla.org
Foo должен быть привязан к вашему классу. Затем, когда вы создаете новый экземпляр, вы можете вызвать myNewInstance.foo (). Если вы импортируете свой класс, вы можете вызвать статический метод.
источник
Когда я столкнулся с такой ситуацией, я сделал что-то вроде этого:
так что теперь я могу вызвать метод информации как
Logger.info("my Msg", "Tag");
источник
В вашем случае, если вы хотите
Foo.talk()
:Но это неэффективный способ реализации, использование
prototype
лучше.Другой способ, Мой путь определяется как статический класс:
Выше статического класса не нужно использовать,
prototype
потому что он будет создан только один раз как статическое использование.источник
Javascript не имеет реальных классов, скорее он использует систему наследования прототипов, в которой объекты «наследуются» от других объектов через их цепочку прототипов. Это лучше всего объяснить с помощью самого кода:
источник