示例代码:
// 构造函数
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__) //true
-
Function()
是一个构造函数,并且它的实例化对象也是自己。//验证 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.youkuaiyun.com/cc18868876837/article/details/81211729