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.toStringobj 它自己的 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 实例。


长版

由于您的问题很笼统,我将尝试描述一些主题,希望您能回答自己的问题。以下是我将尝试涵盖的主题:

  1. 单词的两种用法"prototype"。
  2. 如何在 JavaScript 中创建 classes。
  3. Function & Object 构造函数如何关联。

注意: 解释 JavaScript 的真正工作原理可能很困难,有时甚至令人困惑。我希望你能从中有所收获。


单词的两种用法"prototype"

单词"prototype"在JavaScript中可能有点混乱。那是因为根据上下文至少有两种用法:

1) "The prototype object of another object"

另一个对象的原型对象也被称为"internal prototype",记为[[Prototype]] , 或 __proto__;他们都是同一个意思。让我们以这个数组为例:nums = [9, 8, 7];。我们说 nums 是一个数组...但是为什么?

  1. 我们说它是一个数组,因为它是 Array 构造函数的一个实例(构造函数只是函数,除了我们将它们与 new 关键字).
  2. 我们也说它是一个数组,因为它的原型对象(a.k.a。"internal prototype")是Array.prototype 属性.
  3. 中包含的对象

2) "The prototype property of a constructor function"

继续 nums 数组示例,Array 构造函数有一个名为 prototype 的 属性,我们可以这样访问它:Array.prototype。这个 属性 是 Array 实例的 "internal prototype",并提供了我们习惯调用数组的所有方法 - 例如forEachpushpopjoin 等。

因此,按照相同的思路,我的函数 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 中的所有构造函数,如 PersonObjectArrayFunctionStringBoolean、等等,只是功能。由于它们是函数,这意味着它们是在语言内部使用 new Function 创建的,并且所有函数方法如 call()apply() 来自 Function.prototype 。也就是说,Function.prototype是所有函数的"prototype/internal prototype"对象,包括构造函数和函数Function本身。

结论:

不要混淆构造函数的 prototype 属性,它包括未来实例将使用的方法,以及构造函数本身的内部原型。

但是,请记住,构造函数的 prototype 属性 是该构造函数实例的内部 [[Prototype]]。例如,Function.prototypeObject 构造函数的内部 [[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
};

更多资源