盒子
盒子
文章目录󰁋
  1. 对象形式的继承
    1. 浅拷贝
    2. 深拷贝
    3. 利用call和apply继承
    4. ES5中的Object.create()
  2. 类的继承
    1. Object.create()
    2. 调用父类方法
  3. 封装
    1. 命名空间
    2. 静态成员
    3. 私有与公有
    4. 模块化
  4. 多态
    1. 模拟方法重载
    2. 方法重写
  5. 抽象类

OOP之面向对象

一般面向对象包含:继承,封装,多态,抽象

对象形式的继承


浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
  • 从上面的结果看出,浅拷贝的缺陷在于修改了子对象中引用类型的值,会影响到父对象中的值,因为在浅拷贝中对引用类型的拷贝只是拷贝了地址,指向了内存中同一个副本
深拷贝

1
2
3
4
5
6
7
8
9
10
11
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];
}
}
}
  • 利用递归进行深拷贝,这样子对象的修改就不会影响到父对象
1
2
3
extendDeeply(Person, programer);
programer.address.home = 'allin';
Person.address.home; // home
利用call和apply继承

1
2
3
4
5
6
7
8
function Parent(){
this.name = "abc";
this.address = {home: "home"};
}
function Child(){
Parent.call(this);
this.language = "js";
}
ES5中的Object.create()
1
2
3
var p = { name : 'allin'};
var obj = Object.create(o);
obj.name; // allin
  • Object.create()作为new操作符的替代方案是ES5之后才出来的。我们也可以自己模拟该方法:
1
2
3
4
5
6
7
8
9
10
//模拟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)都部署了这个方法。如果遇到老式浏览器,可以用下面的代码自行部署
1
2
3
4
5
6
7
 if (!Object.create) {
    Object.create = function (o) {
       function F() {}
      F.prototype = o;
      return new F();
    };
  }

类的继承


Object.create()

1
2
3
4
5
6
7
8
9
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); //建立继承关系
Programmer.prototype.constructor = Programmer; // 修改constructor的指向
调用父类方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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); // 调用父类的构造器
}


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); // 调用父类上的方法
}

封装


命名空间

  • js是没有命名空间的,因此可以用对象模拟
1
2
3
4
5
6
7
8
9
10
var app = {};  // 命名空间app
//模块1
app.module1 = {
name: 'allin',
f: function(){
console.log('hi robot');
}
};
app.module1.name; // "allin"
app.module1.f(); // hi robot
静态成员

1
2
3
4
5
6
7
8
9
function Person(name){
var age = 100;
this.name = name;
}
//静态成员
Person.walk = function(){
console.log('static');
};
Person.walk(); // static
私有与公有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
模块化

1
2
3
4
5
6
7
8
9
10
11
var moduleA;
moduleA = function() {
var prop = 1;

function func() {}

return {
func: func,
prop: prop
};
}(); // 立即执行匿名函数

多态


模拟方法重载

  • arguments属性可以取得函数调用的实参个数,可以利用这一点模拟方法的重载
1
2
3
4
5
6
7
8
function demo(a, b ){
console.log(demo.length); // 得到形参个数
console.log(arguments.length); //得到实参个数
console.log(arguments[0]); // 第一个实参 4
console.log(arguments[1]); // 第二个实参 5
}

demo(4, 5, 6);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//实现可变长度实参的相加
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


// 参数不同的情况
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'});
方法重写

1
2
3
4
5
6
7
8
9
10
11
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

抽象类


  • 在构造器中 throw new Error(''); 抛异常。这样防止这个类被直接调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
支持一下
扫一扫,支持poetries