前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面向对象编程

面向对象编程

作者头像
子舒
发布2022-06-09 13:44:34
6800
发布2022-06-09 13:44:34
举报
文章被收录于专栏:子舒的个人博客

面向对象把构成问题的transaction分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为,意在写出通用代码,加强代码重用,屏蔽差异性。

一、什么是面向对象编程

js是基于原型的,基于面向对象编程

面向对象就是把数据和对数据的操作方法放在一起,作为一个整体——对象。对同类对象抽象出其共性,形成类

1.面向过程程序设计

将一个项目(或者一个事件)从头到尾按顺序,一步一步完成,先做什么,后做什么,一直到结束,也是我们人做事的方法。

自上而下,先确定一个整体的框架,然后添砖加瓦,逐步实现想要得到的效果,适用于简单的系统,容易理解。但是难以应对复杂的系统,不易维护扩展,难以复用

面向过程是分析解决问题的步骤,然后用函数把这些步骤一步一步的实现,然后在使用的时候一一调用则可。强调的是完成这件事儿的动作,更接近我们日常处理事情的思维。

2.面向对象程序设计

将一个项目(或者一个事件)分成更小的项目,每一个部分负责一方面的功能,最后由这些部分组成一个整体,先设计组件,在完成拼装,适用于大型复杂的系统

面向对象把构成问题的transaction分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为,意在写出通用代码,加强代码重用,屏蔽差异性。

想要弄明白面向对象,需要先理解类和对象的概念

《什么是类和对象?》

二、创建对象的方法

1.创建字面量和实例

