理解原型对象(有些文章简称为原型)和原型链,是理解JS的重要一环。下面是笔者对JS中原型的理解,浏览器
俗话说,JS中万物皆对象。函数也是一个对象,只不过函数是在特定环境中执行代码的对象。bash
什么是函数对象?每声明一个函数,此函数在JS执行解释时都会被看成一个对象来维护,这就是函数对象。JS中声明函数的方式有:函数
function fn1(){}
var fn2 = function(){}
var fn3 = new Function()
复制代码
因此能够理解为fn一、fn二、fn3都是函数对象。JS中还包括一些系统内置的函数对象,好比:this
Function Object Array String Number RegExp
复制代码
函数对象以外的对象都是普通对象。函数对象能建立普通的对象,反之则不行。spa
也即不管何时以什么方式建立一个函数(函数对象),都会根据特定的规则为该函数建立一个prototype属性(原型对象的地址的引用),这个属性就是指向该函数的原型对象。好比: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
复制代码
通常咱们定义一个构造函数(构造函数其实就是普通的函数,只不过目的是建立对象),而后经过new
操做符来建立一个普通对象。code
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
与其原型对象的关系,但实例与构造函数的原型对象有什么关系呢?cdn
每当调用构造函数建立一个实例即普通对象后,该实例将包含一个内部的指针[[Prototype]]
,这个指针指向的就是构造函数的原型对象。对象
目前ECMAScript的标准中并无实现标准的访问该指针的方式,但像Firefox、Chrome和Safari等浏览器实现了__proto__
属性,此属性就是用来访问指针[[Prototype]]
,因此能够借用__proto__
属性展现实例和原型对象的关系。
xiaoming.__proto__ === Person.prototype // true
xiaohong.__proto__ === Person.prototype // true
复制代码
每建立一个函数,就会为相应的函数建立一个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
方法
xiaoming.__proto__.__proto__
。也没有找到
sayAge
方法。
xiaoming.__proto__.__proto__.__proto__
,但发现
xiaoming.__proto__.__proto__.__proto__
为
null
,中止搜索,抛出错误或返回
undefined
。
若是原型对象和实例上具备同名的属性或方法,则搜索时取最近的。
如上述的原型链的搜索机制,你经过阅读本文知道xiaoming.__proto__
是Person.prototype
,但xiaoming.__proto__.__proto__
呢? 不说话看图:
原型链中的关系图其实还缺乏一环,就是内置函数Function
。Function
比较特殊,有兴趣的能够去研究下Function
与Object
的关系。
本文是笔者对原型对象和原型链的理解,若有错误或不足的地方,欢迎指正。