0
点赞
收藏
分享

微信扫一扫

JavaScript是如何实现继承


JavaScript是如何实现继承


前言:大多OO语言都支持两种继承方式: 接口继承和实现继承.
而ECMAScript中无法实现接口继承,ECMAScript只支持实现继承.
而且其实现继承主要是依靠 原型链 来实现。


构造函数,原型,实例之间的关系


  • 每个构造函数都有一个原型对象
  • 原型对象包含一个指向构造函数的指针(这句话特别重要)
  • 实例都包含一个指向原型对象的内部指针
    每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针(prototype),而实例则包含一个指向原型对象的内部指针(​proto​)。

1、利用原型链


基本思想:利用原型,让一个引用类型继承另外一个引用类型的属性和方法。
可以简单的理解为,让当前类的原型对象 = new Other();
这样原型对象包含一个指向构造函数的指针,就是指向父类new的构造指针。
所有父类的this相当于就是子类的this啦,父类的属性方法会被覆盖。


例子:

function SuperType() {
this.property = true;
this.superName='superName';
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.property = false;
this.subName='subname';
}
//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.property;
}
var instance = new SubType();
//子类拥有相同的属性,当前的上下文为子类,所以父类的属性property被覆盖。
console.log(instance.getSuperValue());//false
console.log(instance.superName);//superName
console.log(instance.subName);
console.dir(instance);
//父类的属性和方法,都在__proto__

2、借用构造函数


基本思想:在子类型构造函数的内部调用超类构造函数.
通过使用call()和apply()方法可以在新创建的对象上执行构造函数
因为call和apply方法的目的就是复用当前存在类中的方法或者属性,构造也是不在话下
所以父类中的this被改变为当前类的this,然后执行环境就是当前的执行环境。

总之call和apply既可以用到构造函数里面也可以用到普通函数里面
但有一点是他们都不能继承原型链(不能继承原型链)
估计和new不一样吧,使用new的时候会把当前构造函数的原型对象指向this指针
但是call的时候没有原型对象所以原型的方法不能够去调用


例子:

function SuperType() {
console.log(this);//sub...
this.property = true;
this.superName='superName';
this.getSuper = function(){
return this.property;
}
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.property = false;
this.subName='subname';
//子类的property先定义,被后来的覆盖了
SuperType.call(this);
}
SubType.prototype.getSubValue = function (){
return this.property;
}
var instance = new SubType();
console.log(instance.getSuper());//true;
console.log(instance.superName);//superName
console.log(instance.property);//true
console.dir(instance);
//父类的属性和方法,都在实例对象的方法和属性上.

但是不能调用父类的原型方法,比如:

估计是一个函数就一个原型对象,在构造中在此使用原型对象不太合理。

这个我还得看看书,慢慢的理解。

//Uncaught TypeError: instance.getSuperValue is not a function
console.log(instance.getSuperValue());

3、组合继承(借用构造继承和原型链结合使用)


第一使用原型链的时候不可以向父类传递参数。
第二使用借用构造的时候不能继承原型链。
所有结合一起的意思就是既可以访问原型链,又可以向父类传递参数。


优势:可以传递参数,可以访问原型链

例子:

