Javascript OOP之面向?qū)ο?/h1>
更新時(shí)間:2016年07月31日 18:04:42 投稿:hebedich
Javascript是一種基于對(duì)象(object-based)的語言,你遇到的所有東西幾乎都是對(duì)象。但是,它又不是一種真正的面向?qū)ο缶幊蹋∣OP)語言,因?yàn)樗恼Z法中沒有class(類)。
面向?qū)ο蟪绦蛟O(shè)計(jì)(Object-oriented programming,OOP)是一種程序設(shè)計(jì)范型,同時(shí)也是一種程序開發(fā)的方法。對(duì)象指的是類的實(shí)例。它將對(duì)象作為程序的基本單元,將程序和數(shù)據(jù)封裝其中,以提高軟件的重用性、靈活性和擴(kuò)展性?!S基百科
一般面向?qū)ο蟀豪^承,封裝,多態(tài),抽象
對(duì)象形式的繼承
淺拷貝
var Person = {
name: 'allin',
age: 18,
address: {
home: 'home',
office: 'office',
}
sclools: ['x','z'],
};
var programer = {
language: 'js',
};
function extend(p, c){
var c = c || {};
for( var prop in p){
c[prop] = p[prop];
}
}
extend(Person, programer);
programer.name; // allin
programer.address.home; // home
programer.address.home = 'house'; //house
Person.address.home; // house
從上面的結(jié)果看出,淺拷貝的缺陷在于修改了子對(duì)象中引用類型的值,會(huì)影響到父對(duì)象中的值,因?yàn)樵跍\拷貝中對(duì)引用類型的拷貝只是拷貝了地址,指向了內(nèi)存中同一個(gè)副本。
深拷貝
function extendDeeply(p, c){
var c = c || {};
for (var prop in p){
if(typeof p[prop] === "object"){
c[prop] = (p[prop].constructor === Array)?[]:{};
extendDeeply(p[prop], c[prop]);
}else{
c[prop] = p[prop];
}
}
}
利用遞歸進(jìn)行深拷貝,這樣子對(duì)象的修改就不會(huì)影響到父對(duì)象。
extendDeeply(Person, programer);
programer.address.home = 'allin';
Person.address.home; // home
利用call和apply繼承
function Parent(){
this.name = "abc";
this.address = {home: "home"};
}
function Child(){
Parent.call(this);
this.language = "js";
}
ES5中的Object.create()
var p = { name : 'allin'};
var obj = Object.create(o);
obj.name; // allin
Object.create()作為new操作符的替代方案是ES5之后才出來的。我們也可以自己模擬該方法:
//模擬Object.create()方法
function myCreate(o){
function F(){};
F.prototype = o;
o = new F();
return o;
}
var p = { name : 'allin'};
var obj = myCreate(o);
obj.name; // allin
目前,各大瀏覽器的最新版本(包括IE9)都部署了這個(gè)方法。如果遇到老式瀏覽器,可以用下面的代碼自行部署。
if (!Object.create) {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
類的繼承
Object.create()
function Person(name, age){}
Person.prototype.headCount = 1;
Person.prototype.eat = function(){
console.log('eating...');
}
function Programmer(name, age, title){}
Programmer.prototype = Object.create(Person.prototype); //建立繼承關(guān)系
Programmer.prototype.constructor = Programmer; // 修改constructor的指向
調(diào)用父類方法
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.headCount = 1;
Person.prototype.eat = function(){
console.log('eating...');
}
function Programmer(name, age, title){
Person.apply(this, arguments); // 調(diào)用父類的構(gòu)造器
}
Programmer.prototype = Object.create(Person.prototype);
Programmer.prototype.constructor = Programmer;
Programmer.prototype.language = "js";
Programmer.prototype.work = function(){
console.log('i am working code in '+ this.language);
Person.prototype.eat.apply(this, arguments); // 調(diào)用父類上的方法
}
封裝
命名空間
js是沒有命名空間的,因此可以用對(duì)象模擬。
var app = {}; // 命名空間app
//模塊1
app.module1 = {
name: 'allin',
f: function(){
console.log('hi robot');
}
};
app.module1.name; // "allin"
app.module1.f(); // hi robot
靜態(tài)成員
function Person(name){
var age = 100;
this.name = name;
}
//靜態(tài)成員
Person.walk = function(){
console.log('static');
};
Person.walk(); // static
私有與公有
function Person(id){
// 私有屬性與方法
var name = 'allin';
var work = function(){
console.log(this.id);
};
//公有屬性與方法
this.id = id;
this.say = function(){
console.log('say hello');
work.call(this);
};
};
var p1 = new Person(123);
p1.name; // undefined
p1.id; // 123
p1.say(); // say hello 123
模塊化
var moduleA;
moduleA = function() {
var prop = 1;
function func() {}
return {
func: func,
prop: prop
};
}(); // 立即執(zhí)行匿名函數(shù)
prop,func 不會(huì)被泄露到全局作用域?;蛘吡硪环N寫法,使用 new
moduleA = new function() {
var prop = 1;
function func() {}
this.func = func;
this.prop = prop;
}
多態(tài)
模擬方法重載
arguments屬性可以取得函數(shù)調(diào)用的實(shí)參個(gè)數(shù),可以利用這一點(diǎn)模擬方法的重載。
function demo(a, b ){
console.log(demo.length); // 得到形參個(gè)數(shù)
console.log(arguments.length); //得到實(shí)參個(gè)數(shù)
console.log(arguments[0]); // 第一個(gè)實(shí)參 4
console.log(arguments[1]); // 第二個(gè)實(shí)參 5
}
demo(4, 5, 6);
//實(shí)現(xiàn)可變長(zhǎng)度實(shí)參的相加
function add(){
var total = 0;
for( var i = arguments.length - 1; i >= 0; i--){
total += arguments[i];
}
return total;
}
console.log(add(1)); // 1
console.log(add(1, 2, 3)); // 7
// 參數(shù)不同的情況
function fontSize(){
var ele = document.getElementById('js');
if(arguments.length == 0){
return ele.style.fontSize;
}else{
ele.style.fontSize = arguments[0];
}
}
fontSize(18);
console.log(fontSize());
// 類型不同的情況
function setting(){
var ele = document.getElementById('js');
if(typeof arguments[0] === "object"){
for(var p in arguments[0]){
ele.style[p] = arguments[0][p];
}
}else{
ele.style.fontSize = arguments[0];
ele.style.backgroundColor = arguments[1];
}
}
setting(18, 'red');
setting({fontSize:20, backgroundColor: 'green'});
方法重寫
function F(){}
var f = new F();
F.prototype.run = function(){
console.log('F');
}
f.run(); // F
f.run = function(){
console.log('fff');
}
f.run(); // fff
抽象類
在構(gòu)造器中 throw new Error(''); 拋異常。這樣防止這個(gè)類被直接調(diào)用。
function DetectorBase() {
throw new Error('Abstract class can not be invoked directly!');
}
DetectorBase.prototype.detect = function() {
console.log('Detection starting...');
};
DetectorBase.prototype.stop = function() {
console.log('Detection stopped.');
};
DetectorBase.prototype.init = function() {
throw new Error('Error');
};
// var d = new DetectorBase();// Uncaught Error: Abstract class can not be invoked directly!
function LinkDetector() {}
LinkDetector.prototype = Object.create(DetectorBase.prototype);
LinkDetector.prototype.constructor = LinkDetector;
var l = new LinkDetector();
console.log(l); //LinkDetector {}__proto__: LinkDetector
l.detect(); //Detection starting...
l.init(); //Uncaught Error: Error
相關(guān)文章
-
ajax不執(zhí)行success回調(diào)而是執(zhí)行了error回調(diào)
最近在看jQuery的API文檔,在使用到j(luò)Query的ajax時(shí),如果指定了dataType為json,老是不執(zhí)行success回調(diào),而是執(zhí)行了error回調(diào)函數(shù),極度郁悶 2012-12-12
-
JavaScript While 循環(huán)基礎(chǔ)教程
只要指定條件為 true,循環(huán)就可以一直執(zhí)行代碼, 2007-04-04
-
ECMAScript6的新特性箭頭函數(shù)(Arrow Function)詳細(xì)介紹
這篇文章主要介紹了ECMAScript6的新特性箭頭函數(shù)(Arrow Function)詳細(xì)介紹,ECMAScript6其實(shí)就是JavaScript,它的新特性就是JS的新特性,引入只是時(shí)間問題,需要的朋友可以參考下 2014-06-06
-
關(guān)于JavaScript 原型鏈的一點(diǎn)個(gè)人理解
本文給大家分享的是個(gè)人關(guān)于JavaScript原型鏈相關(guān)知識(shí)的一些理解,這里推薦給大家,希望大家能夠喜歡 2016-07-07
-
詳解JavaScript設(shè)計(jì)模式開發(fā)中的橋接模式使用
橋接模式的適用場(chǎng)合非常廣泛,除了在事件回調(diào)函數(shù)與接口之間進(jìn)行橋接外,橋接模式也可以用于連接公開的API代碼和私用的實(shí)現(xiàn)代碼,下面我們就來詳解JavaScript設(shè)計(jì)模式開發(fā)中的橋接模式使用 2016-05-05
-
javascript之函數(shù)直接量(function(){})()
javascript之函數(shù)直接量(function(){})()... 2007-06-06
-
淺談Web頁面向后臺(tái)提交數(shù)據(jù)的方式和選擇
下面小編就為大家?guī)硪黄獪\談Web頁面向后臺(tái)提交數(shù)據(jù)的方式和選擇。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧 2016-09-09
最新評(píng)論
面向?qū)ο蟪绦蛟O(shè)計(jì)(Object-oriented programming,OOP)是一種程序設(shè)計(jì)范型,同時(shí)也是一種程序開發(fā)的方法。對(duì)象指的是類的實(shí)例。它將對(duì)象作為程序的基本單元,將程序和數(shù)據(jù)封裝其中,以提高軟件的重用性、靈活性和擴(kuò)展性?!S基百科
一般面向?qū)ο蟀豪^承,封裝,多態(tài),抽象
對(duì)象形式的繼承
淺拷貝
var Person = {
name: 'allin',
age: 18,
address: {
home: 'home',
office: 'office',
}
sclools: ['x','z'],
};
var programer = {
language: 'js',
};
function extend(p, c){
var c = c || {};
for( var prop in p){
c[prop] = p[prop];
}
}
extend(Person, programer);
programer.name; // allin
programer.address.home; // home
programer.address.home = 'house'; //house
Person.address.home; // house
從上面的結(jié)果看出,淺拷貝的缺陷在于修改了子對(duì)象中引用類型的值,會(huì)影響到父對(duì)象中的值,因?yàn)樵跍\拷貝中對(duì)引用類型的拷貝只是拷貝了地址,指向了內(nèi)存中同一個(gè)副本。
深拷貝
function extendDeeply(p, c){
var c = c || {};
for (var prop in p){
if(typeof p[prop] === "object"){
c[prop] = (p[prop].constructor === Array)?[]:{};
extendDeeply(p[prop], c[prop]);
}else{
c[prop] = p[prop];
}
}
}
利用遞歸進(jìn)行深拷貝,這樣子對(duì)象的修改就不會(huì)影響到父對(duì)象。
extendDeeply(Person, programer);
programer.address.home = 'allin';
Person.address.home; // home
利用call和apply繼承
function Parent(){
this.name = "abc";
this.address = {home: "home"};
}
function Child(){
Parent.call(this);
this.language = "js";
}
ES5中的Object.create()
var p = { name : 'allin'};
var obj = Object.create(o);
obj.name; // allin
Object.create()作為new操作符的替代方案是ES5之后才出來的。我們也可以自己模擬該方法:
//模擬Object.create()方法
function myCreate(o){
function F(){};
F.prototype = o;
o = new F();
return o;
}
var p = { name : 'allin'};
var obj = myCreate(o);
obj.name; // allin
目前,各大瀏覽器的最新版本(包括IE9)都部署了這個(gè)方法。如果遇到老式瀏覽器,可以用下面的代碼自行部署。
if (!Object.create) {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
類的繼承
Object.create()
function Person(name, age){}
Person.prototype.headCount = 1;
Person.prototype.eat = function(){
console.log('eating...');
}
function Programmer(name, age, title){}
Programmer.prototype = Object.create(Person.prototype); //建立繼承關(guān)系
Programmer.prototype.constructor = Programmer; // 修改constructor的指向
調(diào)用父類方法
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.headCount = 1;
Person.prototype.eat = function(){
console.log('eating...');
}
function Programmer(name, age, title){
Person.apply(this, arguments); // 調(diào)用父類的構(gòu)造器
}
Programmer.prototype = Object.create(Person.prototype);
Programmer.prototype.constructor = Programmer;
Programmer.prototype.language = "js";
Programmer.prototype.work = function(){
console.log('i am working code in '+ this.language);
Person.prototype.eat.apply(this, arguments); // 調(diào)用父類上的方法
}
封裝
命名空間
js是沒有命名空間的,因此可以用對(duì)象模擬。
var app = {}; // 命名空間app
//模塊1
app.module1 = {
name: 'allin',
f: function(){
console.log('hi robot');
}
};
app.module1.name; // "allin"
app.module1.f(); // hi robot
靜態(tài)成員
function Person(name){
var age = 100;
this.name = name;
}
//靜態(tài)成員
Person.walk = function(){
console.log('static');
};
Person.walk(); // static
私有與公有
function Person(id){
// 私有屬性與方法
var name = 'allin';
var work = function(){
console.log(this.id);
};
//公有屬性與方法
this.id = id;
this.say = function(){
console.log('say hello');
work.call(this);
};
};
var p1 = new Person(123);
p1.name; // undefined
p1.id; // 123
p1.say(); // say hello 123
模塊化
var moduleA;
moduleA = function() {
var prop = 1;
function func() {}
return {
func: func,
prop: prop
};
}(); // 立即執(zhí)行匿名函數(shù)
prop,func 不會(huì)被泄露到全局作用域?;蛘吡硪环N寫法,使用 new
moduleA = new function() {
var prop = 1;
function func() {}
this.func = func;
this.prop = prop;
}
多態(tài)
模擬方法重載
arguments屬性可以取得函數(shù)調(diào)用的實(shí)參個(gè)數(shù),可以利用這一點(diǎn)模擬方法的重載。
function demo(a, b ){
console.log(demo.length); // 得到形參個(gè)數(shù)
console.log(arguments.length); //得到實(shí)參個(gè)數(shù)
console.log(arguments[0]); // 第一個(gè)實(shí)參 4
console.log(arguments[1]); // 第二個(gè)實(shí)參 5
}
demo(4, 5, 6);
//實(shí)現(xiàn)可變長(zhǎng)度實(shí)參的相加
function add(){
var total = 0;
for( var i = arguments.length - 1; i >= 0; i--){
total += arguments[i];
}
return total;
}
console.log(add(1)); // 1
console.log(add(1, 2, 3)); // 7
// 參數(shù)不同的情況
function fontSize(){
var ele = document.getElementById('js');
if(arguments.length == 0){
return ele.style.fontSize;
}else{
ele.style.fontSize = arguments[0];
}
}
fontSize(18);
console.log(fontSize());
// 類型不同的情況
function setting(){
var ele = document.getElementById('js');
if(typeof arguments[0] === "object"){
for(var p in arguments[0]){
ele.style[p] = arguments[0][p];
}
}else{
ele.style.fontSize = arguments[0];
ele.style.backgroundColor = arguments[1];
}
}
setting(18, 'red');
setting({fontSize:20, backgroundColor: 'green'});
方法重寫
function F(){}
var f = new F();
F.prototype.run = function(){
console.log('F');
}
f.run(); // F
f.run = function(){
console.log('fff');
}
f.run(); // fff
抽象類
在構(gòu)造器中 throw new Error(''); 拋異常。這樣防止這個(gè)類被直接調(diào)用。
function DetectorBase() {
throw new Error('Abstract class can not be invoked directly!');
}
DetectorBase.prototype.detect = function() {
console.log('Detection starting...');
};
DetectorBase.prototype.stop = function() {
console.log('Detection stopped.');
};
DetectorBase.prototype.init = function() {
throw new Error('Error');
};
// var d = new DetectorBase();// Uncaught Error: Abstract class can not be invoked directly!
function LinkDetector() {}
LinkDetector.prototype = Object.create(DetectorBase.prototype);
LinkDetector.prototype.constructor = LinkDetector;
var l = new LinkDetector();
console.log(l); //LinkDetector {}__proto__: LinkDetector
l.detect(); //Detection starting...
l.init(); //Uncaught Error: Error
相關(guān)文章
ajax不執(zhí)行success回調(diào)而是執(zhí)行了error回調(diào)
最近在看jQuery的API文檔,在使用到j(luò)Query的ajax時(shí),如果指定了dataType為json,老是不執(zhí)行success回調(diào),而是執(zhí)行了error回調(diào)函數(shù),極度郁悶2012-12-12
JavaScript While 循環(huán)基礎(chǔ)教程
只要指定條件為 true,循環(huán)就可以一直執(zhí)行代碼,2007-04-04
ECMAScript6的新特性箭頭函數(shù)(Arrow Function)詳細(xì)介紹
這篇文章主要介紹了ECMAScript6的新特性箭頭函數(shù)(Arrow Function)詳細(xì)介紹,ECMAScript6其實(shí)就是JavaScript,它的新特性就是JS的新特性,引入只是時(shí)間問題,需要的朋友可以參考下2014-06-06
關(guān)于JavaScript 原型鏈的一點(diǎn)個(gè)人理解
本文給大家分享的是個(gè)人關(guān)于JavaScript原型鏈相關(guān)知識(shí)的一些理解,這里推薦給大家,希望大家能夠喜歡2016-07-07
詳解JavaScript設(shè)計(jì)模式開發(fā)中的橋接模式使用
橋接模式的適用場(chǎng)合非常廣泛,除了在事件回調(diào)函數(shù)與接口之間進(jìn)行橋接外,橋接模式也可以用于連接公開的API代碼和私用的實(shí)現(xiàn)代碼,下面我們就來詳解JavaScript設(shè)計(jì)模式開發(fā)中的橋接模式使用2016-05-05
javascript之函數(shù)直接量(function(){})()
javascript之函數(shù)直接量(function(){})()...2007-06-06
淺談Web頁面向后臺(tái)提交數(shù)據(jù)的方式和選擇
下面小編就為大家?guī)硪黄獪\談Web頁面向后臺(tái)提交數(shù)據(jù)的方式和選擇。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09

