JavaScript物件導向-- 繼承(Inherit) - IV
上篇只講完第1個條件, 接著講第2個條件。
一開始, 會覺得當然直接將子層Class的原型指向父層Class就好了, 但會發生call by sharing:
e.g.
function A() {
this.abc = 12;
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.call(this);
}
B.prototype = A.prototype;
var objA = new A();
var objB = new B();
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objA.show(); // 12
objB.show(); // 12
objB.square(); // 144
objA.square(); // 144 (照理說, 應該要是不存在, 但因有call by sharing, 因而當B.prototype = A.prototype時, 在B所做的任何動作, 在A都看的到)
因而, 我們的解法: 透過prototype建立一個新實體, 因為瀏覽器總是先從自己本身物件上找方法&屬性, 找不到才會往原型裡面找。 我們透過這種方式擴充子層Class的原型方法, 且不會去影響父層Class了。以下舉例:
e.g.
function A(abc) {
this.abc = abc || 12;
console.log("A class"); //繼承的時後會執行兩次
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.apply(this, arguments);
}
B.prototype = new A();
var objA = new A();
var objB = new B(11);
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objB.square(); //121
objA.square(); //objA.square is not a function, 成功, 擴充的子層Class的原型方法不影響父層Class了。
但從上面例子可知道, 當我利用父層Class建立的新實體當作子層Class的原型時, 會導致父層Class多被執行一次, 所以解法就如上篇的第2個條件一樣, 以下舉例:
e.g.
var util = {
inherits: function(ctor, superCtor) { // ctor為子層Class, superCtor為父層Class
var F = function() {}; // 建立空的全新Class
F.prototype = superCtor.prototype; // 將空Class設定給父層Class原型
ctor.prototype = new F(); // 這樣new的是F, 不會重複執行父層Class, 且同時又能將父層Class的原型屬性&方法拿來使用。
ctor.uber = superCtor.prototype; // uber只是一個屬性, 可將父層Class屬性存入--> 當要使用的不是擴充的子層Class的原型屬性&方法時, 可以這麼用。
ctor.prototype.constructor = ctor; // 將constructor指向為自己(也就是子層Class), 這樣才不會迷路。
}
};
function A(abc) {
this.abc = abc || 12;
console.log("A class");
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.apply(this, arguments);
}
util.inherits(B, A); // B去繼承A的方法&屬性
var objA = new A();
var objB = new B();
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objB.square(); //144
objA.square(); //objA.square is not a function
最後, 可以改良上面例子, 因為在JavaScript中看到的任何東西皆來自物件, 所以可替Object的原型建立繼承方法, 使得繼承關係更方便建立:
e.g.
Object.prototype.inherits = function(superCtor) {
var F = function() {};
F.prototype = superCtor.prototype;
this.prototype = new F();
this.uber = superCtor.prototype;
this.prototype.constructor = this;
};
function A(abc) {
this.abc = abc || 12;
console.log("A class");
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.apply(this, arguments);
}
B.inherits(A);
var objA = new A();
var objB = new B();
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objB.square(); //144
objA.square(); //objA.square is not a function
終於將JavaScript繼承說完了!
p.s. JavaScript只有單一繼承, 其他語言有多重繼承。
一開始, 會覺得當然直接將子層Class的原型指向父層Class就好了, 但會發生call by sharing:
e.g.
function A() {
this.abc = 12;
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.call(this);
}
B.prototype = A.prototype;
var objA = new A();
var objB = new B();
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objA.show(); // 12
objB.show(); // 12
objB.square(); // 144
objA.square(); // 144 (照理說, 應該要是不存在, 但因有call by sharing, 因而當B.prototype = A.prototype時, 在B所做的任何動作, 在A都看的到)
因而, 我們的解法: 透過prototype建立一個新實體, 因為瀏覽器總是先從自己本身物件上找方法&屬性, 找不到才會往原型裡面找。 我們透過這種方式擴充子層Class的原型方法, 且不會去影響父層Class了。以下舉例:
e.g.
function A(abc) {
this.abc = abc || 12;
console.log("A class"); //繼承的時後會執行兩次
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.apply(this, arguments);
}
B.prototype = new A();
var objA = new A();
var objB = new B(11);
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objB.square(); //121
objA.square(); //objA.square is not a function, 成功, 擴充的子層Class的原型方法不影響父層Class了。
但從上面例子可知道, 當我利用父層Class建立的新實體當作子層Class的原型時, 會導致父層Class多被執行一次, 所以解法就如上篇的第2個條件一樣, 以下舉例:
e.g.
var util = {
inherits: function(ctor, superCtor) { // ctor為子層Class, superCtor為父層Class
var F = function() {}; // 建立空的全新Class
F.prototype = superCtor.prototype; // 將空Class設定給父層Class原型
ctor.prototype = new F(); // 這樣new的是F, 不會重複執行父層Class, 且同時又能將父層Class的原型屬性&方法拿來使用。
ctor.uber = superCtor.prototype; // uber只是一個屬性, 可將父層Class屬性存入--> 當要使用的不是擴充的子層Class的原型屬性&方法時, 可以這麼用。
ctor.prototype.constructor = ctor; // 將constructor指向為自己(也就是子層Class), 這樣才不會迷路。
}
};
function A(abc) {
this.abc = abc || 12;
console.log("A class");
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.apply(this, arguments);
}
util.inherits(B, A); // B去繼承A的方法&屬性
var objA = new A();
var objB = new B();
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objB.square(); //144
objA.square(); //objA.square is not a function
最後, 可以改良上面例子, 因為在JavaScript中看到的任何東西皆來自物件, 所以可替Object的原型建立繼承方法, 使得繼承關係更方便建立:
e.g.
Object.prototype.inherits = function(superCtor) {
var F = function() {};
F.prototype = superCtor.prototype;
this.prototype = new F();
this.uber = superCtor.prototype;
this.prototype.constructor = this;
};
function A(abc) {
this.abc = abc || 12;
console.log("A class");
}
A.prototype.show = function() {
console.log(this.abc);
};
function B() {
A.apply(this, arguments);
}
B.inherits(A);
var objA = new A();
var objB = new B();
B.prototype.square = function() {
console.log(this.abc * this.abc);
};
objB.square(); //144
objA.square(); //objA.square is not a function
終於將JavaScript繼承說完了!
p.s. JavaScript只有單一繼承, 其他語言有多重繼承。
留言
張貼留言