什么是原型
所有函数都有的一个属性,这个属性的值是一个对象,这个对象叫作原型对象。
这个属性是prototype,通过这个属性可以访问这个原型对象。
函数访问原型
//创建People构造函数
function People(){}
//访问People的原型对象(prototype属性)
console.log(People.prototype);
根据返回结果可以看出,People.prototype这个属性返回一个对象,这个对象中有一个属性constructor,该属性的值是People这个构造函数。
对象访问原型
所有对象都有__proto__属性,这个属性的值是创建这个对象的构造函数的原型对象。
这个属性是隐式原型.
与prototype不同的是,__proto__给浏览器识别的属性,它不是ECMAscript的语法规范,不推荐使用。
我们可以通过Object.getPrototype()这个方法访问对象的隐式原型对象
var obj = new Object();
console.log(Object.getPrototype(obj));
Object构造函数创建obj所以obj的隐式原型对象就是Object.prototype
我们可以看一下是否是Object.prototype
console.log(Object.getPrototypeOf(obj) === Object.prototype);
由此可见,我们可以用Object.getPrototypeOf(obj)访问对象的原型对象
原型链
原型链是由多个原型相互联系形成的链式结构。
它与作用域链有些相似
我们可以创建一个简单的原型链
//创建一个构造函数
function People(){}
//实例化一个对象
var p1 = new People();
此时原型链就已形成
p1.proto --> People.prototype -->Object.prototype --> null
p1的隐式原型对象是People.prototype
People.prototype的隐式原型对象是Object.prototype
Object.prototype的隐式原型是null
原型链的作用
通过原型链可以访问原型上的属性和方法
该作用扩展了对象的可以访问的属性和方法的范围,同时也可以减少相同函数多次书写的情况,可以实现函数声明一次,在多个对象上调用的效果
//创建一个构造函数
function People(name){
this.name = name;
}
//在People的原型上添加move方法
People.prototype.move = function(){
console.log(this.name+'在移动');
}
//实例化一个对象
var p1 = new People('Mach');
p1.move();
通过上面的例子可以看到,p1本身没有move方法但是却能调用move方法就是因为原型链扩展了p1的访问范围,使得p1能够访问People.prototype上的方法
同时也说明了能够减少相同代码的书写,当实例化多个对象时,每个对象都可以通过原型链访问move方法,不必每个对象都重新创建一个move方法
继承
js中的继承与面向对象中的继承有些差异,其通过原型链实现继承
下面进行简单的继承
function People(){}
People.prototype.move = function(){
console.log(this.name+'在移动');
}
function Man(name){
this.name = name;
}
Man.prototype = People.prototype;
var p = new People();
var m = new Man('m');
m.move();
由此我们可以说m继承了People.prototype上的方法
但是这种方法有一些缺点,当在Man.prototype上进行更改时,People.prototype中的属性也会被更改
实例如下
Man.prototype.sleep =function(){
this.name+'我在睡';
}
console.log(People.prototype);
由此可见,People.prototype中有一个sleep方法,因此说明Man.prototype的更改对People.prototype产生了影响
那么怎样才能使Man.prototype不会影响People.prototype呢
我们可以借助第三个构造函数作为隔离
示例如下
function People(){}
People.prototype.move = function(){
console.log(this.name+'在移动');
}
function Man(name){
this.name = name;
}
function Fn(){}
Fn.prototype = People.prototype;
Man.prototype = new Fn();
var p = new People();
var m = new Man('m');
m.move();
Man.prototype.sleep =function(){
this.name+'我在睡';
}
console.log(People.prototype);
此时People.prototype中并没有sleep方法,因此将People.prototype与Man.prototype隔离开,使得两者不会相互影响。
总结
- prototype属性是每个函数都有的一个属性,该属性的值是一个对象
- proto 是每个对象都具有的属性,该对象指向其构造函数的prototype,但是该属性不是规范,不推荐直接使用,可以用Object.getPeototypeOf()方法代替
- 可以通过原型链实现继承,也可以扩展对象的可访问范围