Используйте прототип JavaScript для функций или this.functionName = functionName [duplicate]

Согласно Рабочий проект W3C 19 ноября 2013 г. display не является анимационным свойством . К счастью, visibility является анимированным. Вы можете связать его переход с переходом непрозрачности ( JSFiddle ):

  • HTML:
    Foo
    
    
    
  • CSS:
    #foo {
        transition-property: visibility, opacity;
        transition-duration: 0s, 1s;
    }
    
    #foo.hidden {
        opacity: 0;
        visibility: hidden;
        transition-property: opacity, visibility;
        transition-duration: 1s, 0s;
        transition-delay: 0s, 1s;
    }
    
  • JavaScript для тестирования:
    var foo = document.getElementById('foo');
    
    document.getElementById('hide-button').onclick = function () {
        foo.className = 'hidden';
    };
    
    document.getElementById('show-button').onclick = function () {
        foo.className = '';
    };
    

Обратите внимание, что если вы просто сделаете ссылку прозрачной, не установив visibility: hidden, она останется кликабельной.

721
задан CRABOLO 10 October 2015 в 17:19
поделиться

14 ответов

Примеры имеют очень разные результаты.

Перед рассмотрением различий следует отметить следующее:

  • Прототип конструктора обеспечивает способ совместного использования методов и значений между экземплярами через личное свойство [[Prototype]] экземпляра.
  • Функция функции задана тем, как функция вызывается или с помощью bind (не обсуждается здесь). Если функция вызывается для объекта (например, myObj.method()), то этот внутри метода ссылается на объект. Где этот не задан вызовом или с помощью bind , он по умолчанию ссылается на глобальный объект (окно в браузере) или в строгом режиме, остается неопределенным.
  • JavaScript - это объектно-ориентированный язык, то есть все объекты, включая функции.

Итак, вот фрагменты, о которых идет речь:

var A = function () {
    this.x = function () {
        //do something
    };
};

В этом случае переменной A присваивается значение, которое является ссылкой на функцию. Когда эта функция вызывается с помощью A(), функция этой функции не задается вызовом, поэтому по умолчанию используется глобальный объект, и выражение this.x эффективно window.x. В результате ссылка на выражение функции с правой стороны назначается window.x.

В случае:

var A = function () { };
A.prototype.x = function () {
    //do something
};

происходит нечто совсем другое. В первой строке переменной A присваивается ссылка на функцию. В JavaScript все объекты функций имеют свойство prototype по умолчанию, поэтому нет никакого отдельного кода для создания объекта A.prototype .

Во втором line, A.prototype.x назначается ссылка на функцию. Это создаст свойство x , если оно не существует, или назначьте новое значение, если это произойдет. Таким образом, разница с первым примером заключается в том, какое свойство объекта x участвует в выражении.

Другой пример приведен ниже. Это похоже на первое (и может быть то, о чем вы хотели спросить):

var A = new function () {
    this.x = function () {
        //do something
    };
};

В этом примере оператор new был добавлен перед выражением функции, чтобы вызвать функцию как конструктор. При вызове с new функция этой функции установлена ​​для ссылки на новый объект, чье личное свойство [[Prototype]] установлено для ссылки на общедоступный прототип конструктора . Поэтому в операторе присваивания свойство x будет создано на этом новом объекте. Когда вызывается как конструктор, функция по умолчанию возвращает свой этот объект , поэтому нет необходимости в отдельном заявлении return this;.

Чтобы проверить, что A имеет свойство x :

console.log(A.x) // function () {
                 //   //do something
                 // };

Это необычное использование new , так как единственный способ ссылки на конструктор - через A.constructor . Это было бы гораздо чаще:

var A = function () {
    this.x = function () {
        //do something
    };
};
var a = new A();

Другой способ достижения аналогичного результата - использовать сразу вызываемое выражение функции:

var A = (function () {
    this.x = function () {
        //do something
    };
}());

В этом случае A присвоено возвращаемое значение вызова функции с правой стороны. Здесь опять же, поскольку этот не задан в вызове, он будет ссылаться на глобальный объект, а this.x - эффективно window.x. Поскольку функция ничего не возвращает, A будет иметь значение undefined.

Эти различия между этими двумя подходами также проявляются, если вы сериализуете и де-сериализуете свои объекты Javascript в / от JSON. Методы, определенные на прототипе объекта, не сериализуются при сериализации объекта, что может быть удобно, если, например, вы хотите сериализовать только части данных объекта, но не его методы:

