js中继承可以分为两种:对象冒充和原型链方式
一、对象冒充包括三种:临时属性方式、call()及apply()方式
1.临时属性方式
function Person(name){
this.name = name;
this.say = function(){
alert('My name is '+this.name);
}
}
function F2E(name,id){
this.temp = Person;
this.temp(name);
delete this.temp;
this.id = id;
this.showId = function(){
alert('Good morning,Sir,My work number is '+this.id);
}
}
var simon = new F2E('Simon',9527);
simon.say();
simon.showId();
2.call()/apply()方式
实质上是改变了this指针的指向
function Person(name){
this.name = name;
this.say = function(){
alert('My name is '+this.name);
}
}
function F2E(name,id){
Person.call(this,name); //apply()方式改成Person.apply(this,new Array(name));
this.id = id;
this.showId = function(){
alert('Good morning,Sir,My work number is '+this.id);
}
}
var simon = new F2E('Simon',9527);
simon.say();
simon.showId();
缺点:先来看这么一张内存分配图:
在OO概念中,new实例化后,对象就在堆内存中形成了自己的空间,值得注意的是,这个代码段。而成员方法就是存在这个代码段的,并且方法是共用的。问题就在这里,通过对象冒充方式继承时,所有的成员方法都是指向this的,也就是说new之后,每个实例将都会拥有这个成员方法,并不是共用的,这就造成了大量的内存浪费。并且通过对象冒充的方式,无法继承通过prototype方式定义的变量和方法,如以下代码将会出错:
function Person(name){
this.name = name;
this.say = function(){
alert('My name is '+this.name);
}
}
Person.prototype.age = 20;
Person.prototype.sayAge = function(){alert('My age is '+this.age)};
function F2E(name,id){
Person.apply(this,new Array(name));
this.id = id;
this.showId = function(){
alert('Good morning,Sir,My work number is '+this.id);
}
}
var simon = new F2E('Simon',9527);
simon.sayAge(); //提示TypeError: simon.sayAge is not a function
二、原型链方式
function Person(){
this.name = 'Simon';
}
Person.prototype.say = function(){
alert('My name is '+this.name);
}
function F2E(id){
this.id = id;
this.showId = function(){
alert('Good morning,Sir,My work number is '+this.id);
}
}
F2E.prototype = new Person();
var simon = new F2E(9527);
simon.say();
simon.showId();
alert(simon.hasOwnProperty('id')); //检查是否为自身属性
接下来按照上面的例子来理解以下js原型链概念:
原型鏈可以理解成:js中每個物件都有一個隱藏的__proto__屬性,一個實例化物件的__proto__屬性指向其類別的prototype方法,而這個prototype方法又可以被賦值成另一個實例化對象,這個物件的__proto__又需要指向其類,由此形成一條鏈,也就是前面程式碼中的
F2E.prototype = new Person()
這句話是關鍵。 js物件在讀取某個屬性時,會先尋找自身屬性,沒有則再去依序查找原型鏈上物件的屬性。也就是說原型鏈的方法是可以共用的,這樣就解決了物件冒充浪費記憶體的缺點。
下面再來來說缺點:
缺點顯而易見,原型鏈方式繼承,就是實例化子類時不能將參數傳給父類,也就是為什麼這個例子中function Person()沒有參數,而是直接寫成了this.name=”Simon」的原因。下面的程式碼將無法達到預期的效果:
function Person (name){
this.name = name;
}
Person.prototype.say = function(){
alert('My name is ' this.name);<.>
function F2E(name,id){
this.id = id;
this.showId = function(){
this this.showId = function(){
this this.showId = function(){
this .id);
}
}
F2E.prototype = new Person();
var simon = new F2E("Simon",9527);
var simon = new F2E("Simon",9527);
simon.say( );
simon.showId();
function Person(name){
this.name = = name;
}
alert('My name is ' this.name);
}
function F2E(name,id){
this.showId = function(){
alert('Good morning,Sir,My work number is ' this.id);
}
}
); //此處無法進行傳值,this.name或name都不行,直接寫F2E.prototype = new Person('wood')是可以的,但是這樣的話simon.say()就變成了My name is wood
var simon = new F2E("Simon",9527);
simon.say(); //彈出My name is undefined
simon.showId();
最後,總結自認為較好的繼承實現方式,成員變數採用物件冒充方式,成員方法採用原型鏈方式,程式碼如下:
function Person(name){
this.name = name;
}
}
.prototype.say = function(){
alert('My name is ' this.name);
}
function F2E(name,id){
,name);
this.id = id;
}
F2E.prototype = new Person();
//此處注意一個細節,showId不能寫在F2E.prototype = new Person();前面
F2E.prototype.showId = function(){
alert('Good morning,Sir,My work number is ' this.id);
}
var simon = new F2E("Simon",9527);simon.say();simon.showId();