JavaScript原型链以及Object,Function之间的关系

JavaScript里任何东西都是对象,任何一个对象内部都有另一个对象叫__proto__,即原型,它可以包含任何东西让对象继承。当然__proto__本身也是一个对象,它自己也有自己的__proto__,这样一级一级向上,就构成了一个__proto__链,即原型链。当然原型链不会无限向上,它有个终点,可以称为原型链的顶端,或者root,它是一个特殊的对象,它的__proto__为null。

obj.__proto__.__proto__......__proto__ === null;

但是对象并不是凭空产生的,它一般是某一个class,或者确切说是构造函数的实例。JavaScript和其它面向对象的语言不太一样,它没有所谓的class,而是用函数来模拟class。定义了一个函数,实际上就定义了一个class,函数本身就是class的constructor,例如:

function foo() {}
var a = new foo();

这里创建了一个对象a,是foo的一个实例。既然a是一个对象,它就有原型__proto__,那a的__proto__是怎么设定的?这里就引出了prototype,它是函数才有的一个属性,也是一个对象,一个函数创建的实例的__proto__就指向这个函数的prototype。所以a的__proto__被设定为foo.prototype,即:

a.__proto__ === foo.prototype;

当然foo.prototype也是个对象,它自然也有__proto__,默认指向Object.prototype,即:

foo.prototype.__proto__ === Object.prototype;

而Object.prototype已经没有原型了,它的__proto__是null。这是JavaScript里一个很特殊的对象,它就是原型链的顶端

以上就构成了由对象a开始的原型链:

a.__proto__ === foo.prototype;
a.__proto__.__proto__ === Object.prototype;

JavaScript因此而推断出:

a instanceof foo === true;
a instanceof Object === true;

注意,这就是JavaScript判断一个对象是否instanceof某个函数的依据,即对象a的原型链上有没有一个__proto__是这个函数的prototype,如果有,那么a就是这个函数的instance。由于一般所有的原型链最终都会指向顶端的Object.prototype,所以它们都是Object的instance。

prototype和__proto__有什么作用

我们可以设定

foo.prototype = {
  x : 2,
  y : 3,
}

这里用字面量创建一个对象赋给了foo.prototype,这样foo所创建出来的任何对象的__proto__就都指向了它,因此它们就可以访问里面的field,或者说可以认为它们继承了这些field,例如:

var a = new foo();
a.x === 2;
a.y === 3;

不只是foo.prototype,继续往上foo.prototype的__proto__,以及原型链上所有的__proto__都会被这个对象继承。一般的对象的很多常用方法如toString,都是从原型链顶端的Object.prototype里继承来的。

函数也是对象

上面说了,JavaScript里任何东西都是对象,包括函数,可以称为函数对象。所以foo也是对象,那foo的原型__proto__是什么?它是谁的instance?

JavaScript里定义了一个特殊的函数叫Function,可以称作是所有函数的爸爸,所有的函数都是它的实例,因此你可以认为,定义foo的时候发生了这样的事情:

var foo = new Function(args, function_body);

于是我们有:

foo.__proto__ === Function.prototype;
foo instanceof Function === true;

注意这里的Function.prototype,这也是JavaScript里一个特殊的对象,Chrome的console里要是输入Function.prototype,根本什么也打印不出来,什么native code,就是说它是内部实现的。

这个原型链还没到顶,Function.prototype仍然有原型__proto__,指向Object.prototype,所以我们最终有:

foo.__proto__.__proto__ === Object.prototype;
foo instanceof Object === true;

现在有个问题来了,那Function自己呢?它其实也是个函数,也是个对象,它的__proto__指向谁?答案是它自己的prototype,即:

Function.__proto__ === Function.prototype;
Function instanceof Function === true;
Function.__proto__.__proto__ === Object.prototype;
Function instanceof Object === true;

总结一下:所有的函数都是Function的instance,Function自己也是它自己的instance,不过后者严格来说并不准确,Function并不是它自己创造自己的,而应该看作JavaScript里原生的一个函数对象,只不过它的__proto__指向了它自己的prototype而已。

Function和Object的关系

这是JavaScript比较奇葩的一个地方,也是不太让人容易接受的一点。

我们知道一般任何对象都是Object的instance,因为原型链的顶端都指向Object.prototype。那么Object本身是什么?Object也是个函数,而任何函数都是Function的实例对象,比如Array,String,当然Object也包括在内,它也是Function的实例,即:

Object.__proto__ === Function.prototype;
Object instanceof Function === true

同时,Function是个对象,它的原型是Function.__proto__,指向Function.prototype,并且这个原型链向上继续指向Object.prototype,即:

Function.__proto__.__proto__ === Object.prototype;
Function instanceof Object === true

这样就有了一个JavaScript里经常说到的蛋鸡问题:

Object instanceof Function === true
Function instanceof Object === true

到底谁先谁后,谁主谁次?关于这一点网上已经有很多解释,这里首先陈述我的观点,是先有Function,它是主;后有Object,是次。以下是我个人的理解,可能并不准确。要看待这个问题,我们可以从JavaScript创造世界开始想象:

  1. 我们知道Object.prototype是原型链的root。但首先,现在世界上还没有Object,更没有Object.prototype。现在只有个特殊的对象,姑且称它为root_prototype,里面定义了些基本的field和method比如toString之类的,以后我们要让所有的原型链都最终指向它。注意它没有原型,它的__proto__是null,这也是它和所有其它JavaScript对象的区别,使它与众不同,能有资格成为原型链的root。
  2. 然后定义Function。先看Function的prototype,我们只要知道这是一个特殊的对象,它的原型__proto__指向刚才的root_prototype,就是说Function.prototype.__proto__ === root_prototype,这样它就算连上了原型链的root。
  3. 上面已经讲过了,Function也是个对象,也有__proto__,指向Function自己的prototype,所以说白了Function也是个奇葩,是JavaScript里规定的一个特殊的东西。而Function.prototype的原型__proto__继续指向root_prototype,所以Function也连上了原型链root。
  4. 所有的函数,什么Array之类的,包括Object也是函数,都是继承Function的,就是说,任意函数foo.__proto__ === Function.prototype,所以我们自然有Object instanceof Function。
  5. 然后再看Object,它本来就是个函数而已,和其它函数没什么区别,都继承了Function。可是现在最关键的一步是,强行设定让Object.prototype = root_prototype,这样Object.prototype就成了原型链的root!注意这里的逻辑,是先有了root_prototype,然后规定Object.prototype等于它,这一步是人为规定的,这就是Object的特殊之处。你要是规定bar.prototype也等于root_prototype,那bar.prototype也成了原型链的的顶端。所以JavaScript里__proto__这个东西其实是很随意的,放在哪个函数的prototype里,哪个函数就成了你爹。
  6. 好了现在Object.prototype === root_prototype了,成了所有对象原型链的root。那么由第3步的结论,Function也是对象,是连上了root_prototype的,而现在root_prototype给Object.prototype了,那Function自然就是instanceof Object。
总结一下:
- 首先没鸡没蛋,先有一个特殊对象root_prototype,它是上帝。
- 接下来应该是先有Function,并且定义它的prototype和__proto__,都连上了root_prototype。
- 然后才有了Object,它是Function的instance,继承了Function。这时候Object仍然只是个普通的函数。
- 然后规定Object.prototype = root_prototype,这时候Object才开始显得特殊,成为了原型链的顶端,否则它和其它函数根本没什么区别。
- 于是所有的东西,包括Function,都成了Object的instance了。

这里要强调Object和其它函数的不同之处。Object之所以特殊,就是因为Object的prototype被设定为了root_prototype,仅此而已;而其它函数例如foo,它的prototype只是一个普通的对象,这个对象的__proto__默认情况下指向root_prototype。至于为什么这样设定,为什么Object会特殊化,大概只是因为Object这个名字起得好,而foo,bar没那么特殊。所以说白了Object函数只是一个盛放root_prototype的容器而已,从而使它晋升为一个特殊的函数。

另外值得注意的是,obj instanceof function 并不意味着obj就是这个function创建出来的,只不过是obj的原型链上有function.prototype而已。

所以所谓的 Object instanceof Function 和 Function instanceof Object 的蛋鸡问题,前者应该来说是自然而然、不容置疑的,可以认为Object函数是Function创造出来的;而后者说白了只是因为强行规定了Object函数的特殊性,而导致的一个推论,而Function并非是Object创建的。

