在js 中讲到 面向对象,就离不开原型 原型链 继承 等等概念;那么这些具体是什么,我们一起来深入理解。
function Dog(name){
this.name= name ;
this.barking = '汪汪汪';
}
var dog1 = new Dog('大狗');
var dog2 = new Dog ('二狗');
console.log(dog1);//{name:'大狗',barking:'汪汪汪'}
console.log(dog2); //{name:'二狗',barking:'汪汪汪'}
dog1.barking='汪汪';
console.log(dog1);//{name:'大狗',barking:'汪汪'}
console.log(dog2); //{name:'二狗',barking:'汪汪汪'}
从上面的代码 我们先声明了一个Dog 的 构造函数, 对Dog 使用new 关键字生成 对应的实例对象,从上面修改其中一个实例对象的属性,另一个不受影响。其实,每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费
javascript 的设计者 希望可以共享需要的方法和属性 ,此时 ,现在去掉实例中的 Barking 属性, 添加 prototype .
function Dog(name){
this.name= name ;
}
Dog.prototype = { Barking : '汪汪汪,汪汪' }
console.log(dog1.Barking) // '汪汪汪,汪汪'
console.log(dog2.Barking) // '汪汪汪,汪汪'
现在, Barking属性放到原型对象中, 只需要修改原型中的Barking , 两个实例都能共享发生变化。
2. 原型链 :讲到原型 ,离不开原型链 , 讲到原型链离不开 _proto_ ;
原型链是在Object.create() 或 DOG.prototype 创建原型对象时,生成的 _proto_ 指针来实现的 。
如上例 实例对象dog1 的 _proto_ 指针, 指向了构造函数的原型对象Dog.prototype,原型对象上的_proto_ 指Object.prototype ;
以上例 说明 当访问dog1上的Barking 属性 , 会先在dog1 上自有属性上找, 如果没有找到会通过_proto_指针 在 Dog.prototype ,找到了return 对应的值, 没有找到会继续沿着_proto_ 在 ,Object.prototype ,终点为null 。
以上可以描述为 xxx.__proto__.__proto__.__proto__ ,通过_proto_ 指针 形成一个访问属性 或者方法的链 即为原型链;
总结一下原型链作用:对象属性的访问修改和删除。
- 访问。优先在对象本身查找,没有则顺着原型链向上查找
- 修改。只能修改跟删除自身属性,不会影响到原型链上的其他对象。
为了更好理解 原型可以参考下图
3.继承
es5 以及更早的版本是通过 上方原型的方式进行继承, 组合继承 :
function Father (name){
this.name= name;
}
Father.prototype.say=function (){
console.log('my name is ' + this.name);
}
function Child (name){
Father.call(this,name); // 继承 构造函数Father的属性
}
Child.prototype = new Father() ; Child 原型中的_proto_指针会指向 Father.prototype, 继承 构造函数Father 的原型;
或者 :
//Child.prototype= Object.create(Father.prototype,{
//constructor:{
// value:Child,
// enumerable:false,
// writable:true,
// configurable:true,
// }
// })
var children1 = new Child('Tom');
children1.say(); // my name is Tom
寄生组合继承
function ParentFn(name,age){
this.color='red'
this.name=name;
}
ParentFn.prototype.sayName= function(){
console.log(this.name);
}
function ChildFn(parentName,childName){
ParentFn.call(this, parentName);
this.childName = childName;
}
ChildFn.prototype=Object.create(ParentFn.protoType);
ChildFn.prototype.constructor= ChildFn;
最后children1 访问say方法 , 先在自身找, 再通过 _proto_ 在Child.prototype 找, 再通过 Child.prototype的_proto_在Father 的原型上找到say 方法 调用。
注释部分的 的原型继承(寄生式继承) : 其核心 把父类的 原型方法 赋值给了子类的原型, 并且把构造函数设置为子类, 这样避免,解决了父类构造函数中无用的属性,还可以正确找到子类的构造函数。
es6 中采用 class, extends 关键字实现继承, 使用起来非常方便,
class Parent {
constructor(name){
this.name=name;
}
getName(){
console.log(this.name)
}
};
class Child extends Parent {
constructor(name){
super(name);
}
};
var children1 = new Child('Tom');
children1.getName(); // Tom