js之面向对象——封装

前言

回顾多年前第一次接触js,其实是有点反感的。相比于C#、java等强类型语言,js语法无法在IDE中通过键入“.”的方式,获取对象的智能提示,对于一个初学者这是一个非常不好的体验。即使多年以后,IDE有了不小的进步,但也只能将js内置对象的方法和属性以智能提示的方式体现,而自己定义的方法、属性,或者引入的第三方js对象的方法、属性依然是无法被IDE感知。

究其原因,无非就是因为js的对象没有class这一概念,或者说没有“元数据”。js只知道它是一个对象,但是无法知道这个对象的“本质”。

为什么js没有像java那样设计?具体原因不得而知,估计还是与js的历史背景有关,早期js只是被设计为浏览器脚本语言,因此设计者并没有引入类的概念,因为这会使语言本身过于复杂。另外,没有类,并不意味着无法实现面向对象编程,相反js通过更轻巧(或者说是“化繁为简”)的设计,达到了面向对象编程语言的效果——封装、继承、多态 。

创建对象

class作用是定义对象的属性和方法,java中必须先有类,才能声明一个该类的对象。而js显然觉得这是个累赘,如果我想创建一个猫,直接这样就可以了:

var cat = {
	name:"tom",
	color:"gray"
}

//或者,先创建创建空对象,再设置属性
var cat = {};
cat.name = "tom";
cat.color = "gray";

//也可以这样写
cat["name"] = "tom";
cat["color"] = "gray";

很简单,也很自由——在创建对象的同时,把需要的属性直接声明并赋值。

如果我们想要为cat对象添加一个方法,可以

var cat = {
	name:"tom",
	color:"gray",
	say:function(){
		alert("i'm "+this.name+",miao~");
	}
}

//或者
cat.say = function(){
	alert("i'm "+this.name+",miao~");
}

cat.say();//调用方法

如果我们再创建一只猫可以

var cat2 = {
	name:"kitty",
	color:"white"
}

如果我们要为cat2创建一个say方法呢?好了,我发现你一定也知道问题所在了——难道我们要对每个cat对象都要定义say方法?
我们当然不希望这样,开发过程痛苦是一方面,运行时不同cat的say方法本质上还是不同的函数对象,这会造成内存浪费,影响性能。

js是如何解决这个问题的呢?

原型

js中所说的原型是一个名为prototype的属性,从作用上来说 ,原型就好比class对象,我们可以将“猫”的公共属性和方法定义在原型对象上,这样就可以达到封装的效果了。

但是呢,拥有这个属性的对象是function,所以上文中如果想通过cat.prototype只能获得一个undefined,因为cat是一个对象,而不是方法。

如何创造出这个方法?

//定义cat构造方法,方法体里有用到this,即调用方法的对象
//通常构造器对通过new 关键字来调用,这意味着js创建了一个新的对象,这个对象就是执行这个方法的this,返回值也是这个this
function Cat(name,color){
	this.name=name;
	this.color=color;
}

//Cat就是创建猫对象的方法,我们可以获取其原型对象,并为原型对象添加一个say方法
Cat.prototype.say=function(){
	alert("i'm "+this.name+",miao~");
}

//创建一个cat
var cat = new Cat("tom","gray");
//调用say方法
cat.say();

上述代码的本质其实很接近java了,这样编写,所有通过 new Cat()创建的对象,就都有say方法了。
我们可以换种方式认为——js通过构造器的原型对象,为一类对象定义通用的属性和方法,这不就是class的作用么?

常见的属性、操作符和方法

  • constructor属性
    对于上文的cat对象,我们可以通过cat.constructor获取其构造函数,也就是Cat对象,注意Cat是一个方法;
cat.constructor == Cat;//true
  • instanceof运算符
    用于判断cat对象是否由Cat方法创建,等价于:a对象是否是A类的实例;
cat instanceof Cat;//true
  • proptotype.isPrototypeOf(obj)方法
    proptotype对象都有方法isPrototypeOf(obj),用于判断obj是不是proptotype的实例,作用和instanceof类似;
Cat.prototype.isPrototypeOf(cat);//true
  • obj.hasOwnProperty(propName)方法
    每个对象都有hasOwnProperty(propName),用于判断某个属性,是由本对象在声明时自己定义的,还是由prototype定义的,true表示前者,false表示后者,propName是个字符串,表示属性名称。
    通常返回true的话,我们成为这个属性为“本地属性”,反之成为“原型属性”。
cat.hasOwnProperty("name");//true
cat.hasOwnProperty("say");//false,方法say也是属性,这里不要写成“say()”
  • in运算符
    in运算符有两个作用,一个是判断某个对象是否包含某个属性,不管是本地还是原型属性;另一个更常见,可以用来遍历一个对象的所有属性
"name" in cat;//true,name 是 cat的属性
//遍历出 name、color、say三个属性
for(var propName in cat){
	alert(propName);//propName 是字符串
	alert(cat[propName]);//通过对象[属性名]的方式,访问属性的值
}

好了,先消化一下本片文章的内容,想想——如果我想为“Cat”这个class定义一个getType()方法,返回的就是原型对象,该怎么实现呢?

下一主题:js之面向对象——继承

    原文作者:hangwen0305
    原文地址: https://blog.csdn.net/hangwen0305/article/details/83592965
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