js面向对象

本文将循序渐进的介绍js面向对象的基础知识。


js面向对象_第1张图片
面向对象编程的好处

什么是面向对象呢?

js面向对象_第2张图片
一对象

面向对象编程 (OOP : Object Oriented Programming)。
它从90年代开始成为软件开发的主流思想。
它的概念就是将世间万物视为对象,并将其分类。
同一类的对象有相同的属性和行为。
比如,一个人,它包括性别、年龄,颜值等属性。还可以有些行为,比如:微笑、说话、吃饭、写代码等。

面向对象具有4个主要特性:

  • 唯一 :每个对象都有自身唯一的标识,通过这种标识,可找到相应的对象。在对象的整个生命期中,它的标识都不改变。
  • 抽象:将具有一致的属性和行为的对象抽象成类。反映了与应用有关的重要性质,而忽略其他一些无关内容。
  • 继承:继承子类自动共享父类数据结构和方法的机制,这是类之间的一种关系,继承性是面向对象思想最重要的一点。
  • 多态:多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。

面向对象与面向过程到底有什么区别?

  • 可维护性强
    写代码简单,维护起来却不容易。
    经过深思熟虑的抽象,松散的代码可以被重构为“有胳膊有腿”的个体,
    属于它的变量被定义成属性,相关的函数片段被定义成它的行为(方法),看到这样清晰的结构,对这些函数片段和散落的变量就会一目了然其作用。
  • 可复用性强
    说实话,在实际工作前,我读过面向对象的知识,但是从未应用过。
    直到工作中遇到一个特定的需求不得已为之,之后就爱上了这种写法。
    如果让你做一个轮播图,可能100行之内就可以搞定。但是,如果很多页面都需要你这个轮播图,但是有些许不同,比如轮播时间间隔,轮播方向,切换图片后的行为(轮播动画结束时需要执行的链接地址更新,统计等等等回调函数)。
    或者,一个页面有两处轮播,而且两处的class,行为不同,你会将那段代码改吧改吧复制成两个吗?
    如果是面向对象,就可以将方法,属性抽象,初始化不同的实例,实例间求同存异,各自都有自己的状态,互不影响。

如何用JS写面向对象?

创建对象的最简单方式就是创建一个Object实例,然后为它添加属性和方法。

  • 普通模式
var person = new Object();
person.name = ‘Taylor Swift’;
person.job = ‘singer’;
person.sayName =function(){
    alert(this.name);
}

这样,一个有名字,有工作,会说话的人就诞生了。但是如果批量生产多个对象实例呢?

  • 工厂模式:
var create = function(name, job){
    var o= new Object();
    o.name = name;
    o.job = job;
    o.sayName =function(){
        alert(this.name);
    }
    return o;
}
var person = create('Taylor Swift','singer');

这样,我们就可以将实例间的区别以参数的方式传递给工厂函数,返回给我们对象实例。但是,实例却不知道“爹”是谁?

  • 构造函数模式:
var Person = function(name, job){
    this.name = name;
    this.job = job;
    this.sayName =function(){
        alert(this.name);
    }
}
var person1 = new Person('Taylor Swift','singer');
console.log(person1 instanceof Person);

利用构造模式的instanceof方法我们就可以做“亲自鉴定”了。但是注意看,实例化几个对象,就会有多少sayName()方法,但是sayName()方法都相同,没必要浪费内存啊。这样写可不可以?

var Person = function(name, job){
    this.name = name;
    this.job = job;
    this.sayName = sayName;
}
function sayName(){
    alert(this.name);
}

把sayName单独提取出来就可以了,但是看着功能构造器Person没什么关系,很松散。
这时候就要引入一个js中原型对象的概念。

  • 原型对象:
    无论什么时候,只要创建了一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
    就拿前面的例子来说,Person.prototype.consructor 指向 Person.

用原型对象的方式写:

 var Person = function(){};
Person.prototype.name = 'Taylor Swift';
Person.prototype.job = 'singer';
Person.prototype.sayName = function(){
    alert(this.name);
};

我们看看原型方式的内存使用图:


js面向对象_第3张图片
内存使用图.png

但是原型模式还不够完美,它省略了为构造函数传递初始化参数的环节,结果所有实例在默认情况下都获得相同的属性值。
另外,因为的共享机制这个优点,存在一个潜在的问题。
如果共享的对象是引用类型(比如Array),实例之间可能会相互影响。比如下面的例子:

    var Person = function(){};
    Person.prototype.name = 'Taylor Swift';
    Person.prototype.job = 'singer';
    Person.prototype.friends = ['Ivy', 'Cathy'];
    Person.prototype.sayName = function(){
        alert(this.name);
    };
    var person1 = new Person();
    var person2 = new Person();
    person2.friends.push('jordan');
    console.log(person1.friends,person2.friends);

那么,接下来,我们最优的模式就要亮相了:

    var Person = function(){
        this.friends = ['Ivy', 'Cathy'];
        this.name = 'Taylor Swift';
        this.job = 'singer';
    };
    Person.prototype.sayName = function(){
        alert(this.name);
    };

ES6的写法会更简单:

class Person {
    constructor(name) {
        this.name = name;
        this.job = job;
    }
    sayName() {
        console.log(this.name);
    }
}

虽然目前es6兼容性还不够好,但是如果你想用这种写法的话,可以用编译工具,开发环境的文件是es6,生产环境运行的是编译后的es5写法。

你可能感兴趣的:(js面向对象)