function SuperType(property,name) {
this.property = property;
this.superName=name;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType(property,name,age) {
this.age = age;
//传递参数
SuperType.call(this,property,age);

}
SubType.prototype = new SuperType();//函数可以不用传递参数的,这里只是传递原型链
SubType.prototype.getSubValue = function (){
return this.property;
}
var instance = new SubType('ugly','wangji','24');
console.log(instance.property);//ugly
console.log(instance.age);//24
console.log(instance.getSuperValue());//可以访问原型链 ugly
console.dir(instance);//自己在控制台上打打试试

4、原型式继承


基本想法:借助原型可以基于(已有)的对象创建新对象,同时还不必须因此创建自定义的类型。

我自己的理解,将一个已经创建好的对象的属性和方法放置在心创建的对象的原型指针上。
创建一个对象:console.dir()可以显示一个对象所有的属性和方法。


var Person = {
a:"wangji",
getName:function(){
return this.name;
}
}
console.dir(Person);
console.log(Person.getName())

原型式继承:将已经创建好的对象的所有属性全部放置在新的对象的原型指针上

function object(o) {
function F(){};
F.prototype = o;
return new F();
}

测试:

var test1 = object(Person);
console.dir(test1);

var test2 = object(Person);
test2.setName("jet");
console.dir(test1);//对象原型中没有改变
console.dir(test2);//对象原型_proto_没有改变
//但是设置的setName,因为this.a= name; 所以对象的属性中增加了一个a='jet';


总结:感觉这个很第一个利用原型链一样的,只是一个是创建好的对象,一个是没有创建的对象,实质是一样的。最好自己通过控制台打出来自己看一下,方便理解。


再次理解将Person改变一下:

var Person = {
a:"wangji",
getName:function(){
return this.name;
},
setName:function(name){
return this.a =name;
},
friends:["111",'222']//增加这个试一下。
}

测试2:

var test1 = object(Person);
var test2 = object(Person);
test2.friends.push("333");
//这里的push是使用原型属性中的
//并没有使用this指向当前的环境
//因为原型的属性对于所有的创建的实例是共享的
//所以下面两个打印的效果是一样的,原型属性friends中增加了333
//因为原型的指针只是一个引用,所有的实例共享。
console.dir(test1);
console.dir(test2);
//自己在控制台上打打试试


ECMAScript5通过新增Object.create()方法规范化了原型式继承。
这个方法接收两个参数:
一个用作新对象原型的对象
一个作为新对象定义额外属性的对象。


var Person = {
a:"wangji",
getName:function(){
return this.name;
},
setName:function(name){
return this.a =name;
},
friends:["111",'222']
}

var test1 = Object.create(Person,{sex:{
value:"男"
}});
var test2 = Object.create(Person);
test2.friends.push("333");
console.dir(test1);//在对象上增加了一个sex的属性,其他和test2一样的
console.dir(test2);

create and new 的区别

例子:自己打印出来看看!

function Person(){
this.name = "wangji";
this.age =24;
}
Person.prototype.getName = function(){
return this.name;
}
var person = new Person();
//name and age 都在person的属性上,不再原型上
console.dir(person);

//通过crate的,所以的name,age都在createObject的原型上
var createObject = Object.create(person);
console.dir(createObject);

4、寄生式继承


基本思想:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真正是它做了所有工作一样返回对象。


先看个例子:

function object(o) {
function F(){};
F.prototype = o;
return new F();
}
var person ={
age:24,
name:'wangji'
};
var clone = object(person);
clone.age=34;
console.dir(person);
console.dir(clone);

ojbject 函数将person的属性等全部放置在原型链上,有点像原型链式的继承。

原型链的例子:

function createAnother(original) {
var clone = object(original);
clone.sayHi = function () {
alert("hi");
};
return clone;
}
var person = {
name:"EvanChen",
friends:["Shelby","Court","Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();///"hi"
console.dir(person);
console.dir(anotherPerson);


将对象以前的方法继承下来,放置在原型链上,然后在增加新的方法增强以前的对象,返回新的对象。
打印出来是否发现两个不一样,一个在原型链上,一个在对象的属性上


6、寄生组合式继承


基本思想:通过借用函数来继承属性,通过原型链的混成形式来继承方法

两个定义好的函数,用来继承,并不是用对象来实现继承,和之前讲的混合模式一样。


function object(o) {
function F(){};
F.prototype = o;
return new F();
}

//其实也就是将父类的原型链和子类相关联
function inheritProperty(subType, superType) {
var superPrototype = object(superType.prototype);//创建对象
superPrototype.constructor = subType;//增强对象
subType.prototype = superPrototype;//指定对象
}

function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function (){
alert(this.name);
};
function SubType(name,age){
//继承父类的非原型的方法,这个和之前的混合模式一样
SuperType.call(this,name);
this.age = age;
}
inheritProperty(SubType,SuperType);
SubType.prototype.sayAge = function() {
alert(this.age);
}

var superTest = new SuperType('wangji');
console.dir(superTest);
var sub = new SubType("wangji",24);
console.dir(sub)

7、参考


​​http://www.jb51.net/article/75714.htm​​​​http://www.jb51.net/article/81766.htm​​



举报

相关推荐

0 条评论