代码语言:javascript
复制
window.onload = function() {
    // 实例
    var person = new Object();
    person.name = '小明';
    person.age = 22;
    person.year = function() {
        console.log(this.name + '今年' + this.age + '岁了!')
    };
    person.year();

    // 字面量
    var student = {
        name: '小明',
        age: 22,
        year: function () {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
    }
    student.year();
}

// 小明今年22岁了!

两者输出的结果是一样的,控制台输出:

缺点:重复实例化对象,代码冗余高

2.工厂模式

代码语言:javascript
复制
window.onload = function() {
    function createObj(name, age) {
        var obj = new Object();
        obj.name = name,
        obj.age = age,
        obj.year = function() {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
        return obj;
    }
    var obj = createObj('小明', 22);
    obj.year();
}

// 小明今年22岁了!

优点:解决重复实例化对象的问题 缺点:无法识别对象的类型,因为所有的实例都指向一个原型

3.构造函数

代码语言:javascript
复制
window.onload = function() {
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.year = function() {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
    }
    var student = new Person('小明', 22);
    student.year();
}

// 小明今年22岁了!

优点:可以识别对象的类型 缺点:多个实例重复创建方法,无法共享

4. 原型模式

代码语言:javascript
复制
window.onload = function() {
    function Par() {}
    Par.prototype = {
        constructor: 'Par',
        name: '小明',
        age: 22,
        year: function() {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
    };
    var son = new Par();
    son.year();
}

// 小明今年22岁了!

缺点:所有实例共享他的属性和方法,不能传参和初始化属性值

5.混合模式(推荐使用)

是构造函数和原型模式混合的写法,拥有各自的优点,构造函数共享实例属性,原型模式共享方法和想要共享的属性,可以传参和初始化属性值

先用构造函数定义对象的属性方法,然后用原型模式创建方法,使用的属性通过prototype获取,有一个constructor属性,可以指向要操作的函数对象(构造函数)

比如constructor: Par,就代表下面这个原型方法指向Par()对象(构造函数)

代码语言:javascript
复制
window.onload = function() {
    function Par(name, age) {
        this.name = name;
        this.age = age;
    }
    Par.prototype = {
        constructor: Par,
        year: function() {
            console.log(this.name + '今年' + this.age + '岁了!');
        }
    };
    var son = new Par('小明', 22)
    son.year();
}

// 小明今年22岁了!

三、原型,原型链

1.原型对象

  1. 函数对象都具有prototype属性,它指向函数的原型对象(浏览器内存创建的对象),原型对象都具有constructor属性,它指向prototype属性所在的函数对象(构造函数)
代码语言:javascript
复制
window.onload = function() {
    function Par(name, age) {
        this.name = name;
        this.age = age;
    }
    Par.prototype = {
    // constructor指向对象
        constructor: Par,
        year: function() {
            console.log(this.name + '今年' + this.age + '岁了!');
        }
    };
    var son = new Par('小明', 22)
    son.year();

/*********************************************/
    console.log(Par.prototype)
    console.log(Par.prototype.constructor)
/*********************************************/
}

通过控制台可以看到

构造函数的prototypr属性指向原型对象

原型对象的construcyor属性指向构造函数

  1. 当调用构造函数创建一个实例后,该实例会有一个隐藏属性__proto__ ,它指向构造函数的原型对象
代码语言:javascript
复制
console.log(son.__proto__ === Par.prototype)

// true
  1. 所有的构造函数的prototype都是object类型
代码语言:javascript
复制
console.log(typeof Par.prototype)

// object
  1. Function的prototype是一个空函数,所有内置函数的__proto__属性都指向这个空函数
代码语言:javascript
复制
console.log(Math.__proto__)
  1. 如果构造函数实例和原型对象中同时定义了一个属性,在调用时,会屏蔽原型对象中的属性,如果想要访问原型对象中的属性值,需要通过delete方法将同名属性在实例(构造函数)中彻底删除
代码语言:javascript
复制
window.onload = function () {
    function Par(name) {
        this.name = name;
    }
    Par.prototype.name = "张三";
    var son = new Par("李四");
    console.log(son.name); // 李四
    console.log(son.__proto__.name); // 张三

    // 使用delete删除实例的同名属性值
    console.log(delete son.name);   // true
    console.log(son.name); // 张三
}
  1. 通过hasOwnProperty(属性名)可以判断一个属性存在于构造函数中,还是原型对象中

true表示存在构造函数中;false表示存在原型对象中

代码语言:javascript
复制
console.log(Par.hasOwnProperty(name));  // false
  1. 操作符in,可以判断一个属性是否存在(存在于构造函数和原型对象中皆可)
代码语言:javascript
复制
window.onload = function () {
    function Par(name, age) {
        this.name = name;
        this.age = age;
    }
    Par.prototype = {
        constructor: Par,
        year: function() {
            console.log(this.name + this.age)
        }
    };
    var son = new Par('xm', '22')
    son.year();
    console.log('name' in Par); // true
    console.log('age' in Par);  // false
}

同样的两个属性,判断其是否存在于实例或者原型对象中,输出的结果不一样

参考:《对象中是否有某一个属性 in》https://www.cnblogs.com/IwishIcould/p/12333739.html

2.__proto__和prototype的区别

  1. prototype属性只有函数对象上才有,而__proto__属性所有对象都有
  2. prototype是由函数对象指向原型对象,而__proto__是由实例指向函数对象的原型对象
  3. 原型链,将父类型的实例作为子类型的原型对象,这种链式关系叫做原型链

3.继承

  1. 原型链继承

优点:父类原型定义的属性和方法可以复用 缺点:子类实例没有自己的属性,不能向父类传递参数

代码语言:javascript
复制
function test1() {
    function SuperType() {
        this.city = [ "北京", "上海", "天津" ];
        this.property = true;
    }
    SuperType.prototype = {
        constructor : SuperType,     // 保持构造函数和原型对象的完整性
        age : 15,
        getSuperValue : function() {
            return this.property;
        }
    };
    function SonType() {
        this.property = false;
    }

    // 重写子类的原型指向父类的实例:继承父类的原型
    SubType.prototype = new SuperType();

    SubType.prototype = {
        constructor : SubType,
        getSonType : function() {
            return this.property;
        }
    };

    // 优点验证
    let son = new SubType();
    console.log(son.age); // 15
    console.log(son.getSuperValue()); // false

    // 缺点验证
    let instance1 = new SubType();
    instance1.city.push("重庆");
    console.log(instance1.city); // ["北京", "上海", "天津", "重庆"]

    let instance2 = new SubType();
    console.log(instance2.city); // ["北京", "上海", "天津", "重庆"]

}

// test1();
  1. 构造函数继承

优点:子类实例有自己的属性,可以向父类传递参数,解决原型链继承的缺点 缺点:父类原型的属性和方法不可复用

代码语言:javascript
复制
function test2() {
    function SuperType(name) {
        this.name = name;
        this.city = [ "北京", "上海", "天津" ]
    }
    SuperType.prototype = {
        constructor : SuperType,
        age : 18,
        showInfo : function() {
            return this.name;
        }
    };

    function SubType() {
        // 父类调用call()或者apply()方法和子类共用同一个this,实现子类实例属性的继承
        SuperType.call(this, "张三");
    }

    // 优点验证
    let instance = new SubType();
    instance.city.push("重庆");
    console.log(instance.city); // ["北京", "上海", "天津", "重庆"]

    let instance1 = new SubType();
    console.log(instance1.city); // ["北京", "上海", "天津"]

    // 缺点验证
    console.log(instance.age); // undefined
    instance.showInfo(); // son.showInfo is not a function
}

// test2();
  1. 组合继承(推荐)

优点:原型的属性和方法可以复用,每个子类实例都有自己的属性 缺点:父类构造函数调用了两次,子类原型中的父类实例属性被子类实例覆盖

代码语言:javascript
复制
function test3() {
    function SuperType(name) {
        this.name = name;
        this.city = [ "北京", "上海", "天津" ]
    }
    SuperType.prototype = {
        constructor : SuperType,
        showInfo : function() {
            console.log(this.name + "今年" + this.age + "岁了");
        }
    };

    function SubType(name, age) {
        // 1. 通过构造方法继承实现实例属性的继承
        SuperType.call(this, name);
        this.age = age;
    }

    // 2. 通过原型链继承实现原型方法的继承
    SubType.prototype = new SuperType();

    // 优点验证
    let instance = new SubType("张三", 15);
    instance.showInfo(); // 张三今年15岁了

    let instance1 = new SubType();
    instance1.city.push("重庆");
    console.log(instance1.city); // ["北京", "上海", "天津", "重庆"]

    let instance2 = new SubType();
    console.log(instance2.city); // ["北京", "上海", "天津"]

}

// test3();
  1. 寄生组合继承(推荐)

优点:解决了组合继承的缺点,效率高 缺点:基本没有

代码语言:javascript
复制
function test4() {
    function inheritPrototype(subType, superType) {
        // 1. 继承父类的原型
        var prototype = Object.create(superType.prototype);
        // 2. 重写被污染的construct
        prototype.constructor = subType;
        // 3. 重写子类的原型
        subType.prototype = prototype;
    }
    function SuperType(name) {
        this.name = name;
        this.city = [ "北京", "上海", "天津" ];
    }

    SuperType.prototype.sayName = function() {
        console.log(this.name);
    };

    function SubType(name, age) {
        SuperType.call(this, name);
        this.age = age;
    }

    // 将父类原型指向子类
    inheritPrototype(SubType, SuperType);

    SubType.prototype.sayAge = function() {
        console.log(this.age);
    }

    // 优点验证
    let instance = new SubType("张三", 15);
    instance.sayName(); // 张三

    let instance1 = new SubType();
    instance1.city.push("重庆");
    console.log(instance1.city); // ["北京", "上海", "天津", "重庆"]

    let instance2 = new SubType();
    console.log(instance2.city); // ["北京", "上海", "天津"]
}

// test4();

4.ES6新方法--class

新的关键字class在es6开始被引入到javascript中来,class的目的就是让定义类更简单

用函数方法实现:

代码语言:javascript
复制
function Person(name) {
    this.name = name;
}

Person.prototype.hello = function () {
    console.log('Hello, ' + this.name + '!');
}

var son = new Person('xm')
son.hello();    // Hello, xm!

class来实现:

代码语言:javascript
复制
class Person {
    constructor(name) {
        this.name = name;
    }

    hello() {
        console.log('Hello, ' + this.name + '!');
    }
}

var son = new person('xm')
son.hello();    // Hello, xm!

可以在看到,在定义class中,直接包含了构造函数constructor属性,和原型对象上的函数hello()方法,省略掉了function关键字

需要注意:原来的写法是,构造函数和原型对象分散开来写,现在用class可以直接把两者串在一个对象中,只有最后传参和调用方法时写法是一样的

class继承

class定义对象的另一个巨大的好处是继承更方便了。想一想我们从Person派生一个PrimaryPerson需要编写的代码量。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:

代码语言:javascript
复制
class PrimaryPerson extends Person {
    constructor(name, grade) {
        super(name); // 记得用super调用父类的构造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

注意PrimaryPerson的定义也是通过class关键字实现的,而extends则表示原型链对象来自Person,子类的构造函数可能会和父类的不太相同

例如,PrimaryPerson需要namegrade两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。

PrimaryPerson已经自动获得了父类Personhello方法,我们又在子类中定义了新的myGrade方法。

ES6引入的class和原有的JavaScript原型继承有什么区别呢?

实际上它们没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。

但是!

目前并不是所有的浏览器都支持class,所以在选择的时候一定要慎重!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020 年 11 月,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、什么是面向对象编程
    • 1.面向过程程序设计
      • 2.面向对象程序设计
      • 二、创建对象的方法
        • 1.创建字面量和实例
          • 2.工厂模式
            • 3.构造函数
              • 4. 原型模式
                • 5.混合模式(推荐使用)
                • 三、原型,原型链
                  • 1.原型对象
                    • 2.__proto__和prototype的区别
                      • 3.继承
                        • 4.ES6新方法--class
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档