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)文章
-
Javascript客戶端腳本的設(shè)計(jì)和應(yīng)用
Javascript客戶端腳本的設(shè)計(jì)和應(yīng)用... 2006-08-08
-
JavaScript中各數(shù)制轉(zhuǎn)換全面總結(jié)
這篇文章主要介紹了JavaScript中各數(shù)制轉(zhuǎn)換,利用toString的基模式來進(jìn)行轉(zhuǎn)換,對(duì)數(shù)字調(diào)用 toString(10) 與調(diào)用 toString() 它們返回的區(qū)別和相同之處等等都在本文中提及,具體操作步驟大家可查看下文的詳細(xì)講解,感興趣的小伙伴們可以參考一下。 2017-08-08
-
React+TypeScript進(jìn)行項(xiàng)目構(gòu)建案例講解
這篇文章主要介紹了React+TypeScript進(jìn)行項(xiàng)目構(gòu)建案例講解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下 2021-07-07
-
JavaScript CSS 修改學(xué)習(xí)第四章 透明度設(shè)置
今天我打算使用一些元素的透明度設(shè)置來實(shí)現(xiàn)淡出淡入效果。但是有些瀏覽器對(duì)于某些元素的透明度設(shè)置支持不夠。而且要設(shè)置表格的透明度,幾乎在所有的瀏覽器里面都不可能。
2010-02-02
-
詳解JavaScript中的數(shù)據(jù)類型,以及檢測(cè)數(shù)據(jù)類型的方法
這篇文章主要介紹了JavaScript中的數(shù)據(jù)類型,以及檢測(cè)數(shù)據(jù)類型的方法,幫助大家鞏固基礎(chǔ)知識(shí),感興趣的朋友可以了解下 2020-09-09
-
JavaScript的三大前端框架Vue和Angular和React
這篇文章主要為大家介紹了JavaScript的三大前端框架Vue和Angular和React,文章中有詳細(xì)的代碼示例,有需要的朋友可以借鑒參考下, 2023-04-04
-
淺析js中2個(gè)等號(hào)與3個(gè)等號(hào)的區(qū)別
這篇文章介紹了js中2個(gè)等號(hào)與3個(gè)等號(hào)的區(qū)別,有需要的朋友可以參考一下 2013-08-08
最新評(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)文章
Javascript客戶端腳本的設(shè)計(jì)和應(yīng)用
Javascript客戶端腳本的設(shè)計(jì)和應(yīng)用...2006-08-08JavaScript中各數(shù)制轉(zhuǎn)換全面總結(jié)
這篇文章主要介紹了JavaScript中各數(shù)制轉(zhuǎn)換,利用toString的基模式來進(jìn)行轉(zhuǎn)換,對(duì)數(shù)字調(diào)用 toString(10) 與調(diào)用 toString() 它們返回的區(qū)別和相同之處等等都在本文中提及,具體操作步驟大家可查看下文的詳細(xì)講解,感興趣的小伙伴們可以參考一下。2017-08-08React+TypeScript進(jìn)行項(xiàng)目構(gòu)建案例講解
這篇文章主要介紹了React+TypeScript進(jìn)行項(xiàng)目構(gòu)建案例講解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07JavaScript CSS 修改學(xué)習(xí)第四章 透明度設(shè)置
今天我打算使用一些元素的透明度設(shè)置來實(shí)現(xiàn)淡出淡入效果。但是有些瀏覽器對(duì)于某些元素的透明度設(shè)置支持不夠。而且要設(shè)置表格的透明度,幾乎在所有的瀏覽器里面都不可能。2010-02-02詳解JavaScript中的數(shù)據(jù)類型,以及檢測(cè)數(shù)據(jù)類型的方法
這篇文章主要介紹了JavaScript中的數(shù)據(jù)類型,以及檢測(cè)數(shù)據(jù)類型的方法,幫助大家鞏固基礎(chǔ)知識(shí),感興趣的朋友可以了解下2020-09-09JavaScript的三大前端框架Vue和Angular和React
這篇文章主要為大家介紹了JavaScript的三大前端框架Vue和Angular和React,文章中有詳細(xì)的代碼示例,有需要的朋友可以借鑒參考下,2023-04-04淺析js中2個(gè)等號(hào)與3個(gè)等號(hào)的區(qū)別
這篇文章介紹了js中2個(gè)等號(hào)與3個(gè)等號(hào)的區(qū)別,有需要的朋友可以參考一下2013-08-08