说说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
的关系。
本文是笔者对原型对象和原型链的理解,如有错误或不足的地方,欢迎指正。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Iterative Methods for Sparse Linear Systems, Second Edition
Yousef Saad / Society for Industrial and Applied Mathematics / 2003-04-30 / USD 102.00
Tremendous progress has been made in the scientific and engineering disciplines regarding the use of iterative methods for linear systems. The size and complexity of linear and nonlinear systems arisi......一起来看看 《Iterative Methods for Sparse Linear Systems, Second Edition》 这本书的介绍吧!