首先,请记住JavaScript主要是一种原型语言,而不是基于类的语言1。Foo不是一个类,它是一个函数,一个对象。您可以使用new关键字从该函数实例化一个对象,这将允许您创建类似于标准OOP语言中的类的东西。
我建议大多数时候忽略__proto__,因为它的跨浏览器支持很差,而是专注于学习prototype是如何工作的。
如果你有一个从函数2创建的对象实例,并且你以任何方式访问它的一个成员(方法、属性、属性、常量等),访问将沿着原型层次结构向下流动,直到它(a)找到成员,或者(b)没有找到另一个原型。
层次结构从被调用的对象开始,然后搜索它的原型对象。如果原型对象有原型,则重复执行,如果不存在原型,则返回undefined。
例如:
foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"
foo = {};
console.log(foo.bar); // logs undefined
function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "buzz" because f has an attribute "bar" set
在我看来,你至少已经在某种程度上理解了这些“基本”部分,但我需要明确地说明它们。
在JavaScript中,所有东西都是object3。
一切都是一个对象。
function Foo(){}不仅定义了一个新函数,它还定义了一个可以使用Foo访问的新函数对象。
这就是为什么你可以用Foo.prototype访问Foo的原型。
你还可以在Foo上设置更多的函数:
Foo.talk = function () {
alert('hello world!');
};
这个新函数可以通过以下命令访问:
Foo.talk();
我希望现在您已经注意到函数对象上的函数与静态方法之间的相似性。
假设f = new Foo();Foo.prototype.bar = function(){…}作为定义类的共享方法,而Foo。Baz = function(){…}作为定义类的公共静态方法。
ECMAScript 2015为这些类型的声明引入了各种语法糖,使它们更容易实现,同时也更容易阅读。因此,前面的例子可以写成:
class Foo {
bar() {...}
static baz() {...}
}
这允许bar被调用为:
const f = new Foo()
f.bar()
baz被称为:
Foo.baz()
1: class在ECMAScript 5规范中是一个“未来保留字”,但ES6引入了使用class关键字定义类的能力。
2:本质上是一个由构造函数创建的类实例,但有许多微妙的区别,我不想误导你
3:基本值——包括未定义、null、布尔值、数字和字符串——在技术上不是对象,因为它们是低级语言实现。布尔值、数字和字符串仍然与原型链交互,就像它们是对象一样,因此为了回答这个问题,更容易将它们视为“对象”,尽管它们不完全是。