Класс против статического метода в JavaScript - PullRequest
249 голосов
/ 08 октября 2011

Я знаю, что это будет работать:

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 работать,

  1. Foo.__proto__ = Foo.prototype
  2. Foo.talk = Foo.prototype.talk

Есть ли другие способы сделать это?Я не знаю, правильно ли это делать.Используете ли вы методы класса или статические методы в вашем коде JavaScript?

Ответы [ 14 ]

380 голосов
/ 08 октября 2011

Прежде всего, помните, что JavaScript - это в основном язык прототипа , а не язык на основе классов 1 .Foo это не класс, это функция, которая является объектом.Вы можете создать экземпляр объекта из этой функции, используя ключевое слово new, которое позволит вам создать нечто похожее на класс на стандартном языке ООП.

Я бы предложил игнорировать __proto__ большую часть времени, потому что он плохо поддерживает кросс-браузер, и вместо этого сосредоточиться на изучении того, как работает prototype.

Если у вас есть экземпляр объекта, созданный из функции 2 и вы получаете доступ к одному из его членов (методы, атрибуты, свойства, константы и т. д.) любым способом, доступ будет проходить по иерархии прототипа, пока он либо (а) не найдет элемент, либо (б) не найдет другой прототип.

Иерархия начинается с вызываемого объекта, а затем ищет его объект-прототип.Если у объекта-прототипа есть прототип, он повторяется, если прототипа не существует, возвращается undefined.

Например:

foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"

foo = {};
console.log(foo.bar); // logs undefined

function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "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(){...} как определение открытого статического method для класса.


В 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, логические значения, числа и строки - технически не являются объектами, поскольку они являются низкоуровневыми реализациями языка.Булевы числа, числа и строки по-прежнему взаимодействуют с цепочкой прототипов, как будто они являются объектами, поэтому для целей этого ответа проще считать их «объектами», даже если они не совсем.

67 голосов
/ 05 мая 2014

Вы можете достичь этого, как показано ниже:

function Foo() {};

Foo.talk = function() { alert('I am talking.'); };

Теперь вы можете вызвать функцию «разговора», как показано ниже:

Foo.talk();

Вы можете сделать это, потому что в JavaScript функции также являются объектами. "zzzzBov" также ответил на это, но это длинное чтение.

36 голосов
/ 12 сентября 2013

Вызов статического метода из экземпляра:

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

31 голосов
/ 02 февраля 2015

Вот хороший пример, чтобы продемонстрировать, как 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
26 голосов
/ 16 марта 2016

Кроме того, теперь можно делать с 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'
11 голосов
/ 14 октября 2015

Я использую пространства имен:

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();
6 голосов
/ 07 июля 2016

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";
    }

}
3 голосов
/ 29 марта 2017

Если вам нужно написать статические методы в 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/

2 голосов
/ 09 августа 2018

Просто дополнительные заметки. Используя класс 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-in для проверки объекта

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
);
1 голос
/ 05 марта 2017

Когда вы пытаетесь вызвать Foo.talk, JS пытается найти функцию от talk до __proto__ и, конечно, она не может быть найдена.

Foo.__proto__ - это Function.prototype.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...