JavaScript:对象继承自 Function.prototype
JavaScript: Object inheriting from Function.prototype
我正在测试 James Shore 的 Object Playground,我发现所有方法都继承自 Function.prototype,包括全局 Object.prototype 上的方法。这是如何运作的?这不是有点圆吗?我的意思是... Function.prototype "itself" 不是 Object.prototype 固有的吗?那么 Object 如何继承 Function.prototype 中的任何内容?函数不只是对象的子类型吗?无论如何,对象不应该固有地包含这些行为吗?为什么需要继承?
什么是原型?
JavaScript 是一种基于原型的语言。这意味着技术上没有 "classes"。只有描述对象的原型。每个对象都有一个原型。原型本身,实际上是一个对象。 (令人困惑吧?不要想太多,如果你不能全神贯注。它有时会 click。只知道原型是你可以修改的对象) .
在我们继续之前,我想指出我这里的代码示例没有遵循正确或最佳实践。我写的代码示例纯粹是为了演示或解释一个概念。
让我们看一些代码:
Object.toString(); // "[object Object]"
Object.prototype.toString(); // "[object Object]"
Object.hasOwnProperty('toString'); // true
typeof Object; // "function"
typeof Object.prototype // "object"
var obj = new Object();
obj.toString(); // "[object Object]"
obj.hasOwnProperty('toString'); // false
obj.toString = function() {
return 'My Object';
};
obj.toString(); // "My Object"
obj.hasOwnProperty('toString') // true
obj.__proto__.toString(); // "[object Object]"
typeof obj; // "object"
typeof obj.__proto__; // "object"
您可能还注意到 typeof Object
returns "function"
。这是因为 Object 实际上是一个构造方法,用来实例化新的对象。我的实例对象实际上是 typeof obj === "object"
.
原型链
正如您在上面的代码中看到的,Object
包含一个名为 toString
的方法。但是 Object
的实例没有。 obj
没有自己的 toString
方法。但是您仍然可以在 obj
上调用 toString
。 JavaScript 通过遵循其 原型链 来实现其 继承 。
我可以重写 obj.toString
给 obj
它自己的 toString
方法,但是 obj.__proto__
属性 仍然有原来的 toString
方法来自其 prototype
.
如果有问题的对象不包含它自己的 属性 toString
,那么 属性 将查看它的原型。如果它自己的原型不包含 属性 toString
,那么查找将继续沿着原型链向上查找,直到找到 属性。如果原型是null
,那么就是当属性是undefined
.
JavaScript 中的所有内容都是对象?
是的,在最核心的地方,最终在原型链中,每个对象都是一个 JavaScript 对象。包括Functions
.
var func = function() {};
func.__proto__ //function Empty() {}
func.__proto__.__proto__ // Object {}
func.__proto__.isPrototypeOf(Object) // true
所以函数是 JavaScript 中的一个对象。这就是您可以将属性附加到函数的原因。
还有一些说明...
all methods inherit from Function.prototype
不,不是所有方法都继承自Function
。方法 是 Function
。
So how does Object.prototype inherent anything from Function.prototype?
对象没有从函数继承任何东西。对象可以使用函数,例如toString
方法。
Object
有点展示了复合设计模式。 (除了可以向引用自身的对象添加属性外 and/or 创建循环引用)
来自 [http://en.wikipedia.org/wiki/Composite_pattern]
In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies.
因为Object
包含几个属性,也都是Object
.
所以它是循环的吗?不,不是真的。但是肯定是递归的
您可以在此处阅读有关 JavaScript 原型的更多信息:http://blog.pluralsight.com/understanding-javascript-prototypes
请注意,ES6 在 JavaScript 中引入了实际的 类。这与我上面解释的一切都不一样。 ES6 class
是我还没有玩过的东西。
TL;DR
Object.prototype 在原型链的最后,它不继承任何东西。 Object 构造函数是继承自 Function.prototype 的构造函数,因为它只是一个函数;这是一个 Function 实例。
长版
由于您的问题很笼统,我将尝试描述一些主题,希望您能回答自己的问题。以下是我将尝试涵盖的主题:
- 单词的两种用法"prototype"。
- 如何在 JavaScript 中创建 classes。
- Function & Object 构造函数如何关联。
注意: 解释 JavaScript 的真正工作原理可能很困难,有时甚至令人困惑。我希望你能从中有所收获。
单词的两种用法"prototype"
单词"prototype"在JavaScript中可能有点混乱。那是因为根据上下文至少有两种用法:
1) "The prototype object of another object"
另一个对象的原型对象也被称为"internal prototype",记为[[Prototype]] , 或 __proto__
;他们都是同一个意思。让我们以这个数组为例:nums = [9, 8, 7];
。我们说 nums
是一个数组...但是为什么?
- 我们说它是一个数组,因为它是
Array
构造函数的一个实例(构造函数只是函数,除了我们将它们与 new 关键字).
- 我们也说它是一个数组,因为它的原型对象(a.k.a。"internal prototype")是
Array.prototype
属性. 中包含的对象
2) "The prototype property of a constructor function"
继续 nums
数组示例,Array 构造函数有一个名为 prototype
的 属性,我们可以这样访问它:Array.prototype
。这个 属性 是 Array 实例的 "internal prototype",并提供了我们习惯调用数组的所有方法 - 例如forEach
、push
、pop
、join
等。
因此,按照相同的思路,我的函数 foo()
或任何其他函数的内部原型是 Function.prototype
属性 中包含的对象;换句话说,Function.prototype
是任何函数的 "internal prototype" 对象。另外,我们可以说 Function 构造函数有一个 prototype 属性,它最终是所有函数的 "internal prototype" .
我要说的是我们用两种不同的方式谈论一件事(原型)。在第一种方式中我们说:对象的 "the prototype/internal prototype",在第二种方式中我们说:"the constructor's prototype" 属性.
如何在 JavaScript
中创建 classes
In JavaScript 构造函数 类似于其他编程语言中的 classes。好吧,不完全是。实际上,为了类似于 classes,JavaScript 使用了 构造函数 和另一个称为 原型 的对象的组合。实际上每个 JavaScript 函数都会自动获取一个 prototype 属性 因为函数可以用作构造函数或简单地用作函数。当一个函数不被用作构造函数时,它的 prototype 属性 不会被用作任何东西,它只是作为一个无用的 属性 悬在那里。
在 classical 语言中,class 包含 实例变量 和 实例方法 ,但是在 JavaScript 中,构造函数包含实例变量,其 原型 对象包含实例方法。
实例变量对于构造函数的特定实例是唯一的(它们包含实例特定数据),实例方法由所有实例共享。换句话说,所有实例都可以执行实例方法,但不能访问彼此的变量。
因此,JavaScript 中的所有对象都是其各自构造函数的实例。例如,[1,2,3]
这样的数组是 function Array() {}
构造函数的一个实例。 {key: 'value'}
等对象是 function Object() {}
构造函数的实例。 JavaScript 函数,例如 alert()
是 function Function() {}
构造函数的实例...等等。
同样,JavaScript 中的所有构造函数都有一个 prototype
属性,并且这个 属性 包括构造函数实例将继承的方法。
示例:
// Person constructor to create people instances
function Person(name, age) {
// Every instance has its own "instance variables", a.k.a. properties.
this.name = name;
this.age = age;
}
// The "instance methods"
Person.prototype = {
greet: function() {
return 'Hello ' + this.name;
},
//...
};
// Joe is an instance of the `Person` constructor, and Joe's "prototype"
// is the `Person.prototype` object. We call Joe's "prototype" the
// "internal prototype".
var joe = new Person('Joe Doe', 44);
joe.name; //=> Joe Doe
joe.greet(); //=> Hello Joe Doe
函数和对象构造函数如何关联
Object
构造函数。
Object 构造函数与上面的 Person 构造函数类似,只是它创建对象实例而不是 person 实例。
Function
构造函数。
Function 构造函数就像上面的 Person & Object 构造函数,不同之处在于它创建 Function 实例,换句话说,它创建函数。
JavaScript 中的所有构造函数,如 Person
、Object
、Array
、Function
、String
、Boolean
、等等,只是功能。由于它们是函数,这意味着它们是在语言内部使用 new Function
创建的,并且所有函数方法如 call()
和 apply()
来自 Function.prototype 。也就是说,Function.prototype是所有函数的"prototype/internal prototype"对象,包括构造函数和函数Function
本身。
结论:
不要混淆构造函数的 prototype
属性,它包括未来实例将使用的方法,以及构造函数本身的内部原型。
但是,请记住,构造函数的 prototype
属性 是该构造函数实例的内部 [[Prototype]]。例如,Function.prototype
是 Object
构造函数的内部 [[Prototype]],这是有道理的,因为 Object
构造函数只是另一个函数(一个 Function
实例) .
要获得代码结论,请查看如何在 JavaScript 中内部创建对象和函数构造函数:
// Object constructor
// ==============================================
function Object() { /* ... */ }
// Object.keys()
// Object.observe()
// ...
// `Object.__proto__` (internal [[Prototype]])
// -----------------------------------------------
// Since `Object` is a function, it inherits all of Function's
// instance methods (the ones inside of Function.prototype).
//
// In other words the `Object` constructor can use methods
// like `apply()`, `call()`, `bind()`, and more.
//
// So we can say that the Object's prototype is the
// `Function.prototype` object.
Object.__proto__ = Function.prototype;
// `Object.prototype` (instance methods)
// -----------------------------------------------
// The Object's `prototype` property is totally different from
// the `__proto__` property. This `prototype` property includes
// methods that all JavaScript objects inherit. So an object
// literal like `var obj = {}` or an array like `var arr = []`
// or even a function like `alert` can use these methods.
Object.prototype = {
constructor: Object,
hasOwnProperty: function() {},
isPrototypeOf: function() {},
//...
};
// Function constructor
// ==============================================
function Function() { /* ... */ }
// Function.call()
// Function.apply()
// ...
// [[Prototype]] + instance methods
// -----------------------------------------------
// Since `Function` is a function itself and at the same time
// the constructor for other JavaScript functions, its internal
// [[Prototype]] and the `prototype` property point to the same
// exact object.
Function.__proto__ = Function.prototype = {
apply: function() {},
call: function() {},
bind: function() {},
//...
// Just an object literal, so it inherits the
// Object's instance methods.
__proto__: Object.prototype
};
更多资源
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model#Determining_instance_relationships
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
- https://es5.github.io/#x15.3.4
- http://people.mozilla.org/~jorendorff/es5.1-final.html#sec-15.3.2.1; 为每个函数自动创建原型 属性,以提供将函数用作构造函数的可能性。
- https://www.quora.com/In-JavaScript-what-is-the-logic-behind-the-data-structure-of-function-prototype-proto-and-constructor?share=1
我正在测试 James Shore 的 Object Playground,我发现所有方法都继承自 Function.prototype,包括全局 Object.prototype 上的方法。这是如何运作的?这不是有点圆吗?我的意思是... Function.prototype "itself" 不是 Object.prototype 固有的吗?那么 Object 如何继承 Function.prototype 中的任何内容?函数不只是对象的子类型吗?无论如何,对象不应该固有地包含这些行为吗?为什么需要继承?
什么是原型?
JavaScript 是一种基于原型的语言。这意味着技术上没有 "classes"。只有描述对象的原型。每个对象都有一个原型。原型本身,实际上是一个对象。 (令人困惑吧?不要想太多,如果你不能全神贯注。它有时会 click。只知道原型是你可以修改的对象) .
在我们继续之前,我想指出我这里的代码示例没有遵循正确或最佳实践。我写的代码示例纯粹是为了演示或解释一个概念。
让我们看一些代码:
Object.toString(); // "[object Object]"
Object.prototype.toString(); // "[object Object]"
Object.hasOwnProperty('toString'); // true
typeof Object; // "function"
typeof Object.prototype // "object"
var obj = new Object();
obj.toString(); // "[object Object]"
obj.hasOwnProperty('toString'); // false
obj.toString = function() {
return 'My Object';
};
obj.toString(); // "My Object"
obj.hasOwnProperty('toString') // true
obj.__proto__.toString(); // "[object Object]"
typeof obj; // "object"
typeof obj.__proto__; // "object"
您可能还注意到 typeof Object
returns "function"
。这是因为 Object 实际上是一个构造方法,用来实例化新的对象。我的实例对象实际上是 typeof obj === "object"
.
原型链
正如您在上面的代码中看到的,Object
包含一个名为 toString
的方法。但是 Object
的实例没有。 obj
没有自己的 toString
方法。但是您仍然可以在 obj
上调用 toString
。 JavaScript 通过遵循其 原型链 来实现其 继承 。
我可以重写 obj.toString
给 obj
它自己的 toString
方法,但是 obj.__proto__
属性 仍然有原来的 toString
方法来自其 prototype
.
如果有问题的对象不包含它自己的 属性 toString
,那么 属性 将查看它的原型。如果它自己的原型不包含 属性 toString
,那么查找将继续沿着原型链向上查找,直到找到 属性。如果原型是null
,那么就是当属性是undefined
.
JavaScript 中的所有内容都是对象?
是的,在最核心的地方,最终在原型链中,每个对象都是一个 JavaScript 对象。包括Functions
.
var func = function() {};
func.__proto__ //function Empty() {}
func.__proto__.__proto__ // Object {}
func.__proto__.isPrototypeOf(Object) // true
所以函数是 JavaScript 中的一个对象。这就是您可以将属性附加到函数的原因。
还有一些说明...
all methods inherit from Function.prototype
不,不是所有方法都继承自Function
。方法 是 Function
。
So how does Object.prototype inherent anything from Function.prototype?
对象没有从函数继承任何东西。对象可以使用函数,例如toString
方法。
Object
有点展示了复合设计模式。 (除了可以向引用自身的对象添加属性外 and/or 创建循环引用)
来自 [http://en.wikipedia.org/wiki/Composite_pattern]
In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies.
因为Object
包含几个属性,也都是Object
.
所以它是循环的吗?不,不是真的。但是肯定是递归的
您可以在此处阅读有关 JavaScript 原型的更多信息:http://blog.pluralsight.com/understanding-javascript-prototypes
请注意,ES6 在 JavaScript 中引入了实际的 类。这与我上面解释的一切都不一样。 ES6 class
是我还没有玩过的东西。
TL;DR
Object.prototype 在原型链的最后,它不继承任何东西。 Object 构造函数是继承自 Function.prototype 的构造函数,因为它只是一个函数;这是一个 Function 实例。
长版
由于您的问题很笼统,我将尝试描述一些主题,希望您能回答自己的问题。以下是我将尝试涵盖的主题:
- 单词的两种用法"prototype"。
- 如何在 JavaScript 中创建 classes。
- Function & Object 构造函数如何关联。
注意: 解释 JavaScript 的真正工作原理可能很困难,有时甚至令人困惑。我希望你能从中有所收获。
单词的两种用法"prototype"
单词"prototype"在JavaScript中可能有点混乱。那是因为根据上下文至少有两种用法:
1) "The prototype object of another object"
另一个对象的原型对象也被称为"internal prototype",记为[[Prototype]] , 或 __proto__
;他们都是同一个意思。让我们以这个数组为例:nums = [9, 8, 7];
。我们说 nums
是一个数组...但是为什么?
- 我们说它是一个数组,因为它是
Array
构造函数的一个实例(构造函数只是函数,除了我们将它们与 new 关键字). - 我们也说它是一个数组,因为它的原型对象(a.k.a。"internal prototype")是
Array.prototype
属性. 中包含的对象
2) "The prototype property of a constructor function"
继续 nums
数组示例,Array 构造函数有一个名为 prototype
的 属性,我们可以这样访问它:Array.prototype
。这个 属性 是 Array 实例的 "internal prototype",并提供了我们习惯调用数组的所有方法 - 例如forEach
、push
、pop
、join
等。
因此,按照相同的思路,我的函数 foo()
或任何其他函数的内部原型是 Function.prototype
属性 中包含的对象;换句话说,Function.prototype
是任何函数的 "internal prototype" 对象。另外,我们可以说 Function 构造函数有一个 prototype 属性,它最终是所有函数的 "internal prototype" .
我要说的是我们用两种不同的方式谈论一件事(原型)。在第一种方式中我们说:对象的 "the prototype/internal prototype",在第二种方式中我们说:"the constructor's prototype" 属性.
如何在 JavaScript
中创建 classesIn JavaScript 构造函数 类似于其他编程语言中的 classes。好吧,不完全是。实际上,为了类似于 classes,JavaScript 使用了 构造函数 和另一个称为 原型 的对象的组合。实际上每个 JavaScript 函数都会自动获取一个 prototype 属性 因为函数可以用作构造函数或简单地用作函数。当一个函数不被用作构造函数时,它的 prototype 属性 不会被用作任何东西,它只是作为一个无用的 属性 悬在那里。
在 classical 语言中,class 包含 实例变量 和 实例方法 ,但是在 JavaScript 中,构造函数包含实例变量,其 原型 对象包含实例方法。
实例变量对于构造函数的特定实例是唯一的(它们包含实例特定数据),实例方法由所有实例共享。换句话说,所有实例都可以执行实例方法,但不能访问彼此的变量。
因此,JavaScript 中的所有对象都是其各自构造函数的实例。例如,[1,2,3]
这样的数组是 function Array() {}
构造函数的一个实例。 {key: 'value'}
等对象是 function Object() {}
构造函数的实例。 JavaScript 函数,例如 alert()
是 function Function() {}
构造函数的实例...等等。
同样,JavaScript 中的所有构造函数都有一个 prototype
属性,并且这个 属性 包括构造函数实例将继承的方法。
示例:
// Person constructor to create people instances
function Person(name, age) {
// Every instance has its own "instance variables", a.k.a. properties.
this.name = name;
this.age = age;
}
// The "instance methods"
Person.prototype = {
greet: function() {
return 'Hello ' + this.name;
},
//...
};
// Joe is an instance of the `Person` constructor, and Joe's "prototype"
// is the `Person.prototype` object. We call Joe's "prototype" the
// "internal prototype".
var joe = new Person('Joe Doe', 44);
joe.name; //=> Joe Doe
joe.greet(); //=> Hello Joe Doe
函数和对象构造函数如何关联
Object
构造函数。
Object 构造函数与上面的 Person 构造函数类似,只是它创建对象实例而不是 person 实例。
Function
构造函数。
Function 构造函数就像上面的 Person & Object 构造函数,不同之处在于它创建 Function 实例,换句话说,它创建函数。
JavaScript 中的所有构造函数,如 Person
、Object
、Array
、Function
、String
、Boolean
、等等,只是功能。由于它们是函数,这意味着它们是在语言内部使用 new Function
创建的,并且所有函数方法如 call()
和 apply()
来自 Function.prototype 。也就是说,Function.prototype是所有函数的"prototype/internal prototype"对象,包括构造函数和函数Function
本身。
结论:
不要混淆构造函数的 prototype
属性,它包括未来实例将使用的方法,以及构造函数本身的内部原型。
但是,请记住,构造函数的 prototype
属性 是该构造函数实例的内部 [[Prototype]]。例如,Function.prototype
是 Object
构造函数的内部 [[Prototype]],这是有道理的,因为 Object
构造函数只是另一个函数(一个 Function
实例) .
要获得代码结论,请查看如何在 JavaScript 中内部创建对象和函数构造函数:
// Object constructor
// ==============================================
function Object() { /* ... */ }
// Object.keys()
// Object.observe()
// ...
// `Object.__proto__` (internal [[Prototype]])
// -----------------------------------------------
// Since `Object` is a function, it inherits all of Function's
// instance methods (the ones inside of Function.prototype).
//
// In other words the `Object` constructor can use methods
// like `apply()`, `call()`, `bind()`, and more.
//
// So we can say that the Object's prototype is the
// `Function.prototype` object.
Object.__proto__ = Function.prototype;
// `Object.prototype` (instance methods)
// -----------------------------------------------
// The Object's `prototype` property is totally different from
// the `__proto__` property. This `prototype` property includes
// methods that all JavaScript objects inherit. So an object
// literal like `var obj = {}` or an array like `var arr = []`
// or even a function like `alert` can use these methods.
Object.prototype = {
constructor: Object,
hasOwnProperty: function() {},
isPrototypeOf: function() {},
//...
};
// Function constructor
// ==============================================
function Function() { /* ... */ }
// Function.call()
// Function.apply()
// ...
// [[Prototype]] + instance methods
// -----------------------------------------------
// Since `Function` is a function itself and at the same time
// the constructor for other JavaScript functions, its internal
// [[Prototype]] and the `prototype` property point to the same
// exact object.
Function.__proto__ = Function.prototype = {
apply: function() {},
call: function() {},
bind: function() {},
//...
// Just an object literal, so it inherits the
// Object's instance methods.
__proto__: Object.prototype
};
更多资源
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model#Determining_instance_relationships
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
- https://es5.github.io/#x15.3.4
- http://people.mozilla.org/~jorendorff/es5.1-final.html#sec-15.3.2.1; 为每个函数自动创建原型 属性,以提供将函数用作构造函数的可能性。
- https://www.quora.com/In-JavaScript-what-is-the-logic-behind-the-data-structure-of-function-prototype-proto-and-constructor?share=1