原型与原型链一直是JavaScript的重难点,掌握这部份内容将会使咱们的工做更加的高效,并且这也是面试官必问的内容。javascript
首先,咱们要明确的是,在 ES6 以前,咱们建立一个实例并非经过类(class
),而是直接使用构造函数来实现的。java
经过 new 函数名
来实例化对象的函数叫构造函数。任何的函数均可以做为构造函数存在。构造函数首字母通常大写。面试
那么,咱们使用构造函数来建立一个对象。浏览器
function Person(name , age , sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person("Tony" , 18 , "男");
console.log(person.name); // Tony
复制代码
上面这段代码就是建立一个了Person
的构造函数,在Person
构造函数中,为每个对象都添加了三个属性(name,age,sex
),也就是说构造函数每执行一次就会建立一个新的Person
对象。markdown
构造函数简单复习一下,下面步入正题。函数
prototype
在JS中,每当定义一个函数时候,都会默认自带一个prototype
属性,这个属性指向的是该构造函数建立的实例的原型,而且这个属性是一个对象数据类型的值。ui
原型:每个JS对象(null
除外)在建立的时候就会与之关联另外一个对象,这个对象就是咱们所说的原型,每个对象都会从原型"继承"属性。this
举个原型的Demo:spa
function Person() { }
Person.prototype.name = 'Tony'; // 注意:prototype是函数才会有的属性
var person1 = new Person();
var person2 = new Person();
console.log(person1.name); // Tony
console.log(person2.name); // Tony
复制代码
构造函数和实例原型之间的关系: 在这里
Person.prototype
表示实例原型。原型对象就至关于一个公共的区域,全部同一个类的实例均可以访问到这个原型对象,咱们能够将对象中共有的内容,统一设置到原型对象中。prototype
上面,咱们说明了实例和实例原型,那么咱们该怎么表示这二者之间的关系呢?这时候咱们就要说到下面两个属性了
__proto__
这是每个JS 对象(除了 null
)都具备的一个属性,叫__proto__
,这个属性会指向该对象的原型。
person.__proto__ === Person.prototype // true
复制代码
__proto__
绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype
中,实际上,它是来自于 Object.prototype
,与其说是一个属性,不如说是一个 getter/setter
,当使用 obj.__proto__
时,能够理解成返回了 Object.getPrototypeOf(obj)
。
constructor
每一个原型都有一个 constructor
属性指向关联的构造函数。
Person === Person.prototype.constructor // true
复制代码
当获取 person.constructor
时,其实 person
中并无 constructor
属性,当不能读取到constructor
属性时,会从 person
的原型也就是 Person.prototype
中读取,正好原型中有该属性,因此:
person.constructor === Person.prototype.constructor
复制代码
实例原型与构造函数的关系图: 原型链:在JS中,万物皆对象,对象和对象之间也是有关系得,并非孤立存在的。对象之间的继承关系,在JS 中是经过
prototype
对象指向父类对象,直到指向Object
对象为止,这样就造成了一个原型指向的链条,专业术语称之为原型链。
当咱们读取实例的一个属性或方法时,它会先在对象自身中寻找,若是有则直接使用,若是没有则会去原型对象中寻找,若是找到则直接使用。若是没有则去原型的原型中寻找,一直找到最顶层Object
为止,Object
对象的原型没有原型(Object是JS中全部对象数据类型的基类(最顶层的类)在Object.prototype
上没有__proto__
这个属性),若是在Object
原型中依然没有找到,则返回undefined
。
function Person() {}
Person.prototype.name = 'Tony';
var person = new Person();
// 当咱们给实例对象person添加了name属性,打印 person.name 时,结果为name的值Ken。
person.name = 'Ken';
console.log(person.name) // Ken
// 当咱们删除了person的name属性时,再次读取person.name,从person 对象中找不到name属性就会从person的原型也就是person.__proto__和Person.prototype中查找name属性,由于以前咱们给他添加了,因此找到了 name属性为 Tony。
delete person.name;
console.log(person.name) // Tony
复制代码
咱们能够使用对象的hasOwnProperty()
来检查对象自身中是否含有该属性,若是自身属性存在,则返回 true
,不然为false
;使用in
检查对象中是否含有某个属性时,若是对象中没有可是原型中有,则会返回true
,若是都没有则返回false
。
function Person() {
this.name = 'Tony'
}
Person.prototype.age = 18;
var person = new Person();
console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age')); // false
console.log('name' in person); // true
console.log('age' in person); // true
console.log('a' in person); // false
复制代码
最后,相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线:
用心读完上面的总结,应该会对 JS 的原型与原型链有了一个深层次的认识吧,其实这部分仍是须要细心琢磨的,毕竟是比较底层的原理。