什么是原型(Prototype)?
在JavaScript中,每个函数都会有一个prototype属性,这个属性是一个对象,我们称之为原型对象。当我们使用构造函数创建一个新对象时,新对象会继承构造函数的prototype对象的属性和方法。换句话说,prototype是一个用于创建新对象的模板。
你可以这样创建一个原型:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log("Hello, my name is " + this.name);
}
在这个例子中,Person是一个构造函数,用于创建新的Person对象。Person.prototype是一个原型对象,它包含所有Person对象共享的方法,如greet方法。当你创建一个新的Person对象时,这个对象将继承Person.prototype的所有属性和方法。
当我们使用new关键字去实例化该Person对象时,我们可以这样。
var p = new Person('李四',21)
p.greet() // 输出:Hello, my name is 李四
greet方法是在Person.prototype上定义的,但是p对象可以直接访问并调用它,这是因为p对象的[[Prototype]]内部属性指向了Person.prototype,而什么是[[Prototype]]内部属性呢?又为什么指向Person.prototype呢,我们是不是对new有很多疑问呢?
其实看过我手写new的文章(深入理解JavaScript的new关键字与自定义 - 掘金 (juejin.cn))就知道了,文章会教你如何实现new实例化对象的过程,其中就包含了一条那就是 实例对象的__proto__属性必须指向构造函数的原型(prototype),也就是这里的(p.proto = Person.prototype),上面的[[Prototype]]内部属性指的就是对象属性__proto__。
简单介绍
[[Prototype]] 这种形式是谷歌浏览器新版本独特的写法。__proto__ 和Prototype都称为原型,但__proto__是隐式原型,Prototype是显式原型,两者表现出来的功能不同。__proto__是每个对象都拥有的内部属性(注:Object.create( )函数创建的对象没有该内部属性),而对于函数对象来说。它有特殊的prototype显示原型,也因它是对象,所以也拥有隐式原型__proto__。
什么是原型链?
当一个对象查找某个属性或方法时,如果该对象自身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找。原型链是由一系列原型对象链接而成的链表,每个对象的[[Prototype]]属性指向它的原型对象。
JavaScript中的每个对象都有一个隐式的[[Prototype]]属性(除了null和原始类型),这个属性指向其构造函数的prototype对象。如果在原型对象中也没有找到对应的属性或方法,JavaScript将继续沿着原型链向上查找,直到到达原型链的终点,也就是Object.prototype。Object.prototype是所有对象的原型,如果在这个点仍然没有找到所需的属性或方法,那么查找过程结束,返回undefined。
下面我们看一道例题来实现原型链吧:
Grand.prototype.lastName = '张'
function Grand(){
this.name = '三'
}
function Father(){
this.age = 40
}
function Son(){
this.like = 'coding'
}
let son = new Son();
console.log(son.like) // 打印 coding
那么我们该如何让son去拿到Father的age和Grand的name属性呢?
这里我们知道 son 是Son的实例化对象,son的隐式原型__proto__指向Son的原型prototype,继承来的like属性是来自Son.prototype。
那么我们是不是想:如果Son能够继承Father的属性就好了,我们可以看看let son = new Son();,当new一个构造函数时,我们就会返回一个对象,且这个对象可以继承构造函数的方法,又因为 Son.prototype 是一个对象,所以我们试一下Son.prototype = new Father();,结果如下面:
结果我们就可以看到,我们拿到了Father的age属性了,再按这个道理让 Father.prototype = new Grand() ,这样Father就继承了Grand的方法和属性。
{
like : 'coding'
__proto__: Son.prototype == new Father() : {
__proto__:Father.prototype == new Grand(): {
__proto__: Grand.prototype = new Object();
}
}
}
原型链的指向就如上面代码所示,这样一条原型链就建好了。
原型链图
下面我们可以看一张原型链的图,如果我们可以对它进行全面的解释(用自己的话),那么我们对原型和原型链这个知识点就彻底掌握了哦。
相信认真读完上面内容的你,对原型和原型链有深刻的理解了,我们现在来一起讨论一下这个图吧!
从左上角f1开始:
- 我们可以看到实例对象f1由构造函数foo实现,所以其隐式原型指向foo的显式原型。
Foo.prototype的constructor指向其构造函数,实例对象是通过constructor来记录我是谁产生的,而Foo.prototype就是一个对象它是由函数对象Foo产生,所以指向function Foo()。- 函数对象Foo的显式原型 就是等于 Foo.prototype。
- 对于每个函数对象而言都有隐式原型和显式原型,所以函数对象Foo也有隐式原型,它的默认隐式原型就是指向它的构造函数上,也就是指向Function.prototype.
- 再看
Foo.prototype的指向,它叫Foo的显式原型,但它是一个对象,有隐式原型,由于原型链的关系,所有的对象的隐式原型都指向最终的Object的显式原型,所以最终Foo的显式原型的隐式原型__proto__指向了Object.prototype。 - 最终的Object.prototype是原型链的顶端了,它的隐式原型默认指向null。
所以接下来的o1实例对象也是一样的分析,这个就留给小伙伴们自己分析总结了,其实都是一样的。
不知道我分析的怎么样呢?对你的学习有帮助吗?如果对你的学习有帮助,请别吝啬你的小赞赞哦!谢谢你的点赞和关注。