原型与原型链一直是JavaScript的重难点,掌握这部分内容将会使我们的工作更加的高效,而且这也是面试官必问的内容。
首先,我们要明确的是,在 ES6 之前,我们创建一个实例并不是通过类(class
),而是直接使用构造函数来实现的。
一、构造函数
通过 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
对象。
构造函数简单复习一下,下面步入正题。
二、原型
prototype
在JS中,每当定义一个函数时候,都会默认自带一个prototype
属性,这个属性指向的是该构造函数创建的实例的原型,并且这个属性是一个对象数据类型的值。
原型:每一个JS对象(null
除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
举个原型的Demo:
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
表示实例原型。原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
三、原型链
上面,我们说明了实例和实例原型,那么我们该怎么表示这两者之间的关系呢?这时候我们就要说到下面两个属性了
__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 的原型与原型链有了一个深层次的认识吧,其实这部分还是需要细心琢磨的,毕竟是比较底层的原理。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!