JavaScript原型链
要搞清楚原型链,我们首先了解什么是
原型?每个函数都有prototype属性,我们称之为原型,又因为这个属性的值是一个对象,所以我们也称为原型对象。那么什么是
对象呢?这里我们要了解在JavaScript中对象是一种引用数据类型。我们可以通过很多种方式来创建一个对象。我们常用构造函数的方式来创建一个对象:
//构造函数
function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
//生成实列
const p = new People('小明',16,'男')
那么,函数与对象的关系是怎么样的呢?
- 函数是对象,对象都是通过函数创建的。
- 函数与对象并不是简单的包含与被包含的关系。
在我们上面创建的对象中,通过定义常量实例化后,在原来
构造函数中通过this赋值的方法或者属性,已经在实例化后成为了每个实例的实例属性和实例方法,无法共享公共属性和方法。所以设计出了一个原型对象,来存储这个构造函数的公共属性及方法。
我们再下面一段代码了加深一下理解:
// 构造函数
function Preson(name, age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 所有实例共享的公共方法
Preson.prototype.say = function (word) {
console.log(`${this.name}说:${word}`);
}
const p1 = new Preson('张三', 18,'男'); // 创建一个Person实例对象
p1.hasOwnProperty('say') // false 说明不是定义在其本身上的
p1.say('hello world'); // 调用公共方法 打印:张三说:hello world
可以看到我们实例对象实例
p1调用到Person这个构造函数的原型对象上的say()方法。但是为什么呢?明明只有在构造函数内部通过this来赋值的属性或者方法才会被实例所继承,为什么在构造函数的原型对象上定义的say方法也能通过实例来调用到呢?这里就引出了原型链这个概念。
实际上
原型对象中存在的构造函数指向了我们function创建的对象。并且每个对象都有一个__proto__属性,这个属性会指向实例对象的构造函数的原型对象,这样使得彼此之间存在一定的指向关系,能够使每个实列化对象能够使用构造函数的原型对象的方法。
而这种指向关系,我们就称之为原型链。图解如下:
我们可以看到,它的
__proto__属性指向了一个function Function的原型对象,该原型对象为JS中所有函数的原型对象,而其__proto__属性也还是指向了function Object的原型对象,所以验证了原型链的尽头为null,这一说法。
在开发的时候,要注意不要通过
实例对象去改变其构造函数的原型对象,这样会对其他通过该构造函数生成的实例对象造成影响。
//开发中需注意点
// 构造函数
function Preson(name, age) {
this.name = name;
this.age = age;
}
// 所有实例共享的公共方法
Preson.prototype.say = function (word) {
console.log(`${this.name}说:${word}`);
}
const p1 = new Preson('张三', 18); // 创建一个Person实例对象
const p2 = new Preson('李四', 20); // 新创建一个Proson实例对象
p1.say('hello world'); // 调用公共方法
p1.hasOwnProperty('say') // false 说明不是定义在其本身上的
p1.__proto__.do = function () {
console.log('往原型对象中添加方法');
}
p2.do(); // 打印出了-往原型对象中添加方法
原型链的尽头
我们可以看到,
实例对象的__proto__属性指向了一个构造函数的原型对象,该构造函数的原型对象的_proto_属性指向了Object的原型对象,而其Object的原型对象的_proto_属性指向了null,所以验证了原型链的尽头为null,这一说法。