当然这些概念绕来绕去讨论其实我感觉没什么很大意义,无非只是自圆其说而已。大家写代码时也不会太纠结这些问题,重点还是要把原型链搞清楚。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JavaScript原型链是一种继承机制,它使得每个对象都能从另一个对象继承属性。在 JavaScript 中,每个对象都有一个原型对象,并且可以从原型对象继承属性。 每个对象的原型对象都有一个自己的原型对象,以此类推。这就形成了一条原型链,从对象到原型对象再到其原型对象,直到最终到达 null。当你在对象上访问一个属性时,JavaScript 引擎会沿着原型链向上搜索,直到找到一个具有该属性的对象为止。 例如,假设我们有一个构造函数 `Person` 和一个实例 `person1`,它们看起来像这样: ``` function Person(name) { this.name = name; } Person.prototype.sayHi = function() { console.log(`Hi, my name is ${this.name}`); }; const person1 = new Person('Alice'); ``` 在这个例子中,`person1` 的原型对象是 `Person.prototype`,而 `Person.prototype` 的原型对象是 `Object.prototype`。所以,当我们在 `person1` 上调用 `sayHi` 方法时,JavaScript 引擎会在 `person1`、`Person.prototype` 和 `Object.prototype` 中依次搜索该方法,最终找到它并调用。 原型链使得我们能够实现继承,同时又不需要使用传统的继承语法 ### 回答2: JavaScript原型链是一种特殊的继承机制,它是基于对象的,与传统的基于类的继承不同。 在JavaScript中,每一个对象都有一个原型对象,而原型对象也是一个对象,并且它也有自己的原型对象,这样就形成了一条链式结构,被称为原型链。当我们访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,它会沿着原型链向上查找,直到找到为止。 这个查找过程是通过对象的__proto__属性来实现的,__proto__属性指向该对象的原型对象。当我们访问对象的属性时,如果对象和它的原型对象中都没有这个属性,那么会继续查找原型对象的原型对象,直到找到属性或者到达原型链的顶端,即Object.prototype。如果还没有找到,那么返回undefined。 JavaScript原型链的实质是通过对象之间的关联关系来实现继承。当创建一个对象时,JavaScript引擎会自动为这个对象添加一个隐藏的属性__proto__,并将它指向该对象的构造函数的原型对象。这样,通过对象与原型对象之间关系,实现了继承的特性。 原型链的应用主要体现在面向对象编程中的继承。通过定义构造函数和原型对象,我们可以实现对象的继承关系,并且可以方便地从原型对象中继承属性和方法,提高代码的复用性和可维护性。 总之,JavaScript原型链是一种通过对象关联来实现继承的机制,在面向对象编程中起到了重要的作用。它通过沿着原型链查找的方式,实现了属性和方法的继承,并提供了一种灵活、简洁的继承方式。 ### 回答3: JavaScript中的原型链是一种用于继承和属性查找的机制。每个JavaScript对象都有一个原型,它可以是另一个对象或null。 当我们访问一个对象的属性时,JavaScript引擎首先会在该对象自身查找该属性,如果找不到,它将继续在原型链上进行查找。原型链的顶层是Object.prototype,内置的JavaScript对象(如Array、String)都是它的子对象。 当我们创建一个对象时,JavaScript引擎会通过一个\_\_proto\_\_属性将该对象关联到它的构造函数的原型上。构造函数通过prototype属性来指定原型对象,在实例化对象时会自动创建一个存放共享方法和属性的原型对象。 通过原型链,对象可以继承自己的构造函数原型对象上的属性和方法。这意味着我们可以在原型对象上定义共享方法和属性,而不是每次创建新的对象时都重复定义这些方法和属性,从而节省内存空间。 当我们通过对象实例访问一个属性时,JavaScript引擎会首先在对象自身查找该属性,如果找不到,它会继续在原型链上查找,直到找到该属性或者到达原型链的顶层。如果该属性在原型链上的多个位置都有定义,那么只会取最先找到的属性值。 原型链JavaScript中实现继承的基础。通过让一个对象的原型指向另一个对象的实例,我们可以实现原型继承。子对象可以继承父对象原型链上的属性和方法。 总结来说,JavaScript原型链是一种用于实现继承和属性查找的机制。它通过将对象的原型与其构造函数的原型对象关联起来,从而实现属性继承和共享方法的定义。原型链JavaScript中面向对象编程的基础之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值