为了账号安全,请及时绑定邮箱和手机立即绑定

面向对象之封装性

面向对象编程的三个特点

面向对象编程是比较符合人的认知思维的一种编程方式,比如提到机动车,我们会想到几个重要的属性:速度、轮子、载人等;还有几个重要的行为:加速、刹车、转弯等。
我们经常见到的出租车、救护车、公交车等都属于机动车的实际例子,如果执行“加速”,这些车都会增加前进的速度。
可以说,机动车属于具体实物中抽取出来共同的属性和行为,将其封装在一起,这在面向对象编程中称为封装性;而具体到出租车、救护车、公交车等,全部继承了机动车这些属性和行为,这在面向对象编程中称为继承性;同一个属性或行为,可能会产生不同的效果,比如公交车的载人量大于出租车,这在面向对象编程中称为多态性

封装性

将需要的属性(数据)和行为(对数据的操作,以下内容改称为方法)封装在一起,比如 Java 中“类”的概念,叫做面向对象的封装性。

封装方式

首先,按照惯例,如果类名使用字母,那么首字母大写;
其次,类的内容由两部分构成,属性与方法。如果将属性与方法看作类的成员,按照访问权限划分,分为类成员(也称静态成员,使用关键字 static 给予修饰)、实例成员与私有成员。

ES6 的类

ES6 引入了类的概念,语法比较接近传统面向对象语言。

例子:

class Person {
    //实例属性
    name = "Tom";
    id = "232321";
    //实例方法
    getName() {
        return this.name;
    }

    //私有属性(提案中,暂无浏览器支持)
    #age = 20;
    //私有方法(提案中,暂无浏览器支持)
    #getAge() {
        return this.#age;
    }

    //静态属性(提案中,Chrome80 测试通过)
    static job = "IT";
    //静态方法
    static getJob() {
        return this.job;
    }
}

ES6 并不是本系列手记的重点,因此了解即可。

ES5 的组合封装

ES5 的组合封装是指:通过为构造函数定义属性和方法创建静态成员,通过 this 与构造函数原型创建实例成员,通过函数作用域的特性创建私有成员。

例子:

function Person() {
    //实例属性
    this.name = "Tom";
    this.id = "232321";
    //实例方法
    Person.prototype.getName = function() {
        return this.name;
    };

    //私有属性
    var age = 20;
    //私有方法
    var getAge = function() {
        return this.age;
    };
}
//静态属性
Person.job = "IT";
//静态方法
Person.getJob = function() {
    return this.job;
};

私有成员在其继承者(1)中,都是不可访问的;而静态成员,是仅供构造函数本身使用的,不管有没有创建实例对象,都可以通过函数名直接访问。因此能够发生继承的,只有实例成员。
实例成员由两部分组成:第一部分是定义在 this 上的成员,可以供其继承者直接深度复制,相当于在继承者中直接声明一样,为了便于区分,习惯上称这类成员为实例属性(包括属性和方法);第二部分是构造函数原型中的成员,其继承者可以通过原型链访问到,属于所有继承者共有的,习惯上称这类成员为原型属性(包括属性和方法)。

*(1)继承者:子类以及实例对象。

例子:

function Person() {
    //实例属性
    this.name = "Tom";
    this.ids = [1, 2];
    Person.prototype.nums = [8, 9];
    //实例方法
    Person.prototype.getName = function() {
        return this.name;
    };

    //私有属性
    var age = 20;
    //私有方法
    var getAge = function() {
        return this.age;
    };
}
//静态属性
Person.job = "IT";
//静态方法
Person.getJob = function() {
    return this.job;
};

/*未创建对象之前,访问静态成员*/
console.log(Person.job); //输出:IT
console.log(Person.getJob()); //输出:IT

/*创建对象之后,访问私有成员*/
var p = new Person();
console.log(p.age); //输出:undefined

/*创建对象之后,访问实例成员*/
console.log(p.name); //输出:Tom
console.log(p.getName()); //输出:Tom

/*定义在 this 上的实例成员*/
var p1 = new Person();
console.log(Object.is(p.ids, p1.ids));
//输出 false,说明储存空间不同,属于复制

/*定义在函数原型上的实例成员*/
console.log(Object.is(p.nums, p1.nums));
//输出 true,说明储存空间相同,属于原型链查询

利用闭包访问私有成员

闭包是有权访问另一个函数作用域中变量的函数,利用这一点,我们能够创建访问私有成员的特权方法。

例子:

function Person() {
    //私有属性
    var age = 20;

    //特权方法
    this.getAge = function() {
        return age;
    };
    this.setAge = function(value) {
        age = value;
    };
};

var p = new Person();
p.setAge(18);
console.log(p.getAge()); //输出:18

甚至可以直接将闭包作为创建实例对象的构造函数。

例子:

var Person = (function() {
    //私有属性
    var age = 20;

    //构造函数
    return function() {
        this.getAge = function() {
            return age;
        };
        this.setAge = function(value) {
            age = value;
        };
    };
})();

var p = new Person();
p.setAge(18);
console.log(p.getAge()); //输出:18

安全检测

鉴于构造函数只有在 new 关键字的作用下才会生成实例对象,因此检测构造函数的调用方式变得尤为重要。instanceof 以及 ES6 引入的 new.target 属性都可以用来做此类的安全检测。

例子:

function Person() {
    if (this instanceof Person) { //或者 new.target === Person
        this.name = "Tom";
    } else {
        return new Person();
    }
}

var p1 = new Person();
var p2 = Person();

如此一来,即使忘记了使用 new 关键字,仍然可以正确地创建实例对象。


如有错误,欢迎指正,本人不胜感激。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消