var A = function () { 
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

Связанные Вопросы:

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

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

434
ответ дан 27 revs, 9 users 63% 25 August 2018 в 12:48
поделиться

Я знаю, что этому ответили до смерти, но я хотел бы показать фактический пример различий в скорости.

Здесь мы создаем 2 000 000 новых объектов с помощью метода print в Chrome. Мы сохраняем каждый объект в массиве. Помещение print на прототип занимает около 1/2.

10
ответ дан Arnav Aggarwal 25 August 2018 в 12:48
поделиться
121
ответ дан Community 25 August 2018 в 12:48
поделиться

Каждый объект связан с прототипом объекта. При попытке получить доступ к объекту, который не существует, JavaScript будет выглядеть в объекте прототипа объекта для этого свойства и вернуть его, если он существует.

Свойство prototype конструктора функции относится к объекту прототипа всех экземпляров, созданных с помощью этой функции при использовании new.


В первом примере вы добавляете свойство x в каждый экземпляр, созданный с помощью функции A.

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

Во втором примере вы добавляете свойство объекта-прототипа, к которому указывают все экземпляры, созданные с A.

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

В заключение, в В первом примере копия функции присваивается каждому экземпляру. Во втором примере одна копия функции используется всеми экземплярами.

17
ответ дан destoryer 25 August 2018 в 12:48
поделиться

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

Я собрал jsperf чтобы показать это. Существует огромное различие во времени, которое требуется для создания экземпляра класса, хотя это действительно актуально, если вы делаете множество экземпляров.

http://jsperf.com/functions-in- конструктор-против-прототипа

7
ответ дан Devgr 25 August 2018 в 12:48
поделиться

Позвольте мне дать более полный ответ, который я узнал во время учебного курса JavaScript.

В большинстве ответов упоминалась разница уже, т. е. когда прототипирование функции делится со всеми (будущими) экземплярами. В то время как объявление функции в классе создаст копию для каждого экземпляра.

В общем, нет правильного или неправильного, это больше зависит от вкуса или дизайнерского решения в зависимости от ваших требований. Прототипом, однако, является метод, который используется для разработки в объектно-ориентированном виде, как я надеюсь, вы увидите в конце этого ответа.

Вы показали два шаблона в своем вопросе. Я попытаюсь объяснить еще два и попытаться объяснить различия, если это уместно. Не стесняйтесь редактировать / продлевать. Во всех примерах речь идет о объекте автомобиля, который имеет местоположение и может перемещаться.

Рисунок декоратора объекта

Не уверен, что этот шаблон по-прежнему актуальен в настоящее время, но он существует. И это хорошо знать об этом. Вы просто передаете объект и свойство функции декоратора. Декоратор возвращает объект с помощью свойства и метода.

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

Функциональные классы

Функция в JavaScript является специализированным объектом. В дополнение к вызову функция может хранить свойства, подобные любому другому объекту.

В этом случае Car - это функция (также мыслящий объект), которая может быть вызвана, как вы привыкли делать. Он имеет свойство methods (которое является объектом с функцией move). Когда вызывается Car, вызывается функция extend, которая делает некоторую магию, и расширяет функцию Car (мыслящий объект) с помощью методов, определенных в methods.

В этом примере, хотя

Прототипные классы

Первые два шаблона позволяют обсуждать использование методов для определения общих методов или использования методов, которые являются определяемый inline в теле конструктора. В обоих случаях каждый экземпляр имеет свою собственную функцию move.

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

Однако есть одна интересная вещь: каждый объект prototype имеет свойство удобства constructor, которое указывает на (думаю, объект), к которому он присоединился.

Относительно последних трех строк:

В этом примере Car ссылки на объект prototype, который соединяется через constructor к самому Car, т. е. Car.prototype.constructor является Car. Это позволяет вам выяснить, какая функция конструктора построила определенный объект.

amy.constructor 's поиск завершен и, следовательно, делегирован Car.prototype, который имеет свойство конструктора. И поэтому amy.constructor есть Car.

Кроме того, amy является instanceof Car. Оператор instanceof работает, видя, что объект прототипа правого операнда (Car) можно найти где угодно в цепочке прототипов левого операнда (amy).

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

Некоторые разработчики могут быть смущены в начале. Пример ниже:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

Оператор instanceof возвращает false, поскольку прототип Dog не может быть найден нигде в цепочке прототипов fido. fido - простой объект, созданный с помощью литерала объекта, т. е. он просто делегирует Object.prototype.

Псевдоклассические шаблоны

Это действительно просто еще одна форма прототипа в упрощенной форме и более знакомы для тех, кто программирует на Java, например, поскольку он использует конструктор new.

Он делает то же самое, что и в прототипном шаблоне, это просто синтаксический саун-надвод прототип.

. Однако основное отличие заключается в том, что в механизмах JavaScript реализованы оптимизации, которые применяются только при использовании псевдоклассического шаблона. Подумайте о псевдоклассическом шаблоне, вероятно, более быстрой версии прототипа; объектные отношения в обоих примерах одинаковы.

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

Наконец, не должно быть слишком сложно понять, как может быть реализовано объектно-ориентированное программирование. Есть два раздела.

Один раздел, который определяет общие свойства / методы в прототипе (цепочке).

И еще один раздел, где вы помещаете определения, отличающие объекты друг от друга (переменная loc в примеры).

Это то, что позволяет применять такие понятия, как суперкласс или подкласс в JavaScript.

Не стесняйтесь добавлять или редактировать. Еще раз, я мог бы сделать это сообщество wiki возможно.

11
ответ дан Ely 25 August 2018 в 12:48
поделиться

Первый пример изменяет интерфейс только для этого объекта. Второй пример изменяет интерфейс для всех объектов этого класса.

25
ответ дан Glenn 25 August 2018 в 12:48
поделиться

Прототип - это шаблон класса; который применяется ко всем будущим его экземплярам. В то время как это конкретный экземпляр объекта.

11
ответ дан harropriiz 25 August 2018 в 12:48
поделиться

Как говорили другие, первая версия, используя «this», приводит к каждому экземпляру класса A, имеющему собственную независимую копию метода функции «x». Принимая во внимание, что использование «прототипа» будет означать, что каждый экземпляр класса A будет использовать одну и ту же копию метода «x».

Вот некоторый код, чтобы показать эту тонкую разницу:

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

Как отмечали другие, существуют разные причины выбора одного или другого метода. Мой образец просто предназначен, чтобы четко продемонстрировать разницу.

224
ответ дан kaiser 25 August 2018 в 12:48
поделиться

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

Причина использования первой формы является доступ к «частным членам». Например:

var A = function () {
    var private_var = ...;

    this.x = function () {
        return private_var;
    };

    this.setX = function (new_x) {
        private_var = new_x;
    };
};

Из-за правил определения javascript private_var доступен функции, назначенной this.x, но не вне объекта.

54
ответ дан Matthew Crumley 25 August 2018 в 12:48
поделиться

В чем разница? => Много.

Я думаю, версия this используется для включения инкапсуляции, т. Е. Скрытия данных. Это помогает манипулировать частными переменными.

Давайте посмотрим на следующий пример:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

Теперь структуру prototype можно применить следующим образом:

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

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

Давайте посмотрим на реализацию сейчас.

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

Надеюсь, что это поможет.

14
ответ дан oozzal 25 August 2018 в 12:48
поделиться

Конечная проблема с использованием this вместо prototype заключается в том, что при переопределении метода конструктор базового класса все равно будет ссылаться на переопределенный метод. Рассмотрим это:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

по сравнению с:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

Если вы считаете, что это не проблема, это зависит от того, можете ли вы жить без частных переменных и достаточно опытный, чтобы узнать утечку, когда вы ее видите. Кроме того, необходимость поставить логику конструктора после определения методов неудобна.

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

по сравнению с:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1
19
ответ дан tarkabak 25 August 2018 в 12:48
поделиться

Я считаю, что @Matthew Crumley прав. Они функционально , если не структурно, эквивалентны. Если вы используете Firebug для просмотра объектов, созданных с помощью new, вы можете видеть, что они одинаковы. Однако, мое предпочтение было бы следующим. Я предполагаю, что это просто похоже на то, к чему я привык в C # / Java. То есть, определите класс, определите поля, конструктор и методы.

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

EDIT Не означает, что область действия переменной была закрытой, я просто пытался проиллюстрировать, как я определите мои классы в javascript. Имя переменной было изменено, чтобы отразить это.

8
ответ дан tvanfosson 25 August 2018 в 12:48
поделиться

Подумайте о статически типизированном языке, вещи на prototype статичны, а вещи на this связаны с экземпляром.

3
ответ дан Wayou 25 August 2018 в 12:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: