我有一个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()
的行请帮我理解一下。
答案 0 :(得分:50)
我不确定这会回答你的问题,但可能会给你一些见解。请考虑以下示例:
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
函数对象“Person”具有一个直接的'greet'属性,它是一个Function。在OOP方面,您几乎可以将其视为可以直接从Person Function(Person.greet())调用的静态方法。从Person构造函数“实例化”person对象后,新对象“bob”现在从Person.prototype对象引用它的方法。现在当你调用bob.greet()时,它使用原型对象中的greet函数。
希望有所帮助。
答案 1 :(得分:19)
正如你自己所说:你有一个功能对象 。函数是JS中的对象,就像对象文字,数组或其他任何东西一样:可以随意为函数分配属性和方法:
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
)。要知道不建议这样做,事实上改变“native”原型通常是不受欢迎的。仍然:
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
,如果失败,最终会抛出错误。
我在这个问题上发表了很长时间的答案:
What makes my.class.js so fast?(处理原型链)
Objects and functions in javascript(功能概述&lt; =&gt;对象&lt; =&gt;构造函数)
What are the differences between these three patterns of "class" definitions in JavaScript?(还有更多信息)
Javascript - Dynamically change the contents of a function(模糊地触及匿名函数,分配给变量和属性并更改其上下文)
答案 2 :(得分:3)
您可以将divide
创建为[某种] 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...
但这不可取。最好创建一个constructor函数:
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;}
}
}
答案 3 :(得分:2)
不,如果您使用addNum
作为构造函数,那么您的上一个代码才有意义:
var instance = new addNum();
instance.divide();
但是,因为函数是对象,所以以下内容是有效的:
var addNum = function(num1, num2) {
return num1 + num2;
}
addNum.divide = function() {}
在这种情况下,divide
将是addNum
本身的属性,而不是其原型之一。
答案 4 :(得分:0)
首先要理解原型继承有点模糊,但顾名思义,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
并执行jQuery.divide(2,1)
,我也会得到2
,因为它会尝试在函数的直接范围内找到它。如果没有,它将转到它的原型。
希望这能更好地解释一下。