示例代码:
// 构造函数 function Fn(){ } // 实例化对象 let p = new Fn(); let obj - new Object();要点:
每个函数(function)都有一个prototype(即:显式原型),它在函数被定义的时候产生。
每个实例对象都有一个__proto__,(即:隐式原型),它在对象被创建时产生。
原型对象中有一个constructor属性,它指向相对应的函数对象。
//验证 console.log(Fn===Fn.prototype.constructor) //true对象的隐式原型的值为其对应的构造函数的显式原型的值。
//验证 console.log(f.__proto__===Fn.prototype) //true所有的函数的__proto__都一样。
//验证 console.log(Function.__proto__ === Fn.__proto__) //true console.log(Object.__proto__===Function.__proto__) //trueFunction()是一个构造函数,并且它的实例化对象也是自己。
//验证 console.log(Function.prototype === Function.__proto__) //true原型链的作用: 当访问一个对象的属性时,先在自身的属性中查找,若找到,则可以访问自身的这个属性。若没有找到,再沿着__proto__这条链向上查找,若找到,则可以访问原型对象中的该属性。若都没能找到,则返回undefined。
原型链图解: 解决图中obj处的问号:
obj = f.__proto__ ;通过这行代码建立了Object()构造函数与第二层的连接,原本他们是没有关联的。
一个简单的应用:(es5 写法)
//构造函数( 父) function Super(name,age) { this.name=name; this.age=age; this.paly1 = function () { console.log('保护地球1') } } // 给构造函数添加方法 Super.prototype.paly2 = function () { console.log('保护地球2') } //构造函数( 子) function Student(name, age) { Super.call(this,name,age) this.eat = function () { console.log('吃蛋糕') } } let p = new Student('匿名', 21) console.log(p) // Student {name: "匿名", age: 21, eat: ƒ} console.log(p.name); // 匿名 console.log(p.play1()) // 报错 只能访问属性,访问不了方法 console.log(p.play2()) // 报错 只能访问属性,访问不了方法分析:我们知道,Student继承了Super的属性,第20行创建的实例对象p,只能调用Super的属性却无法调用Super的方法,要想调用Super的方法,在es5中得采用原型链,如下:
//构造函数 function Super(name,age) { this.name=name; this.age=age; this.paly1=function () { console.log('保护地球1'); } } // 给构造函数添加方法 Super.prototype.paly2 = function () { console.log('保护地球2') } //构造函数 function Student(name, age) { Super.call(this,name,age) this.eat = function () { console.log('吃蛋糕') } } // 充分运用?处的处理方法 // 原型对象发生了改变 let p = new Student('匿名',21) p.__proto__ = new Super(); //最最简单的继承 // p.__proto__.constructor //指向Super,而它本身是Student的实例对象,让它指回来 p.__proto__.constructor = Student; console.log(p) // Student {name: "匿名", age: 21, eat: ƒ} console.log(p.name); // 匿名 console.log(p.paly1()); // 保护地球1 console.log(p.paly2()); // 保护地球2分析:我们看到第29行和30行未报错,因为我们通过原型链的知识,将实例对象p的原型作为Super的实例对象,实现了最简单的继承,但这时p.__proto__.constructor已经指向Super了,但它本身是Student的实例对象,所以得让它指回来,这样才不会乱,毕竟我们的本意只想Student能继承Super的方法,而不是破坏原型链。
图解:
上述原型链的知识,实现了原生js的继承,会稍显麻烦,所以es6通过class创建类并用extends和super(arg1,arg2)实现继承解决了我们运用起来很麻烦的问题:
class Super { constructor(name,age) { this.name=name; this.age = age; } play1 () { console.log('保护地球1') } play2 () { console.log('保护地球2') } } class Student extends Super { constructor(name,age) { // super //超级 可以实现继承 // super 必须放在this前面 super(name,age) } eat () { console.log('吃蛋糕') } } let s = new Student('汪苏泷',30) console.log(s.name) //汪苏泷 console.log(s.play1()) //保护地球1推荐一篇讲原型链的博客,我觉得写得挺好的,可以参考: https://blog.csdn.net/cc18868876837/article/details/81211729