js是一門基於對象的語言,但它沒有完整的class概念,至少在ES5中是沒有的
封裝--原始模式:
Cat = {
name : '',
color : ''
};
let cat1 = {};
cat1.name = '大花';
cat1.color = 'white';
let cat2 = {};
cat2.name = '二哈';
cat2.color = 'yellow';
此時js中沒有類的概念,可以看出:兩個實例的貓 cat1 和 cat2 沒有任何聯繫
因此前人對原始模式進行改進
封裝--構造函數模式
function Cat(name,color) {
this.name=name;
this.color=color;
}
let cat1 = new Cat('大花', 'white'); // Cat {name: '大花', color:'white'}
let cat2 = new Cat('二哈', 'yellow');
現在看起來,是不是好多了?兩隻貓用一個構造函數構造出來
但是還存在一個問題,如果兩個貓存在共同的屬性type,以及方法eat,那會造成資源的浪費
封裝--工廠模式
function Cat(name,color) {
this.name=name;
this.color=color;
}
Cat.prototype.type = '貓科動物';
Cat.prototype.eat = function () {
console.log('喫魚');
};
let cat1 = new Cat('大花', 'white');
let cat2 = new Cat('二哈', 'yellow');
console.log(cat1);
console.log(cat1.type);
這樣兩個實例都有type屬性和eat方法,指向同一個地址,做到了節省資源的目的
console.log(cat1.eat == cat2.eat); // true
爲了配合protype使用,在Object的原型上定義了isPrototypeOf
console.log(Cat.prototype.isPrototypeOf(cat1)); // true
console.log(cat1.hasOwnProperty('name')); // true 判斷是否是自身屬性,繼承屬性不算
console.log('name' in cat1); // 判斷屬性在不在實例上,繼承也算
繼承--構造函數綁定
使用call或apply方法,將父對象的構造函數綁定在子對象上,即在子對象構造函數中加一行:
function Animal() {
this.species = '動物';
}
function Cat(name,color) {
Animal.apply(this, arguments);
this.name=name;
this.color=color;
}
let cat1 = new Cat('大花', 'white');
console.log(cat1.species);
繼承--prototype模式
”貓“的 prototype 指向 Animal 的實例,這樣貓的實例就能繼承 Animal 了
function Animal() {
this.species = '動物';
}
// Animal.prototype.species = '動物‘;
function Cat(name,color) {
this.name=name;
this.color=color;
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat; // 手動糾正cat1.constructor指向Animal
let cat1 = new Cat('大花', 'white');
console.log(cat1.species);
淺拷貝
把父對象的屬性,全部拷貝給子對象,也能實現繼承
var Chinese = { nation:'中國' };
var Doctor ={ career:'醫生' }
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
var Doctor = extendCopy(Chinese);
Doctor.career = '醫生';
console.log(Doctor.nation); // 中國
淺拷貝存在一個問題,當Chinese中存在一個數組屬性nation: ['漢族',’回族‘,’苗族‘],改變子對象屬性時,父對象屬性有被篡改的風險