

傻傻分不清的__proto__与prototype - Leechikit的专栏 - SegmentFault
source link: https://segmentfault.com/a/1190000011801127?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

傻傻分不清的__proto__与prototype
今天同事小英童鞋问了我一个问题:
function Foo(firstName, lastName){ this.firstName = firstName; this.lastName = lastName; } Foo.prototype.logName = function(){ Foo.combineName(); console.log(this.fullName); } Foo.prototype.combineName = function(){ this.fullName = `${this.firstName} ${this.lastName}` } var foo = new Foo('Sanfeng', 'Zhang'); foo.logName(); // Uncaught TypeError: Foo.combineName is not a function
小英童鞋认为Foo
的原型对象是Foo.prototype
,所以Foo
会继承Foo.prototype
的属性,调用Foo.combineName()
相当于调用Foo.prototype.combineName()
,但结果Foo.combineName()
不是一个方法。
会造成这个问题的原因一定是因为小英童鞋弄混了原型和继承的一些原理,下面我们来整理一下原型和继承的相关原理,找出问题的根本原因。
prototype
prototype
是一个拥有 [[Construct]] 内部方法的对象才有的属性。
例如函数,对象的方法,ES6 中的类。注意 ES6 中的箭头函数没有 [[Construct]] 方法,因此没有prototype
这个属性,除非你为它添加一个。
当创建函数时,JavaScript 会为这个函数自动添加prototype
属性,这个属性指向的是一个原型对象Functionname.prototype
。我们可以向这个原型对象添加属性或对象,甚至可以指向一个现有的对象。
__proto__
接下来我们说说继承,每个对象都有一个__proto__
属性,这个属性是用来标识自己所继承的原型。
注意: JavaScript 中任意对象都有一个内置属性 [[Prototype]] ,在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__
来访问。以下统一使用__proto__
来访问 [[Prototype]],在实际开发中是不能这样访问的。
JavaScript 可以通过prototype
和__proto__
在两个对象之间创建一个关联,使得一个对象就可以通过委托访问另一个对象的属性和函数。
这样的一个关联就是原型链,一个由对象组成的有限对象链,用于实现继承和共享属性。
构造函数创建对象实例
JavaScript 函数有两个不同的内部方法:[[Call]] 和 [[Construct]] 。
如果不通过new
关键字调用函数,则执行 [[Call]] 函数,从而直接执行代码中的函数体。
当通过new
关键字调用函数时,执行的是 [[Construct]] 函数,它负责创建一个实例对象,把实例对象的__proto__
属性指向构造函数的prototype
来实现继承构造函数prototype
的所有属性和方法,将this
绑定到实例上,然后再执行函数体。
模拟一个构造函数:
function createObject(proto) { if (!(proto === null || typeof proto === "object" || typeof proto === "function"){ throw TypeError('Argument must be an object, or null'); } var obj = new Object(); obj.__proto__ = proto; return obj; } var foo = createObject(Foo.prototype);
至此我们了解了prototype
和__proto__
的作用,也了解使用构造函数创建对象实例时这两个属性的指向,以下使用一张图来总结一下如何通过prototype
和__proto__
实现原型链。
从上图我们可以找出foo
对象和Foo
函数的原型链:
foo.__proto__ == Foo.prototype; foo.__proto__.__proto__ == Foo.prototype.__proto__ == Object.prototype; foo.__proto__.__proto__.__proto__ == Foo.prototype.__proto__.__proto__ == Object.prototype.__proto__ == null;
Foo.__proto__ == Function.prototype; Foo.__proto__.__proto__ == Function.prototype.__proto__; Foo.__proto__.__proto__.__proto__ == Function.prototype.__proto__.__proto__ == Object.prototype.__proto__ == null;
构造函数Foo
的原型链上没有Foo.prototype
,因此无法继承Foo.prototype
上的属性和方法。而实例foo
的原型链上有Foo.prototype
,因此foo
可以继承Foo.prototype
上的属性和方法。
到这里,我们可以很简单的解答小英童鞋的问题了,在Foo
的原型链上没有Foo.prototype
,无法继承Foo.prototype
上的combineName
方法,因此会抛出Foo.combineName is not a function
的异常。要想使用combineName
方法,可以这样Foo.prototype.combineName.call(this)
,或者这样this.combineName()
(this
指向实例对象)。
欢迎关注:Leechikit
原文链接:segmentfault.com到此本文结束,欢迎提问和指正。
写原创文章不易,若本文对你有帮助,请点赞、推荐和关注作者支持。
Recommend
-
90
海淘的税与罚,傻傻分不清?来,干货长文一篇帮你全撸明白! ...
-
52
-
38
最近半年我写了很多Web方面的文章,看上去很简单,但如果深究下去,则不是那么一回事了,今天聊聊url编码的事情。 很多人对urlencode编码很熟悉,但对rawurlencode却了解不多,其实对于URL编码,rawurlencode才是标准,它定义在...
-
22
什么是认证(Authentication) 通俗地讲就是验证当前用户的身份,证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统里录入的指纹相匹配时,就打卡成功) 互联网中的认证: 用户名密码登录 邮箱发送登录链接 手机号接收验
-
10
进程、线程与协程傻傻分不清?一文带你吃透 (查看原文) 前言 欢迎来到操作系统系列,依然采用图解 + 大白话的形式来讲解...
-
14
GitHub 19k Star 的Java工程师成神之路,不来了解一下吗! 在我们的 java 业务系统中,或多或少的会涉及到权限、认证等类似的概念。但是很多小伙伴还是傻傻的分不清这些的概念和...
-
5
编辑导语:数据行业从业者,你是不是时常能听到数据中台和数据平台,一字之差的它们,差别在哪里呢?这是令很多人纠结的概念,作者用一篇文章解析这两个概念的含义,当我们面试时被问到也能够轻松应答,希望对你有帮助。
-
14
flows channels 傻傻分不清
-
6
Spring 的 BeanFactory 和 FactoryBean 傻傻分不清?发布于 今天 14:00 ApplicationContext接口,它由BeanFactory接口派生而来。ApplicationContext包含Bean...
-
9
最近新写了一个中间件「运行时动态日志等级开关」,其中使用Java SPI机制实现了自定义配置中心,保证良好的扩展性。 项目地址,走过路过可以点个star :)https://github.com/saigu/LogLevelSwitch 在使用过...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK