摘要:虽然在脚本中没有标准的方式访问,但在每个对象上都支持一个属性,用于访问其构造函数的原型对象。说的是构造函数和原型对象之间的关系,说的是实例对象和原型对象之间的关系。
前言
在 segmentfault 上看到这样一道题目:
var F = function(){}; Object.prototype.a = function(){}; Function.prototype.b = function(){}; var f = new F();
问:f 能取到a,b吗?原理是什么?
乍一看真的有点懵,仔细研究了一下,发现还是对原型理解不透彻,所以总结一篇,填个洞~
Function和Object在解题之前,先再说说 原型、原型链,以及 Function 和 Object 的关系,这也是本文的重点。
原型在创建一个函数的时候,会自动为其创建一个原型对象,可以通过函数的prototype属性访问到。
创建一个构造函数的实例对象,该实例对象内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262 第5版中管这个指针叫[[prototype]]。虽然在脚本中没有标准的方式访问[[prototype]],但Firefox、 Safari、 Chrome在每个对象上都支持一个属性 __proto__,用于访问其构造函数的原型对象。
重要的事情再说一遍:
构造函数通过 prototype 属性访问原型对象。
实例对象通过 [[prototype]] 内部属性访问原型对象,浏览器实现了 _proto_ 属性用于实例对象访问原型对象。
var F = function () {}; var f = new F(); // 假设F的原型对象是 p, 则 // F.prototype === p; // f.__proto__ === p;
再重复一遍。。prototype说的是构造函数和原型对象之间的关系,__proto__说的是实例对象和原型对象之间的关系。
原型链类 A继承B,B继承C……其实就是A的原型对象中有指针指向B的原型对象,而B的原型对象中有指针指向C的原型对象……注意是原型对象之间的联系,A B C 这三个构造函数之间并没什么关系,所以才称为“原型链”吧~
假设a是A的实例对象,则 a 的原型链为下图中紫色线条所示,橙色线条连接了构造函数和其原型对象。
由图可以看出,原型链的末端是Object.prototype.__proto__即null。当查找a的某个属性或方法时,首先查找a自身有没有,没有则沿着原型链一直查找,直到找到或者最后到null返回undefined。
Function 和 ObjectFunction 和 Object 之间的关系有点绕:
Object 是构造函数,既然是函数,那么就是Function的实例对象;Function是构造函数,但Function.prototype是对象,既然是对象,那么就是Object的实例对象。
一切对象都是Object的实例,一切函数都是Function的实例。
Object是Function的实例,而Function.prototype是Object的实例。
二者的关系如下图所示。
可见,Object作为构造函数,它有 prototype 属性指向 Object.prototype , 作为实例对象, 它有 Object.__proto__ 指向Function.prototype。Function是构造函数,它有prototype属性指向Function.prototype,而Function是函数,从而也是Function的实例,所以它有Function.__proto__指向Function.prototype,从而 Function.__proto__ === Function.prototype 为 true。
可在Chrome控制台下进行验证,如图。
解决原型链问题最好的办法就是画图了,经过前面的分析,这个图画起来应该不成问题,如下~
f 的原型链为蓝色线所画,所以 f 可以访问到 a , 不能访问到 b 。
如果不画图,乍一看,可能会觉得f 可以访问到 b,那是可能跟我一样误认为F.prototype指向Function.prototype,但其实F.prototype是对象而不是函数,所以它的原型对象不会是 Function.prototype。
所以,原型链问题一应要画图啊~
原题扩展在上题中,f 只能访问 a,不能访问 b 。但 F 既可以访问 a ,又可以访问 b。
如果把题修改成下面的样子, F.b()的结果是什么呢?为什么呢?可以想一下哦~
var F = function(){}; Object.prototype.a = function(){}; Function.prototype.b = function(){ console.log("F.__proto__") }; F.prototype.b = function (){console.log("F.prototype");};总结
读到这里,有没有发现函数一个比较特殊的地方?
一般的对象,只有一个__proto__属性用于访问其构造函数的原型对象,而对于函数来说,它既是函数又是对象。
作为函数,它生来就有prototype属性指向其原型对象函数名.prototype。
作为Function的实例对象,它有__proto__属性指向Function.prototype
通常,这两个属性是指向两个对象的,但Function的这两个属性指向相同,都指向Function.prototype。
对于函数 A( ) 来说,A.prototype 中的方法是供其实例对象调用的,自己并不会用;当A 作为实例运行时,调用的是 A.__proto__ 中的方法。也就是说,作为构造函数使用时,走的是A.prototype这条链,方法、属性赋给其实例;作为对象使用时,走的是A.__proto__这条链。在不同的场景下,分清它的身份就不会错了。
整篇下来,感觉自己说的也比较絮叨……不足之处,还请各位指正~ 至于题目,真的不知道该叫什么好。。
愿本文能带给坚持看完的你一些收获~ ^_^
参考js 原型的问题 Object 和 Function 到底是什么关系?—— 高票答案
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/85938.html
摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...
摘要:但是,必须强调,闭包是一个运行期概念。通过原型链可以实现继承,而与闭包相关的就是作用域链。常理来说,一个函数执行完毕,其执行环境的作用域链会被销毁。所以此时,的作用域链虽然销毁了,但是其活动对象仍在内存中。 学习Javascript闭包(Closure)javascript的闭包JavaScript 闭包深入理解(closure)理解 Javascript 的闭包JavaScript ...
摘要:在上面的各种原型的变换中,其实难点就在于构造函数也是对象原型对象等所有对象都由构造这四个点。 这篇文章主要是学习一下JavaScript中的难点------原型和原型链 自定义一个对象 我们学习一门编程语言,必然要使用它完成一些特定的功能,而面向对象的语言因为符合人类的认知规律,在这方面做得很好,今天我以JS为例,探索一下JS不同于其他面向对象的语言的地方-------原型和原型链 首...
摘要:在上面的各种原型的变换中,其实难点就在于构造函数也是对象原型对象等所有对象都由构造这四个点。 这篇文章主要是学习一下JavaScript中的难点------原型和原型链 自定义一个对象 我们学习一门编程语言,必然要使用它完成一些特定的功能,而面向对象的语言因为符合人类的认知规律,在这方面做得很好,今天我以JS为例,探索一下JS不同于其他面向对象的语言的地方-------原型和原型链 首...
阅读 2578·2021-11-17 09:33
阅读 3871·2021-10-19 11:46
阅读 883·2021-10-14 09:42
阅读 2231·2021-09-22 15:41
阅读 4084·2021-09-22 15:20
阅读 4574·2021-09-07 10:22
阅读 2281·2021-09-04 16:40
阅读 793·2019-08-30 15:52