原型链这一个话题,需要和很多概念一起讲,才能串成一个比较系统的知识点。在这其中我们就逃不开要讨论继承、原型对象、构造函数、实例了。
一、构造函数
构造函数是一类特殊的函数,它的作用是用来生成实例对象。想要获得某一类型的构造函数可以用 class.prototype.constructor 来获得,也可以对该属性进行赋值操作。利用构造函数创建实例要用到 new 关键字,这个过程会有以下几步:a. 创建一个对象 b. 将构造函数中的 this 指向该对象的引用 c. 执行构造函数中的代码 d. 返回该对象
二、原型对象(prototype)
在原型链中,原型对象是一个很关键的点。定义一个函数对象的时候,会包含一个预定义的原型对象,即 Object 的实例,所以原型对象是针对函数对象来讲的。
三、prototype vs _proto_
上面就提到一点 prototype 是指构造函数的原型对象,它是一个对象,它是构造函数的属性。而_proto_是一个实例对象的内部属性,用来指向创建该实例的函数对象的原型对象。我们将在第五小结用代码更加直观得明白二者的关系
四、继承
在 JavaScript 中,我们可以用改变构造函数的原型对象来实现继承。
五、原型链
什么时候会出现原型链:当一个构造函数的原型对象(prototype)指向另一个引用类型的实例,该实例的构造函数的原型对象又包含一个指向另一个原型的指针。层层递进,就构成了实例与原型的链条。这也就是形成原型链的基本思路。说个通俗的例子:
// 父亲类构造函数
function Father(){
this.FProp = “father”;
}
Father.prototype.FCall = function(){
console.log(JSON.stringify(this)+” is calling father’s function”);
}
// 儿子类构造函数
function Son(){
this.SProp = “son”;
}
Son.prototype.SCall = function(){
console.log(“I’m a son”);
}
// 通过原型继承
// 注意上面的 SCall() 会失效,因为用了继承,prototype 中的方法和 constructor 被重写
Son.prototype = new Father();
Son.prototype.constructor = Son;
// 生成实例对象
var newInstance = function(){
var tom = new Son();
tom.FCall();
console.log(tom.FProp);
//tom.SCall(); 控制台会报 undefined 错误
console.log(tom.SProp);
console.log(Son.prototype.constructor);
}
代码结果:可以看出子类继承了父类之后,可以获取父类的属性和方法。
var getDifference = function(){
var tom = new Son();
console.log(Son.prototype);
console.log(tom.__proto__);
console.log(tom.prototype);
}
代码结果:对象实例获取 prototype 的结果是 undefined