隐藏对象的属性和实现细节,仅对外公开接口,控制程序中属性读取和修改的访问级别。
这就是通常所说的封装。
我们知道,在 Java、C++ 这类严格的面向对象语言中,对象的封装相对比较容易实现。那么在 PHP 和 JavaScript 中,如何对一个对象进行封装,并隐藏对象内部的属性和实现细节呢?
下面通过同一个例子,分别看看 PHP 和 JavaScript 中的实现方式。
PHP中的封装
由于 PHP 提供了 class 关键字,可以比较方便地定义一个类:
<?php
class Workers {
public $age;
public $name;
public $sex;
private $wage=5000;
public function __construct($a, $b, $c){
$this->age =$a;
$this->name =$b;
$this->sex =$c;
}
public function saypay(){
echo $this->wage;
}
}
?>
这里定义了一个“上班族”类。
这个类有四个属性:
- 年龄
- 姓名
- 性别
- 工资
一般来说,前三个属性可以公开访问,而工资可能属于比较私密的信息,不希望任何外部代码都可以直接访问或修改。
因此,我们使用 "private $wage = 5000;" 将工资定义为私有属性。
当实例化一个“小明”对象后:
<?php
$xiaoming = new Workers(25, "小明", "男");
echo $xiaoming->age;
?>
可以直接访问年龄。
但是如果直接访问工资:
<?php
echo $xiaoming->wage;
?>
就会提示无法访问,因为 wage 是一个私有属性。
这样就保护了对象内部的数据。
但是,这里又会出现一个问题。
私有属性并不意味着任何情况下都不能访问。
例如,小明每个月总需要知道自己的工资是多少。这时候,就可以通过类提供的一个公共方法来访问内部的私有属性:
<?php
echo $xiaoming->saypay();
?>
这里的 saypay() 就相当于一个对外公开的接口。
外部代码不能直接访问 "$xiaoming->wage" 但是可以通过 "$xiaoming->saypay()", 获取工资信息。
这样,真正的数据仍然保存在对象内部,而外部只能通过类提供的方法进行访问。
JavaScript中的封装
那么,在 JavaScript 中如何实现类似的效果呢?
JavaScript 当时并没有 PHP 这样直接通过 private 关键字定义私有属性的方式,因此实现思路有所不同。
还是使用刚才的“上班族”例子:
function Workers(a, b, c){
this.age =a;
this.name =b;
this.sex =c;
var wage=5000;
this.saypay =function(){
return wage;
};
}
var xiaoming=newWorkers(25, "小明", "男");
alert(xiaoming.age);
alert(xiaoming.name);
alert(xiaoming.sex);
alert(xiaoming.wage);
alert(xiaoming.saypay());这里:this.age, this.name, this.sex 都是对象的公开属性,因此可以直接访问, alert(xiaoming.age);
但是工资 var wage=5000 只是 Workers 函数内部的一个局部变量。
因此,外部无法直接访问:alert(xiaoming.wage); 结果会是 undefined
但是,saypay() 函数可以访问这个内部变量:
this.saypay =function(){
return wage;
};所以:
alert(xiaoming.saypay());
可以正常获取工资。
这里利用的其实就是 JavaScript 的作用域和闭包特性。
两种方式有什么不同?
两个例子实现出来的效果比较相似:
对象内部有一些属性不希望被外部直接访问,而是通过指定的方法进行读取和操作。
但是它们的实现原理并不一样。
PHP
PHP 通过访问控制关键字:
直接控制对象属性和方法的访问权限。
例如:
外部代码无法直接访问。
JavaScript
JavaScript 这个例子中,并没有真正把 wage 挂载到对象上:
它只是构造函数内部的局部变量。
外部无法直接访问这个变量,但是通过内部定义的方法,仍然可以访问它:
this.saypay =function(){
returnwage;
};因此,两个例子虽然最终实现了类似的效果,但底层思路并不相同。
私有属性也可以通过接口修改
对象内部受保护的属性,有时候不仅需要读取,也需要进行修改。
例如,小明刚开始的工资是:5000
随着工作经验和技术水平的提高,工资可能会发生变化。
这时候,同样可以定义一个公开的方法来修改工资:
functionWorkers(a, b, c){
this.age =a;
this.name =b;
this.sex =c;
var wage=5000;
this.saypay =function(){
returnwage;
};
this.setpay =function(newwage){
wage=newwage;
};
}然后:
varxiaoming=newWorkers(25, "小明", "男");
alert(xiaoming.saypay());
// 5000
xiaoming.setpay(8000);
alert(xiaoming.saypay());
// 8000
//外部代码仍然不能直接修改:
xiaoming.wage =8000;
//而是需要通过:
xiaoming.setpay(8000);
这个方法进行修改。
这样,对象内部的数据访问和修改都可以集中控制。
这也是封装的一个重要意义:
不一定要完全禁止外部访问,而是控制外部应该通过什么方式访问。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。