JS的面向对象编程一:封装

时间:2021-09-22 17:21:44

Javascript是一种基于对象的语言,但它又没有class。这在很长的一段时间里,对JS的面向对象编程的概念很模糊,在编程的时候时有用到,但要说个所以然,却说不出来,所以看了些书,又在网上查了些资料,写下些总结。

一、封装

写个对象,将属性(变量),方法写在对象内,这就是封装。如下:我把两个属性封装在函数内。

 var Cat = {
    name :
'',
    color :
''
  }

用法:

var cat1 = {}; // 创建一个空对象
cat1.name = "小花"; // 按照原型对象的属性赋值
cat1.color = "黄色";

但这种写法用起来比构造函数模式的麻烦。比较如下:

function cat(name,color){
this.name=name,
this.color=color,
}

用法:

var cat1 = new cat('小花','黄色');

从代码的量上就可以看出,构造的更便捷。

这里解释下"构造函数":其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

而且用同一个构造函数生成的实例,他们之间是有联系的。他们会自动含有一个constructor属性,指向它们的构造函数。代码如下;

console.log(cat1.constructor==cat);  //true
console.log(cat2.constructor==cat); //true
console.log(cat1.constructor==cat2.constructor); //true

Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。

alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true

构造函数虽然好用,但是存在一个浪费内存的问题。如下

function cat(name,color){
this.name=name,
this.color=color
this.type="猫咪";
}

看上去好像没什么问题,但是,每实例化一个对象时,就会生成一个name、color、type,他们都是会占用内存的,而每次生成的type,都是相同的,这就多占用一些内存,缺乏效率。

可喜可贺的是,Javascript提供了一个prototype属性,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这也就说明,我们可以直接把type写在prototype对象上,如下:

function cat(name,color){
this.name=name,
this.color=color
}
cat.prototype.type
="猫咪";

验证下不同实例的type,是否是同一个。如下:

var cat1 = new cat('小花','黄色');
var cat2 = new cat('大花','橘色');
console.log(
"cat1.type="+cat1.type);
console.log(
"cat2.type="+cat2.type);
cat1.type
="老虎";
console.log(
"cat1.type="+cat1.type);
console.log(
"cat2.type="+cat2.type);

但输入的结果却是:

cat1.type=猫咪
cat2.type
=猫咪
cat1.type
=老虎
cat2.type
=猫咪

原因在于cat1这个实例没有type这个属性,这个属性是存在于cat1的原型中,而我们可以通过实例访问原型中的值,却不能通过实例改变原型中的值,如果我们在实例中添加一个属性,而这个属性又刚好于原型中的某个属性同名,那么该属性会屏蔽原型中的属性,最后出现上面那种效果。

下面是一些在网上找的Prototype模式的验证方法:

1、 isPrototypeOf()

这个方法用来判断,某个proptotype对象和某个实例之间的关系。

  alert(Cat.prototype.isPrototypeOf(cat1)); //true

  alert(Cat.prototype.isPrototypeOf(cat2)); //true

2、 hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

  alert(cat1.hasOwnProperty("name")); // true

  alert(cat1.hasOwnProperty("type")); // false

3、 in运算符

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

  alert("name" in cat1); // true

  alert("type" in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

  for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }