说说JS中的原型对象和原型链
栏目: JavaScript · 发布时间: 5年前
内容简介:理解原型对象(有些文章简称为原型)和原型链,是理解JS的重要一环。下面是笔者对JS中原型的理解,俗话说,JS中万物皆对象。函数也是一个对象,什么是函数对象?每声明一个函数,此函数在JS执行解释时都会被当作一个对象来维护,这就是函数对象。JS中声明函数的方式有:
理解原型对象(有些文章简称为原型)和原型链,是理解JS的重要一环。下面是笔者对JS中原型的理解,
函数对象
俗话说,JS中万物皆对象。函数也是一个对象, 只不过函数是在特定环境中执行代码的对象。
什么是函数对象?每声明一个函数,此函数在JS执行解释时都会被当作一个对象来维护,这就是函数对象。JS中声明函数的方式有:
function fn1(){} var fn2 = function(){} var fn3 = new Function() 复制代码
所以可以理解为fn1、fn2、fn3都是函数对象。JS中还包括一些系统内置的函数对象,比如:
Function Object Array String Number RegExp 复制代码
函数对象之外的对象都是普通对象。函数对象能创建普通的对象,反之则不行。
理解原型对象(其实就一普通对象)
1、只有函数对象才拥有原型对象
也即无论什么时候以什么方式创建一个函数(函数对象),都会根据特定的规则为该函数创建一个prototype属性(原型对象的地址的引用),这个属性就是指向该函数的原型对象。比如:
function Person () {}; console.log(Person.prototype) // Person.prototype就是Person的原型对象,实际是原型对象的内存地址的引用 复制代码看到没有,原型对象并不神秘,就是一个普通的对象,只不过其默认有了
constructor
和
__proto__
(下一节会讲)属性而已(其中
__proto__
不建议在实际中应用,因为在有些浏览器可能并没有实现该属性)。
由上图看出,函数 Person
的原型对象( Person.prototype
)默认拥有一个属性 constructor
,此属性就是用来重新指向函数 Person
。
function Person () {}; Person.prototype.constructor === Person // true 复制代码
2、普通对象与原型对象的关系
一般我们定义一个构造函数(构造函数其实就是普通的函数,只不过目的是创建对象),然后通过 new
操作符来创建一个普通对象。
function Person (name) { this.name = name; this.age = 18; } var xiaoming = new Person('小明'); // {name: '小明', age: 18} var xiaohong = new Person('小红'); // {name: '小红', age: 18} 复制代码
在上述代码中,变量 xiaoming
和 xiaohong
是构造函数 Person
的实例。我们通过上一节知道了 Person
与其原型对象的关系,但实例与构造函数的原型对象有什么关系呢?
每当调用构造函数创建一个实例即普通对象后,该实例将包含一个内部的指针 [[Prototype]]
,这个指针指向的就是构造函数的原型对象。
目前ECMAScript的标准中并没有实现标准的访问该指针的方式,但像Firefox、Chrome和Safari等浏览器实现了 __proto__
属性,此属性就是用来访问指针 [[Prototype]]
,所以可以借用 __proto__
属性展示实例和原型对象的关系。
xiaoming.__proto__ === Person.prototype // true xiaohong.__proto__ === Person.prototype // true 复制代码
3、总结上述两小节
每创建一个函数,就会为相应的函数创建一个 prototype
的属性,这个属性指向了函数的原型对象,这个函数的原型对象会默认拥有一个 constructor
属性,此属性指向了对应的函数。而使用 new
操作符调用函数创建出来的实例,会拥有一个内部的指针 [[Prototype]]
,此指针指向函数的原型对象。
千言万语不如一幅图:
原型链
由上节我们可以知道,原型对象上的属性和方法被所有实例所共享的。每当访问一个对象的属性或者方法时,会首先搜索对象自身,如果找到了此属性或者方法,则直接返回,否则向对应的原型对象上面搜索,如果找到则直接返回,否则继续向原型对象的原型对象上查找,直到搜索到null,抛出错误或返回 undefined
。
function Person (name) { this.name = name; this.age = 18; } Person.prototype.sayName () { // 在Person的原型对象上添加的方法,被所有实例共享 console.log(this.name); } var xiaoming = new Person('小明'); // {name: '小明', age: 18} xiaoming.sayName(); // 小明 复制代码
上面代码中,实例 xiaoming
本身并没有 sayName
方法,但却成功调用了。 其实就是通过实例内部的 [[Prototype]]
指针去原型对象 Person.prototype
上找对应的方法,然后调用。
如果我调用一个实例本身和原型对象都没有的方法,其过程是怎么样的呢?
xiaoming.sayAge() // 实例本身和原型对象都不存在的方法 复制代码
(1)首先搜索 xiaoming
这个对象,并没有 sayAge
方法,
[[Prototype]]
指针)。没有找到
sayAge
方法
(3)继续向原型对象的原型对象上搜索,即xiaoming.__proto__.__proto__
。也没有找到
sayAge
方法。
(4)继续向原型对象的原型对象的原型对象上搜索,即xiaoming.__proto__.__proto__.__proto__
,但发现
xiaoming.__proto__.__proto__.__proto__
为
null
,停止搜索,抛出错误或返回
undefined
。
如果原型对象和实例上具有同名的属性或方法,则搜索时取最近的。
如上述的原型链的搜索机制,你通过阅读本文知道 xiaoming.__proto__
是 Person.prototype
,但 xiaoming.__proto__.__proto__
呢? 不说话看图:
由此,可得到下面的关系图:
思考
原型链中的关系图其实还缺少一环,就是内置函数 Function
。 Function
比较特殊,有兴趣的可以去研究下 Function
与 Object
的关系。
本文是笔者对原型对象和原型链的理解,如有错误或不足的地方,欢迎指正。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
ES6标准入门(第3版)
阮一峰 / 电子工业出版社 / 2017-9 / 99.00
ES6是下一代JavaScript语言标准的统称,每年6月发布一次修订版,迄今为止已经发布了3个版本,分别是ES2015、ES2016、ES2017。本书根据ES2017标准,详尽介绍了所有新增的语法,对基本概念、设计目的和用法进行了清晰的讲解,给出了大量简单易懂的示例。本书为中级难度,适合那些已经对JavaScript语言有一定了解的读者,可以作为学习这门语言最新进展的工具书,也可以作为参考手册......一起来看看 《ES6标准入门(第3版)》 这本书的介绍吧!