日期:2024年12月3日

封装

封装是面向对象的第一个特征,也是最容易理解的一个特征。所谓封装就是将一些属性(方法)统一放到一个对象中,来完成事物的抽象。封装的关键点在于一个字装。

我们要表示一个人的信息,就要将这个人相关的属性和方法装到一个对象中。要表示一辆汽车的信息,就要把一辆汽车的属性和方法装到一个对象中。就像我们之前举的猪八戒的那个例子一样。

const zbj = {
     name:'猪八戒',
     age:28,
     address:'高老庄',
     sleep:function () {
         console.log(`${this.name}睡着了~~~`);
     }
 };

封装的概念实在是简单不过。但是这样也存在一个问题,上例中直接通过对象字面量({})来创建对象。这种方式创建对象的方式好理解,也十分的方便,但是如果在创建大量对象时依然采用这种方式似乎不是特别的明智。

假设我们要创建10个表示人的信息的对象,它们的结构和猪八戒一样,都有name、age、address三个属性和sleep一个方法,只是name、age和address的值不相同,如果采用上边的方式创建,我们必须要把相同的代码写10次,可能是这样的:

const zbj = {
     name:'猪八戒',
     age:28,
     address:'高老庄',
     sleep:function () {
         console.log(`${this.name}睡着了~~~`);
     }
 };
const swk = {
     name:'孙悟空',
     age:18,
     address:'花果山',
     sleep:function () {
         console.log(`${this.name}睡着了~~~`);
     }
 };
const shs = {
     name:'沙和尚',
     age:38,
     address:'流沙河',
     sleep:function () {
         console.log(`${this.name}睡着了~~~`);
     }
 };
略……

上例中只创建了三个对象,但你会发现创建对象的过程中出现了大量的重复代码,并且这种创建对象的方式实在是太自由了,对象中有什么属性,没有什么属性完全没有限制,说不准某个对象的name属性可能被我写成了naem我都发现不了。

使用函数来创建对象

我们需要另外一种更加方便的创建对象的方式,遇到重复代码第一个想到的就是通过定义函数来避免重复的代码,由此我们可以使用这样一个函数来创建对象。

function createPerson(name, age, address){
    return {
        name:name,
        age:age,
        address:address,
        sleep:function(){
            console.log(`${this.name}睡着了~~~`)
        }
    };
}

const zbj = createPerson('猪八戒',28,'高老庄');
const swk = createPerson('孙悟空',18,'花果山');
const shs = createPerson('沙和尚',38,'流沙河');

怎么样,这样创建对象是不是简单的多了?

构造函数

上述方式中的函数可以用来创建对象,但它也不是我们通常会使用的方式。在JS中为我们提供了一种专门用来创建对象的函数叫做构造函数(constructor)。

定义构造函数和定义一个普通函数没有区别,都是使用function关键字,不同点在于普通函数的名字是小写字母开头,而构造函数是大写字母开头。

function MyClass(){}

上例中的函数就是一个构造函数,构造函数我们也可以称为类(Class),所以起名字时,将构造函数的名字命名为MyClass。

一个函数如果直接调用那么它就是一个普通函数,如果使用new关键字调用那么它就是一个构造函数。

普通函数:

MyClass()

构造函数:

new MyClass()

这一点真的是JS中非常让人烦恼的地方,这里的烦恼并不是指构造函数的使用,而是构造函数的理解上。对于初学者来说,这里会比较痛苦,两种函数的作用不同,但定义方式却没什么太大的区别,没办法,愣记吧!直接调用是普通函数,使用new调用是构造函数。

调用构造函数时,总会返回一个新的对象。

let mc = new MyClass();

上例中调用了构造函数,同时将其返回指赋值给了mc,现在mc指向的就是构造函数所创建的新的对象,当然现在对象中什么属性都没有,它是一个空的对象。在构造函数中,this关键字指向的是它新建的对象,所以可以通过this关键字来向新对象中添加属性,比如可以这样写。

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sleep = function(){
        console.log(`${this.name}睡着了~~~`);
    };
}
let p1 = new Person('孙悟空',18);

在构造函数中this表示的就是新建的对象,向this中添加属性就相当于向新建对象中添加属性。如此一来我们创建好了构造函数Person,并且可以通过Person来创建出许多的Person对象,调用时不要忘了加new。

一个构造函数也被称为一个类,也最好直接称其为类。如果一个对象是通过某个类所创建的,那么我们就说这个对象是这个类的实例,上例中p1就是Person类的实例。通过同一个类创建的对象,称为一类对象。可以通过instancof运算符来检查一个对象是否是一个类的实例:

function Person(name, age){
     this.name = name;
     this.age = age;
     this.sleep = function(){
         console.log(`${this.name}睡着了~~~`);
     };
}
let p1 = new Person('孙悟空',18);
let o = {};
 
console.log(p1 instanceof Person); //true
console.log(o instanceof Person); //false

上例中p1是有Person类所创建,所以使用instance检查是返回true,o不是通过Person所创建,使用instance检查时返回false。

原型

上例中的Person构造函数中含有两个属性和一个方法,两个属性是name和age,一个方法是sleep。在Person中通过this将属性和方法统一添加到了新的对象上。这样会带来一个问题,每创建一个新的对象,就会在新的对象上添加name、age还有sleep。这一点对于name和age来说不是问题,因为每个Person实例的name和age属性都是不同的。但是对于sleep却不是这样了,因为无论是孙悟空还是猪八戒,睡觉的方法都是一摸一样的。也就是我们完全可以让多个对象共用一个方法,而不需要为每一个对象都添加一个方法。

构造函数拥有一个属性叫做prototype,中文翻译过了叫做原型。它指向的是一个对象,所以我们称其为原型对象。一个类只有一个原型对象,一个类的原型对象可以被它的所有实例访问。原型对象就相当于是一个公共的区域由所有实例共享。

在创建类时,可以把一些公共的属性或方法统一设置在原型对象中,这样所有的实例都能使用这些属性或方法,并且可以避免对这些属性或方法重复创建。

function Person(name, age){
     this.name = name;
     this.age = age;
}
 
//将sleep方法添加到原型中,可以避免重复创建方法
Person.prototype.sleep = function(){
     console.log(`${this.name}睡着了~~~`);
};

上例的创建方式和之前的执行效果是相同的,但是这种方式可以有效的避免重复创建方法对象。总之先记着,定义构造函数时,值不同的属性方法(像name和age)在函数中通过this设置。值相同的属性方法(像sleep)在函数外通过原型对象设置。

5 6 投票数
文章评分
订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x