先放张经典的图,或许在学习原型的路上你已经无数次看见这张图了,还是看不懂没有关系,先放这,看完此博客决定自己又行了就回来看这图
js实现继承的方式是原型链
原型链的定义:
Javascript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
比如
let arr = [3,2,1]
arr = arr.reverse()
console.log(arr)// [1,2,3]
我们没有在arr里定义过一个reverse函数,却能够调用,这一定是因为arr从构造函数那继承来的这一函数
以上面代码为例,原型链的定义所描述的就是:
arr调用reverse方法,而没有在arr里找到,于是就沿着一条路径找,一层层的找
最终在Array的prototype里找到,然后调用
而_proto_和prototype都是实现原型链的工具。所以我们从寻找的过程去理解_proto_和prototype
声明一个Parent 类,然后实例化一个child,打印一下
class Parent {
constructor (name) {
this.name = name;
}
print () {
console.log(this.name);
}
}
let child = new Parent('小明');
console.log(child.__proto__);
console.log(Parent.prototype);
打印结果:
我们发现,child.
proto
与 Parent.prototype这俩玩意一模一样:
child.__proto__ === Parent.prototype
而child执行print函数的过程则是:
1、child对象里没有print函数,于是便在其原型上寻找:child.__ proto __ –> Parent.prototype
2、于是便进入Parent.prototype中寻找print函数,有print函数,调用成功
所以我们可以得到这样一个结论:
1、__ proto__是一个访问器,相当于一个索引,用于找到构造函数的prototype
2、prototype是一个构造器,里面包含了构造函数里的各种属性,方法
例子:
function Person(name) {
this.name = name
}
var p2 = new Person('king');
console.log(p2.__proto__) //Person.prototype
console.log(p2.__proto__.__proto__) //Object.prototype
console.log(p2.__proto__.__proto__.__proto__) // null
console.log(p2.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错
console.log(p2.constructor)//Person
console.log(p2.prototype)//undefined p2是实例,没有prototype属性
console.log(Person.constructor)//Function 一个空函数
console.log(Person.prototype)//打印出Person.prototype这个对象里所有的方法和属性
console.log(Person.prototype.constructor)//Person
console.log(Person.prototype.__proto__)// Object.prototype
console.log(Person.__proto__) //Function.prototype
console.log(Function.prototype.__proto__)//Object.prototype
console.log(Function.__proto__)//Function.prototype
console.log(Object.__proto__)//Function.prototype
console.log(Object.prototype.__proto__)//null