JavaScript面向对象编程 – 对象

2017年9月30日 820 次阅读 0 条评论 3.05k 个文字

简单描述

  • JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。
  • JavaScript也是一种基于对象object-based的语言,但他并不是一种真正的面向对象编程语言,因为与其他面向对象语言相比,如JavaC#等没有class

面向对象的三大特性:封装、继承、多肽,本篇博客主要记录关于JavaScript的封装,如何将JavaScript中的属性property以及方法method封装成一个对象。

内置对象

  • Array:提供一个数组的模型、存储大量有序的数据。
  • Boolean:表示两个值:"true" 或 "false"。
  • Date:对象用于处理日期和时间。
  • Math:对象用于执行数学任务。
  • Number: 对象是原始数值的包装对象。
  • String: 对象用于处理文本(字符串)。
  • RegExp:对象表示正则表达式,它是对字符串执行模式匹配的强大工具。
  • Global:全局属性和函数可用于所有内建的 JavaScript 对象。

除了JavaScript内置对象以外还有Browser对象以及HTML DOM对象,这里就不做描述了,可以参考JavaScript 参考手册

生成实例对象的原始模式


假如我们将人看成一个对象,他有“姓名”、“性别”、“年龄”三个属性。

var Person = {
    name: 'Silkage',
    sex: 'man',
    age: 24
};
var silkage = {};
silkage.name = 'Silkage';
silkage.sex = 'man';
silkage.age = 24;
var graceful = {};
graceful.name = 'Graceful';
graceful.sex = 'woman';
graceful.age = 24;

这就算是最基本的封装,将三个属性封装在一个对象里。但是,我们的实际开发中并没有像这样去写,毕竟这样太麻烦了。如果生成多个实例,通过这个方式就会写太多冗余代码,而实例与原型之间也毫无联系。

原始模式的改进

function Person(name, sex, age) {
    return {
        name: name,
        sex: sex,
        age: age
    }
}
var silkage = Person('Silkage', 'man', 24);
var graceful = Person('Graceful', 'woman', 24);

改进后的方式虽然不会像之前那样麻烦,但是silkage与graceful之间仍然没有内在联系,不能够反应出他们是同一个原型对象的实例。

构造函数模式


为了解决从原型对象生成实例的问题,JavaScript中提供构造函数的方式构造对象。构造函数也是一种普通函数,不过在其内部使用this变量。

function Person(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
}
var silkage =new Person('Silkage', 'man', 24);
var graceful =new Person('Graceful', 'woman', 24);

如何验证原型对象与实例对象之间存在的关系呢?我们可以使用实例对象的constructor属性或者使用instanceof运算符。

console.log(silkage.constructor == Person); //true
console.log(graceful.constructor == Person); //true

console.log(silkage instanceof Person); //true
console.log(graceful instanceof Person); //true

虽然构造函数的方法很好用,但是会存在一个浪费内存的问题。
如果我们给Person再添加一个不变的属性type以及一个不变的方法sleep,那么原型对象就会变成这样:

function Person(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.type = '高等动物';
    this.sleep = function() {
        console.log('睡觉zzz')
    };
}
var silkage = new Person('Silkage', 'man', 24);
var graceful = new Person('Graceful', 'woman', 24);
console.log(silkage.type == graceful.type);//false

看起来并没有什么不妥,但实际上这样做有一个很大的弊端。每生成一个实例对象,type属性和sleep()方法都是一模一样的内容,都会造成内存的占用,而且也会影响效率。
而且从上面打印看出,两个由同一个原型对象生成的实例对象中,type属性并不是指向同一个内存地址。

Prototype模式


JavaScript里,每个构造函数都有一个prototype属性,这个属性指向另一个对象。这个对象的所有属性和方法都会被构造函数的实例继承。
这样我们就可以将对象不变的属性和方法直接定义在prototype对象上。

function Person(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
}
Person.prototype.type = '高等动物';
Person.prototype.sleep = function() {
    console.log('睡觉zzz')
};
var silkage = new Person('Silkage', 'man', 24);
var graceful = new Person('Graceful', 'woman', 24);
console.log(silkage.type == graceful.type); //true

这时所有实例的type属性和sleep()方法其实都是同一个内存地址,指向prototype对象,因此提高了运行效率,节约了内存。

Prototype模式的验证方法


isPrototypeOf():判断某个proptotype对象和某个实例之间的关系。

console.log(Person.prototype.isPrototypeOf(silkage)); //true
console.log(Person.prototype.isPrototypeOf(graceful));//true

hasOwnProperty():判断实例的属性是本地属性还是继承自prototype对象的属性。

console.log(silkage.hasOwnProperty("name")); //true
console.log(silkage.hasOwnProperty("type"));//false

in运算符:可以判断实例是否含有某个属性。

console.log("name" in silkage); //true
console.log("type" in silkage);//true

参考资料

当你不够强大的时候,任何的赞美都是嘲讽