Класс против статического метода в JavaScript
Я знаю, что это будет работать:
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?
15 ответов
Прежде всего, помните, что JavaScript - это в первую очередь прототипный язык, а не язык на основе классов 1. Foo
это не класс, это функция, которая является объектом. Вы можете создать экземпляр объекта из этой функции, используя new
ключевое слово, которое позволит вам создать нечто похожее на класс на стандартном языке ООП.
Я бы предложил игнорировать __proto__
большую часть времени, потому что он имеет плохую поддержку кросс-браузер, и вместо этого сосредоточиться на изучении того, как prototype
работает.
Если у вас есть экземпляр объекта, созданный из функции 2, и вы обращаетесь к одному из его членов (методы, атрибуты, свойства, константы и т. Д.) Любым способом, доступ будет течь вниз по иерархии прототипа, пока он (а) не найдет участник, или (b) не находит другого прототипа.
Иерархия начинается с вызываемого объекта, а затем ищет его объект-прототип. Если у объекта-прототипа есть прототип, он повторяется, если прототипа не существует, undefined
возвращается
Например:
foo = {bar: 'baz'};
alert(foo.bar); //alerts "baz"
foo = {};
alert(foo.bar); //alerts undefined
function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
alert(f.bar);
//alerts "baz" because the object f doesn't have an attribute "bar"
//so it checks the prototype
f.bar = 'buzz';
alert( f.bar ); //alerts "buzz" because f has an attribute "bar" set
Мне кажется, что вы, по крайней мере, в какой-то мере уже поняли эти "основные" части, но я должен сделать их явными, чтобы быть уверенным.
В JavaScript все является объектом 3.
все является объектом.
function Foo(){}
не просто определяет новую функцию, она определяет новый объект функции, к которому можно получить доступ, используя Foo
,
Вот почему вы можете получить доступ Foo
прототип с Foo.prototype
,
Что вы также можете сделать, это установить больше функций на Foo
:
Foo.talk = function () {
alert('hello world!');
};
Эта новая функция может быть доступна с помощью:
Foo.talk();
Я надеюсь, что к настоящему времени вы заметили сходство функций в функциональном объекте и статическом методе.
Думать о f = new Foo();
как создание экземпляра класса, Foo.prototype.bar = function(){...}
как определение общего метода для класса, и Foo.baz = function(){...}
как определение публичного статического метода для класса.
ECMAScript 2015 представил множество синтаксических символов для этих видов объявлений, чтобы их было проще реализовать, а также было легче читать. Предыдущий пример может быть записан как:
class Foo {
bar() {...}
static baz() {...}
}
который позволяет bar
называться как:
const f = new Foo()
f.bar()
а также baz
называться как:
Foo.baz()
1: class
был "Future Reserved Word" в спецификации ECMAScript 5, но ES6 предоставляет возможность определять классы с использованием class
ключевое слово.
2: по сути, экземпляр класса, созданный конструктором, но есть много нюансов, которые я не хочу вводить в заблуждение
3: примитивные значения - которые включают undefined
, null
, логические значения, числа и строки - технически не объекты, потому что они являются низкоуровневыми реализациями языка. Булевы числа, числа и строки по-прежнему взаимодействуют с цепочкой прототипов, как если бы они были объектами, поэтому для целей этого ответа проще считать их "объектами", даже если они не совсем.
Вы можете достичь этого, как показано ниже:
function Foo() {};
Foo.talk = function() { alert('I am talking.'); };
Теперь вы можете вызвать функцию "talk", как показано ниже:
Foo.talk();
Вы можете сделать это, потому что в JavaScript функции также являются объектами. "zzzzBov" также ответил на это, но это длительное чтение.
Вызов статического метода из экземпляра:
function Clazz() {};
Clazz.staticMethod = function() {
alert('STATIC!!!');
};
Clazz.prototype.func = function() {
this.constructor.staticMethod();
}
var obj = new Clazz();
obj.func(); // <- Alert's "STATIC!!!"
Проект простого класса Javascript: https://github.com/reduardo7/sjsClass
Вот хороший пример, чтобы продемонстрировать, как Javascript работает со статическими / экземплярами переменных и методов.
function Animal(name) {
Animal.count = Animal.count+1||1;// static variables, use function name "Animal"
this.name = name; //instance variable, using "this"
}
Animal.showCount = function () {//static method
alert(Animal.count)
}
Animal.prototype.showName=function(){//instance method
alert(this.name);
}
var mouse = new Animal("Mickey");
var elephant = new Animal("Haddoop");
Animal.showCount(); // static method, count=2
mouse.showName();//instance method, alert "Mickey"
mouse.showCount();//Error!! mouse.showCount is not a function, which is different from Java
Кроме того, теперь это можно сделать с class
а также static
'use strict'
class Foo {
static talk() {
console.log('talk')
};
speak() {
console.log('speak')
};
};
дам
var a = new Foo();
Foo.talk(); // 'talk'
a.talk(); // err 'is not a function'
a.speak(); // 'speak'
Foo.speak(); // err 'is not a function'
Я использую пространства имен:
var Foo = {
element: document.getElementById("id-here"),
Talk: function(message) {
alert("talking..." + message);
},
ChangeElement: function() {
this.element.style.color = "red";
}
};
И использовать это:
Foo.Talk("Testing");
Или же
Foo.ChangeElement();
ES6 поддерживает сейчас class
& static
ключевые слова, как шарм:
class Foo {
constructor() {}
talk() {
console.log("i am not static");
}
static saying() {
console.log(this.speech);
}
static get speech() {
return "i am static method";
}
}
Если вам нужно написать статические методы в ES5, я нашел для этого отличный учебник:
//Constructor
var Person = function (name, age){
//private properties
var priv = {};
//Public properties
this.name = name;
this.age = age;
//Public methods
this.sayHi = function(){
alert('hello');
}
}
// A static method; this method only
// exists on the class and doesn't exist
// on child objects
Person.sayName = function() {
alert("I am a Person object ;)");
};
смотрите @ https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/
Просто дополнительные заметки. Используя класс ES6, Когда мы создаем статические методы... движок Javacsript устанавливает атрибут дескриптора немного, отличный от "статического" метода старой школы
function Car() {
}
Car.brand = function() {
console.log('Honda');
}
console.log(
Object.getOwnPropertyDescriptors(Car)
);
он устанавливает внутренний атрибут (свойство дескриптора) для brand() в
..
brand: [object Object] {
configurable: true,
enumerable: true,
value: ..
writable: true
}
..
по сравнению с
class Car2 {
static brand() {
console.log('Honda');
}
}
console.log(
Object.getOwnPropertyDescriptors(Car2)
);
который устанавливает внутренний атрибут для brand() в
..
brand: [object Object] {
configurable: true,
enumerable: false,
value:..
writable: true
}
..
Посмотрите, что для перечисляемого установлено значение false для статического метода в ES6.
это означает, что вы не можете использовать цикл for для проверки объекта
for (let prop in Car) {
console.log(prop); // brand
}
for (let prop in Car2) {
console.log(prop); // nothing here
}
статический метод в ES6 рассматривается как личное свойство класса другого (имя, длина, конструктор), за исключением того, что статический метод все еще доступен для записи, поэтому дескриптор доступный для записи установлен в значение true { writable: true }
, это также означает, что мы можем переопределить его
Car2.brand = function() {
console.log('Toyota');
};
console.log(
Car2.brand() // is now changed to toyota
);
Когда вы пытаетесь позвонить Foo.talk
JS пытается найти функцию talk
через __proto__
и, конечно, это не может быть найдено.
Foo.__proto__
является Function.prototype
,
Вызовы статических методов выполняются непосредственно в классе и не могут быть вызваны в экземплярах класса. Статические методы часто используются для создания функции полезности
Довольно четкое описание
Взято непосредственно с mozilla.org
Foo должен быть привязан к вашему классу. Затем, когда вы создаете новый экземпляр, вы можете вызвать myNewInstance.foo(). Если вы импортируете свой класс, вы можете вызвать статический метод.
Javascript не имеет реальных классов, скорее он использует систему наследования прототипов, в которой объекты "наследуются" от других объектов через цепочку их прототипов. Это лучше всего объяснить с помощью самого кода:
function Foo() {};
// creates a new function object
Foo.prototype.talk = function () {
console.log('hello~\n');
};
// put a new function (object) on the prototype (object) of the Foo function object
var a = new Foo;
// When foo is created using the new keyword it automatically has a reference
// to the prototype property of the Foo function
// We can show this with the following code
console.log(Object.getPrototypeOf(a) === Foo.prototype);
a.talk(); // 'hello~\n'
// When the talk method is invoked it will first look on the object a for the talk method,
// when this is not present it will look on the prototype of a (i.e. Foo.prototype)
// When you want to call
// Foo.talk();
// this will not work because you haven't put the talk() property on the Foo
// function object. Rather it is located on the prototype property of Foo.
// We could make it work like this:
Foo.sayhi = function () {
console.log('hello there');
};
Foo.sayhi();
// This works now. However it will not be present on the prototype chain
// of objects we create out of Foo
В вашем случае, если вы хотите Foo.talk()
:
function Foo() {};
// But use Foo.talk would be inefficient
Foo.talk = function () {
alert('hello~\n');
};
Foo.talk(); // 'hello~\n'
Но это неэффективный способ реализовать, используя prototype
лучше.
Другой способ, Мой путь определяется как статический класс:
var Foo = new function() {
this.talk = function () {
alert('hello~\n');
};
};
Foo.talk(); // 'hello~\n'
Выше статического класса не нужно использовать prototype
потому что он будет построен только один раз как статическое использование.
https://github.com/yidas/js-design-patterns/tree/master/class
Когда я столкнулся с такой ситуацией, я сделал что-то вроде этого:
Logger = {
info: function (message, tag) {
var fullMessage = '';
fullMessage = this._getFormatedMessage(message, tag);
if (loggerEnabled) {
console.log(fullMessage);
}
},
warning: function (message, tag) {
var fullMessage = '';
fullMessage = this._getFormatedMessage(message, tag);
if (loggerEnabled) {
console.warn(fullMessage);`enter code here`
}
},
_getFormatedMessage: function () {}
};
так что теперь я могу вызвать метод информации как Logger.info("my Msg", "Tag");
Существуют древовидные способы реализации методов и свойств для объектов функций или классов, а также для их экземпляров.
- В самом классе (или функции):
Foo.method()
или жеFoo.prop
. Это статические методы или свойства - О его прототипе :
Foo.prototype.method()
или жеFoo.prototype.prop
. При создании экземпляры унаследуют этот объект через прототип, который{method:function(){...}, prop:...}
. Таким образом, объект foo получит в качестве прототипа копию объекта Foo.prototype . - На самом экземпляре : метод или свойство добавляется к самому объекту.
foo={method:function(){...}, prop:...}
Ключевое слово будет представлять и действовать по-разному в зависимости от контекста. В статическом методе он будет представлять сам класс (ведьма в конце концов является экземпляром Function:
class Foo {}
вполне эквивалентен
let Foo = new Function({})
В ECMAScript 2015, который сегодня кажется хорошо реализованным, становится яснее увидеть разницу между методами и свойствами класса ( статическими ), методами и свойствами экземпляра и собственными методами и свойствами. Таким образом, вы можете создать три метода или свойства с одинаковым именем, но разными, поскольку они применяются к разным объектам,
this
Ключевое слово в методах будет применяться, соответственно, к самому объекту класса и объекту-экземпляру, прототипу или самому себе.
class Foo {
constructor(){super();}
static prop = "I am static" // see 1.
static method(str) {alert("static method"+str+" :"+this.prop)} // see 1.
prop="I am of an instance"; // see 2.
method(str) {alert("instance method"+str+" : "+this.prop)} // see 2.
}
var foo= new Foo();
foo.prop = "I am of own"; // see 3.
foo.func = function(str){alert("own method" + str + this.prop)} // see 3.