JavaScript面向對象(收集整理)
(1)封裝
首先理解構造函數:所謂"構造函數",其實就是一個普通函數,但是內部使用了this
變量。對構造函數使用new
運算符,就能生成實例,並且this
變量會綁定在實例對象上。
function Cat(name,color){ this.name=name; this.color=color; } var cat1 = new Cat("大毛","黃色"); var cat2 = new Cat("二毛","黑色"); alert(cat1.name); // 大毛 alert(cat1.color); // 黃色
其中cat1
和cat2
會自動含有一個constructor
alert(cat1.constructor == Cat); //true alert(cat2.constructor == Cat); //true
構造函數存在一個弊端:浪費內存,比如:
function Cat(name,color){ this.name = name; this.color = color; this.type = "貓科動物"; this.eat = function(){alert("吃老鼠");}; } var cat1 = new Cat("大毛","黃色"); var cat2 = newCat ("二毛","黑色");
alert(cat1.eat == cat2.eat); //false
其中前兩個屬性name,color是實例獨自擁有的,cat1和cat2的name,color是不同的,而type,eat()確是可以共同擁有的,所有可以把共有的方法,屬性定義在prototype
對象上,如下:
function Cat(name,color){ this.name = name; this.color = color; } Cat.prototype.type = "貓科動物"; Cat.prototype.eat = function(){alert("吃老鼠")}; var cat1 = new Cat("大毛","黃色"); var cat2 = new Cat("二毛","黑色"); alert(cat1.eat == cat2.eat); //true
這時所有實例的type
屬性和eat()
方法,其實都是同一個內存地址,指向prototype
對象,因此就提高了運行效率。
(2)繼承
function Animal(){ this.species = "動物"; } function Cat(name,color){ this.name = name; this.color = color; }
(Cat要繼承Animal)
1.構造函數綁定
使用call或apply方法,將父對象的構造函數綁定在子對象上,即在子對象構造函數中加一行
function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; } var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物
2. prototype模式
將Cat(子)的prototype指向Animal(父)的實例,那麽Cat所創建出來的實例就都繼承了Animal
Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物
其中第二行代碼 Cat.prototype.constructor = Cat; 是因為如果原本constructor本來是指向自己的構造函數cat()的,但是這裏被賦值給了new Animal(),換了個新構造函數,所以為了不造成繼承鏈的紊亂,必須手動修改回來
3. 直接繼承prototype
Cat不變的屬性或方法可以直接寫入Animal.prototype,然後Cat.prototype = Animal.prototype直接繼承
function Animal(){ } Animal.prototype.species = "動物"; Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物
這樣的好處是,對比方法2中,不用執行和建立Animal()的實例,也省了內存;
這樣的缺點是,由於Cat.prototype = Animal.prototype,Cat.prototype和Animal.prototype現在指向了同一個對象,實際上把Animal.prototype對象的constructor屬性也改掉了!
4.利用空對象作為中介
用一個F空對象,幾乎不占內存,作為中間橋梁,修改Cat的prototype對象,就不會影響到Animal的prototype對象。
var F = function(){}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat;
可以封裝成一個函數:
function extend(Child,Parent){ var F = function (){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.construction = Child; Child.uber = Parent.prototype; //假設一個Child不變的屬性uber } extend(Cat,Animal); var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物
5.拷貝繼承
把父對象的所有屬性和方法,拷貝進子對象
function Animal(){} Animal.prototype.species = "動物"; function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; } extend2(Cat, Animal); var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物
(3)非構造函數繼承
1.object()方法
var Chinese = { nation:‘中國‘ }; var Doctor ={ career:‘醫生‘ }
如何使Doctor對象繼承Chinese對象?
function object(o) { function F() {} F.prototype = o; return new F(); }
var Doctor = object(Chinese); Doctor.career = ‘醫生‘; alert(Doctor.nation); //中國
2.淺拷貝:
只是拷貝基本類型的數據
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 = ‘醫生‘; alert(Doctor.nation); // 中國
Chinese.birthPlaces = [‘北京‘,‘上海‘,‘香港‘];
var Doctor = extendCopy(Chinese);
Doctor.birthPlaces.push(‘廈門‘);
alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門
alert(Chinese.birthPlaces); //北京, 上海, 香港, 廈門
下面的代碼顯露出錢淺拷貝的缺點,就是假如父對象的某個屬性也是數組或另一個對象,那麽實際上,子對象獲得的只是一個內存地址,而不是真正拷貝,因此存在父對象被篡改的可能。
3.深拷貝
jQuery庫使用的就是這種繼承方法。
function deepCopy(p, c) { var c = c || {}; for (var i in p) { if (typeof p[i] === ‘object‘) { c[i] = (p[i].constructor === Array) ? [] : {}; deepCopy(p[i], c[i]); } else { c[i] = p[i]; } } return c; } var Doctor = deepCopy(Chinese); Chinese.birthPlaces = [‘北京‘,‘上海‘,‘香港‘]; Doctor.birthPlaces.push(‘廈門‘); alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門 alert(Chinese.birthPlaces); //北京, 上海, 香港
以上內容均參考自:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html
JavaScript面向對象(收集整理)