美文网首页
27-第二十七章 面向对象oop 构造函数(一)

27-第二十七章 面向对象oop 构造函数(一)

作者: 晚溪呀 | 来源:发表于2019-02-22 01:18 被阅读0次

    Javascript是一种基于对象(object-based)的语言。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有 class(类)—– es6以前是这样的
    面向对象程序设计具有许多优点:
    1、开发时间短,效率高,可靠性高,所开发的程序更强壮。由于面向对象编程的可重用性,可以在应用程序中大量采用成熟的类库,从而缩短了开发时间。
    2、应用程序更易于维护、更新和升级。继承和封装使得应用程序的修改带来的影响更加局部化。

    new 执行的函数,函数内部默认生成了一个对象
    函数内部的this默认指向了这个new生成的对象
    new执行函数生成的这个对象,是函数的默认返回值

    一、 面向对象四个基本特征

    对象:对象这个词,本身就是抽象的,也就是把客观事物抽象的成类
    封装:封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
    继承:通过继承创建的新类称为“子类”或“派生类”。继承的过程,就是从一般到特殊的过程。
    多态:对象的多功能,多方法,及方法

    二、对象是什么?

    对象有”属性“(property)和”方法“(method)

    三 对象实例化方式

    原始模式
           var Car = {
                color: 'red', // 车的颜色
                wheel: 4, // 车轮数量
            }
            alert(Car.color); // red
    

    生成两个实例对象

            var Car = {
                color: 'red',
                wheel: 4,
            }
            var Car2 = {
                color: 'blue',
                wheel: 4,
            }    
    

    这样的写法有两个缺点,一是如果多生成几个(100个!)实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。

    二、 原始模式的改进

    我们可以写一个函数,解决代码重复的问题。

            function createCar(color,wheel) {
            return {
              color: color,
              wheel: wheel
            }
          }
        
            // 然后生成实例对象,就等于是在调用函数:
            var cat1 = createCar("红色","4");
            var cat2 = createCar("蓝色","4");
            
            alert(cat1.color) // 红色    
    

    三、 工厂模式

            function createCar(color,wheel){ // createCar工厂
                var obj = new Object; // 或obj = {} 原材料阶段
                obj.color = color; // 加工
                obj.wheel = wheel; // 加工
                return obj; // 输出产品
            }
            // 实例化
            var cat1 = createCar("红色","4");
            var cat2 = createCar("蓝色","4");
            alert(cat1.color); //红色    
    

    四、 构造函数模式

    为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。
    所谓”构造函数”,其实就是一个普通函数,但是内部使用了 this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

            function CreateCar(color,wheel){ // 构造函数首字母大写
                // 不需要自己创建对象了
                this.color = color; // 添加属性,this指向构造函数的实例对象
                this.wheel = wheel; // 添加属性
            
                // 不需要自己return了
            }
            
            // 实例化
            var cat1 = new CreateCar("红色","4");
            var cat2 = new CreateCar("蓝色","4");
            alert(cat1.color); // 红色    
    

    new 函数构造内部变化:

    自动生成一个对象
    this指向这个对象
    函数自动返回这个对象

    五、 构造函数注意事项

    1、此时 CreateCar 称之为构造函数,也可以称之类,构造函数就是类
    2 、cat1,cat2均为CreateCar的实例对象
    3、 CreateCar 构造函数中 this 指向CreateCar实例对象即 new CreateCar( )出来的对象
    4、 必须带 new
    5、 构造函数首字母大写,这是规范,官方都遵循这一个规范,如 Number() Array()
    6、 constructor
    这时 cat1cat2 会自动含有一个constructor 属性,指向它们的构造函数,即CreateCar。 /kənˈstrʌktə(r)/构造函数

    要看一个实例的原型是哪一个构造函数的,只需要看 constructor 即可

          alert(cat1.constructor === CreateCar); // true
          alert(cat2.constructor === CreateCar); // true
    

    7、object instanceof constructor 运算符,验证构造函数与实例对象之间的关系。
    instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象的原型链中的任何位置
    instanceof 运算符 /'ɪnst(ə)ns/ 实例,例子

            alert(cat1 instanceof CreateCar );  // true
            alert(cat2 instanceof CreateCar );  // true
    

    obj1 instanceof obj2
    用于确定 obj2 对象是否在 obj1 对象的原型上

    8、实例的 __proto__ 等于 实例对象对应的类的 prototype

    9、要扩展多个方法采用重新赋值,重新赋值 要修改constructor的指向
    单个方法直接 . 扩展就行了

    10、for in 不仅会遍历对象,还会遍历原型

            function CreateFun(name, len) {
                this.name = name,
                this.len = len
                // 实例的 `__proto__` 等于 实例对象对应的类的 `prototype`
                // p1.__proto__ = CreateFun.prototype
            }
    
            CreateFun.prototype = {
                constructor: CreateFun, // 对应的类(构造函数),
                add(){}
            }
    

    11、原型可以改变
    CreateFun.prototype = Array.prototype;
    这个实例的原型拥有数组所有的方法
    这个实例对象被 CreateFun 实例出来的,也被 Array 实例出来的

    六、 JavaScript值类型(基本类型)和引用类型:

    (1)值类型:数字、字符串、布尔值、null、undefined。
    值类型理解:对变量赋值 会开辟新的内存地址
    变量的交换等于在一个新的地方按照连锁店的规范标准(统一店面理解为相同的变量内容)新开一个分店,这样新开的店与其它旧店互不相关、各自运营。
    (2)引用类型:对象、数组、函数。
    引用类型理解:对变量赋值 指向相同的内存地址
    变量的交换等于把现有一间店的钥匙(变量引用地址)复制一把给了另外一个老板,此时两个老板同时管理一间店,两个老板的行为都有可能对一间店的运营造成影响。
    但是函数不会造成影响,因为函数内部的逻辑是不会被改变的

    七、 构造函数的问题:

    构造函数方法很好用,但是存在一个浪费内存的问题。
    请看,我们现在为再添加一个方法 ·showWheel·。那么,·CreateCar·就变成了下面这样:

            function CreateCar(color, wheel) {
                this.color = color;
                this.wheel = wheel;
                this.showWheel = function () { // 添加一个新方法
                    alert(this.wheel);
                }
            }
    
            //还是采用同样的方法,生成实例:
            var cat1 = new CreateCar("红色", "4");
            var cat2 = new CreateCar("蓝色", "4");    
    

    表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象, type 属性和 eat() 方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

            alert(cat1.showWheel == cat2.showWheel);  // false
    

    八、 Prototype 原型

    Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
    这意味着,我们可以把那些不变的属性和方法,直接定义在 prototype 对象上。

            function CreateCar(color, wheel) {
                // 属性写构造函数里面
                this.color = color;
                this.wheel = wheel;
            }
    
            // 方法写原型里面
            CreateCar.prototype.showWheel = function () {
                alert(this.wheel);
            }
            CreateCar.prototype.showName = function () {
                alert('车');
            }
    
            //然后,生成实例。
            var cat1 = new CreateCar("红色", "4");
            var cat2 = new CreateCar("蓝色", "4");
            alert(cat1.showName); // '车'
            alert(cat1.showWheel == cat2.showWheel); // true
            alert(cat1.showName == cat2.showName); // true
            // 这时所有实例的showWheel属性和showName方法,
            // 实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
           
            alert(cat1.showWheel == cat2.showWheel); // true
    

    九、面向对象 ES6 写法

    class( ES5 保留字, ES6 就是关键字) ES6 定义一个类
    ES6 实例的时候还是 new
    ES5 原型上可以定义普通属性,但是不推荐
    ES6 是不可以的,constructor 以外的只能是函数
    私有属性后面不要加任何符号
    公有属性后面也不要加任何符号

            class CreateFun{
                constructor(name, age) { // 私有属性
                    this.name = name,
                    this.age = age
                } // 不能加符号
                // 下面是公有属性
                add(value) {
                    console.log(value);
                } // 不能加符号
                sub(value) {
                    console.log(value);
                }
            }
    
            // 实例对象是被对应的类实例出来的
            const obj = new CreateFun('大帅逼', '18'); 
    

    十、面向过程 改写面向对象:

    要求:不要出现函数嵌套
    变量-> 属性
    函数->方法
    难点:this指向

    案例:用面向对象改写 拖拽
    案例:用面向对象改写 轮播

    相关文章

      网友评论

          本文标题:27-第二十七章 面向对象oop 构造函数(一)

          本文链接:https://www.haomeiwen.com/subject/kwajyqtx.html