Свойства объектов функции Javascript
У меня есть объект функции JavaScript как:
var addNum = function(num1, num2) {
return num1 + num2;
}
Теперь, если я попытаюсь получить доступ к
addNum.divide()
Я хотел понять цепочку прототипов для вышеуказанного кода. Я прочитал, что в приведенном выше примере addNum будет искать divide(), а затем Function.prototype и, наконец, Object.prototype.
Но мой вопрос в приведенном выше примере, как можно добавить addNum для divide()
Относится ли это к чему-то вроде:
var addNum = function(num1, num2) {
this.divide = function(){}
return num1 + num2;
}
Я не мог понять строку, где он говорит, что addNum будет искать divide()
Пожалуйста, помогите мне понять то же самое.
Ответы
Ответ 1
Я не уверен, что это ответит на ваш вопрос, но может дать вам некоторое представление. Рассмотрим следующий пример:
var Person = (function () {
var Person = function (name) {
this.name = name;
}
Person.greet = function () {
console.log("Hello!");
}
Person.prototype = {
greet: function () {
console.log('Hello, my name is ' + this.name);
}
};
return Person;
})();
var bob = new Person("Bob");
Person.greet(); // logs "Hello!"
bob.greet(); // logs "Hello, my name is Bob
Объект функции "Лицо" имеет прямое свойство "greet", которое является функцией. ООП-мудрый, вы можете почти думать об этом как о статическом методе, который можно вызвать непосредственно из функции Person (Person.greet()). После того как вы "экземпляр" человек объект из конструктора Person, что новый объект "боб" в настоящее время ссылается на нее методы от объекта Person.prototype. Теперь, когда вы вызываете bob.greet(), он использует функцию greet в объекте прототипа.
Надеюсь, что это поможет.
Ответ 2
Как вы сами так говорите: у вас есть функция object. Функции - это объекты в JS, как и литералы Object, массивы или что-то еще: функции могут быть присвоены свойства и методы по желанию:
var someAnonFunction = function(foo)
{
console.log(this);
console.log(this === someAnonFunction);//will be false most of the time
};
someAnonFunction.x = 123;//assign property
someAnonFunction.y = 312;
someAnonFunction.divide = function()
{
console.log(this === someAnonFunction);//will be true most of the time
return this.x/this.y;//divide properties x & y
};
someAnonFunction.divide();
В этом случае объекту функции, на который ссылается someAnonFunction
, была назначена ссылка на анонимную функцию, называемую divide
(ну, в любом случае ссылка на анонимную функцию называлась делением). Таким образом, здесь нет никакого прототипа. Имейте в виду, как вы сами так говорите: все объекты можно проследить до Object.prototype
, просто попробуйте это:
console.log(someAnonFunction.toString === Function.prototype.toString);//functions are stringified differently than object literals
console.log(someAnonFunction.hasOwnProperty === Object.prototype.hasOwnProperty);//true
Или, возможно, это более ясно: простая схема того, как вызов метода/свойства разрешен к значению в JS:
[ F.divide ]<=========================================================\ \
F[divide] ===> JS checks instance for property divide | |
/\ || | |
|| || --> property found @instance, return value-------------------------------| |
|| || | |
|| ===========> Function.prototype.divide could not be found, check prototype | |
|| || | |
|| ||--> property found @Function.prototype, return-----------------------| |
|| || | |
|| ==========> Object.prototype.divide: not found check prototype? | |
|| || | |
|| ||--> property found @Object.prototype, return---------------------|_|
|| || |=|
|| =======>prototype is null, return "undefined.divide"~~~~~~~~~~~~~~~|X|
|| \ /
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~< TypeError can't read property 'x' of undefined
Следовательно, если вы хотите, чтобы вышеприведенный код работал с использованием прототипов, вам придется увеличить прототип рода (в этом случае Function.prototype
). Знайте, что это не рекомендуется, ведь изменение "родных" прототипов часто не одобряется. Тем не менее:
Function.prototype.divide = function (a, b)
{
a = +(a || 0);//coerce to number, use default value
b = +(b || 1) || 1;//division by zeroe is not allowed, default to 1
return a/b;
};
function someFunction ()
{
return 'someString';
};
var another = function(a, b)
{
return a + b;
};
someFunction.divide(12, 6);//will return 2
another.divide(12, 4);//3
В обоих случаях объект функции, на который ссылается имя (someFunction
или another
), будет сканироваться для свойства с именем divide
, которое не найдено. Затем, однако, он сканирует Function.prototype
, где такое свойство найдено.
Если бы это было не так, JS также проверила бы Object.prototype
, если это не сработало, оно в конечном итоге вызовет ошибку.
Я опубликовал довольно длительные ответы на SO по этому вопросу некоторое время назад:
Что делает my.class.js так быстро? (имеет дело с цепочками прототипов)
Объекты и функции в javascript (recap of functions <= > objects <= > constructors)
Каковы различия между этими тремя шаблонами класса? определения в JavaScript? (еще немного информации)
Javascript - динамически изменять содержимое функции (неопределенно затрагивает анонимные функции, связанные с переменными и свойствами и изменяя их контекст)
Ответ 3
Вы можете создать divide
как метод [вид a] static
:
var addNum = function(num1, num2) {
addNum.divide = function(){return num1/num2;};
return num1 + num2;
}
// now you first have to run addNum
var onethirds = addNum(1,3); //=> 4
addNum.divide(); //=> 0.333333...
Но это нецелесообразно. Лучше создайте функцию конструктор:
function Pair(n1,n2){
n1 = n1 || 1;
n2 = n2 || 1;
// create instance methods
this.add = function(){return n1+n2;};
this.divide = function(){return n1/n2;};
this.multiply = function(){return n1*n2;}
}
var pair1 = new Pair(2,6)
,pair2 = new Pair(1,2);
pair1.add(); //=> 8
pair2.divide(); //=> 0.5
//etc.
или более прототипный подход (методы добавляются к прототипу конструктора, а не ко всем экземплярам):
function Pair(n1,n2){
this.n1 = n1 || 1;
this.n2 = n2 || 1;
// create prototype methods (once)
if (!Pair.prototype.add){
var proto = Pair.prototype;
proto.add = function(){return this.n1+this.n2;};
proto.divide = function(){return this.n1/this.n2;};
proto.multiply = function(){return this.n1*this.n2;}
}
}
Чтение материала
Ответ 4
Нет, ваш последний код имеет смысл только в том случае, если вы использовали addNum
как функцию конструктора:
var instance = new addNum();
instance.divide();
Однако, поскольку функции являются объектами, было бы справедливо следующее:
var addNum = function(num1, num2) {
return num1 + num2;
}
addNum.divide = function() {}
В этом случае divide
будет свойством самого addNum
, а не одного из его прототипов.
Ответ 5
Чтобы понять прототипное наследование, сначала несколько неясно, но подумайте об этом, как следует из названия, в JavaScript есть несколько прототипов, а функция - одна из них.
Всякий раз, когда вы создаете новую функцию, вы можете проверить ее тип с помощью команды typeof
. В вашем случае:
var a = function(a,b) { return a + b; }
Он вернет "function"
, поэтому есть два способа добавить к вашей переменной a
больше методов. Один из них, как предложил @Keith Morris, создать новый конструктор и использовать его методы внутри и вернуть его. Это также является предпочтительным способом, потому что тогда вы не загрязняете базовые объекты прототипными методами, которые распространяются на каждый представленный ими объект.
Значение, если я вместо этого сделаю это:
Function.prototype.divide = function(a, b) { return a / b; }
Теперь я могу сделать a.divide(2, 1);
, и он вернет 2
. Но, например, если я использую jQuery
и do jQuery.divide(2,1)
, я также получаю 2
, потому что он пытается найти его в непосредственной близости от функции. Если нет, то он перейдет к прототипу.
Надеюсь, это объяснит это вам немного лучше.