对象高级

1 面向对象

1.1 对象创建方式

  • 方式一: Object构造函数模式

    • 套路: 先创建空Object对象, 再动态添加属性/方法

      • 适用场景: 起始时不确定对象内部数据

      • 问题: 语句太多

    /*
      一个人: name:"Tom", age: 12
    */
    var p = new Object()
    p = {}
    p.name = 'Tom'
    p.age = 12
    p.setName = function (name) {
        this.name = name
    }
    p.setaAge = function (age) {
        this.age = age
    }
    
    console.log(p)
  • 方式二: 对象字面量模式

    • 套路: 使用{}创建对象, 同时指定属性/方法

    • 适用场景: 起始时对象内部数据是确定的

    • 问题: 如果创建多个对象, 有重复代码

    var p = {
        name: 'Tom',
        age: 23,
        setName: function (name) {
            this.name = name
        }
    }
    console.log(p.name, p.age)
    p.setName('JACK')
    console.log(p.name, p.age)
    
    var p2 = {
        name: 'BOB',
        age: 24,
        setName: function (name) {
            this.name = name
        }
    }
  • 方式三: 工厂模式

    • 套路: 通过工厂函数动态创建对象并返回

    • 适用场景: 需要创建多个对象

    • 问题: 对象没有一个具体的类型, 都是Object类型

    // 工厂函数: 返回一个需要的数据的函数
    function createPerson(name, age) {
        var p = {
            name: name,
            age: age,
            setName: function (name) {
                this.name = name
            }
        }
        return p
    }
    
    var p1 = createPerson('Tom', 12)
    var p2 = createPerson('JAck', 13)
    console.log(p1)
    console.log(p2)
  • 方式四: 自定义构造函数模式

    • 套路: 自定义构造函数, 通过new创建对象
    • 适用场景: 需要创建多个类型确定的对象
    • 问题: 每个对象都有相同的数据, 浪费内存
    function Person(name, age) {
        this.name = name
        this.age = age
        this.setName = function (name) {
            this.name = name
        }
    }
    
    var p1 = new Person('Tom', 12)
    var p2 = new Person('Tom2', 13)
    console.log(p1, p1 instanceof Person)
  • 方式五: 构造函数+原型的组合模式

    • 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上
    • 适用场景: 需要创建多个类型确定的对象
    function Person (name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.setName = function (name) {
        this.name = name
    }
    var p1 = new Person('Tom', 12)
    var p2 = new Person('JAck', 23)
    p1.setName('TOM3')
    console.log(p1)
    
    Person.prototype.setAge = function (age) {
        this.age = age
    }
    p1.setAge(23)
    console.log(p1.age)
    
    Person.prototype = {}
    p1.setAge(34)
    console.log(p1)
    var p3 = new Person('BOB', 12)
    p3.setAge(12)

1.2 公有、私有、静态属性和方法

  • 公有属性和公有方法:设置给实例化对象的属性和方法被称作公有属性和公有方法

    function A(name) {
        //公有属性
        this.name = name;
    }
    A.prototype.do = function () {
        //公有方法
        console.log(1);
    }
  • 私有属性和私有方法:声明在构造函数中的变量或函数,被称作为私有属性和私有方法

    function A(name) {
        //私有属性
        var num = 0;
        //公有属性
        this.name = name;
        //私有方法
        function fn() {
    
        }
    }
  • 静态属性和方法:js中无需实例化就可以调用的方法叫做静态方法。

    function A(name) {
        //私有属性
        var num = 0;
        //公有属性
        this.name = name;
        //私有方法
        function fn() {
    
        }
    }
    //静态属性
    A.index = 1;
    //静态方法
    A.say = function () {
        console.log("hello world")
    }
  • 特权方法

    function A(name) {
        //私有属性
        var num = 0;
        //公有属性
        this.name = name;
        //特权方法
        this.getName = function () {
            return this.name;
        }
    }

2 new操作符

new操作符做了什么

  1. 创建一个空对象obj{}
  2. obj[__proto__]属性指向构造函数的原型(即obj.[[__proto__]] = constrc.prototype
  3. 将构造函数内部的this绑定到新建的对象obj,执行构造函数(也就是跟调用普通函数一样,只是此时函数的this为新创建的对象obj而已)
  4. 若构造函数没有返回非原始值(即不是引用类型的值),则返回该新建的对象obj(默认会添加return this)。否则,返回引用类型的值。
//构造函数A
function A(name) {
    this.name = name;
    // 如果构造函数的返回值是值类型,那么就丢弃掉,依然返回构造函数创建的实例。
    // 如果构造函数的返回值是引用类型,那么就返回这个引用类型,丢弃构造函数创建的实例。
    return {
        "sex": "18"
    };
}
//构造函数的原型对象
A.prototype.do = function () {
    console.log(1);
}

//手写new
function writeNew(constr) {
    //创建一个对象
    var o = {};
    //把构造函数的this指向当前的对象o  并且向调用构造函数并传入参数 返回值就是A函数的返回值
    var result = constr.apply(o, Array.prototype.slice.call(arguments, 1));
    //把自定义对象的隐式原型指向构造函数的显式原型
    o.__proto__ = constr.prototype;
    //判断构造函数的返回值是基础值 还是对象
    var isObj = typeof result === 'object' && typeof result != null;
    var isFun = typeof result === 'function';
    //根据构造函数的返回值 设置new的返回值
    return (isObj || isFun) ? result : o;
}

var a1 = new A("lily");
var a2 = writeNew(A, "laowang");
console.log(a1)
console.log(a2)
console.log(a1 instanceof A)
console.log(a2 instanceof A)
console.log(a1.do)
console.log(a2.do)
console.log(a2.do === a1.do)

3 封装、继承、多态

3.1 封装

封装的目的是将信息隐藏,一般来说封装包括封装数据、封装实现,接下来就逐一来看:

3.1.1 封装数据

  • 由于JS的变量定义没有private、protected、public等关键字来提供权限访问,因此只能依赖作用域来实现封装特性

3.1.2 封装实现

  • 封装实现即隐藏实现细节、设计细节,封装使得对象内部的变化对其他对象而言是不可见的,对象对它自己的行为负责,其他对象或者用户都不关心它的内部实现,使用者只需要知道如何使用即可.封装使得对象之间的耦合变松散,对象之间只通过暴露的API接口来通信。

  • 封装实现最常见的就是jQuery、Zepto、Lodash这类JS封装库中,用户在使用的时候并不关心其内部实现,只要它们提供了正确的功能即可

3.2 继承

继承是子类对象能够使用父类对象的数据和行为。

  • 原型链继承

    套路

    1. 定义父类型构造函数
    2. 给父类型的原型添加方法
    3. 定义子类型的构造函数
    4. 创建父类型的对象赋值给子类型的原型
    5. 将子类型原型的构造属性设置为子类型
    6. 给子类型原型添加方法
    7. 创建子类型的对象: 可以调用父类型的方法

    关键

    1. 子类型的原型为父类型的一个实例对象
    function Supper() { //父类型
        this.superProp = 'The super prop'
    }
    //原型的数据所有的实例对象都可见
    Supper.prototype.showSupperProp = function () {
        console.log(this.superProp)
    }
    
    function Sub() { //子类型
        this.subProp = 'The sub prop'
    }
    
    // 子类的原型为父类的实例
    Sub.prototype = new Supper()
    // 修正Sub.prototype.constructor为Sub本身
    Sub.prototype.constructor = Sub
    
    Sub.prototype.showSubProp = function () {
        console.log(this.subProp)
    }
    
    // 创建子类型的实例
    var sub = new Sub()
    // 调用父类型的方法
    sub.showSubProp()
    // 调用子类型的方法
    sub.showSupperProp()
  • 借用构造函数继承

    套路:

    1. 定义父类型构造函数
    2. 定义子类型构造函数
    3. 在子类型构造函数中调用父类型构造

    关键:

    1. 在子类型构造函数中通用super()调用父类型构造函数
    function Person(name, age) {
        this.name = name
        this.age = age
      }
    
      function Student(name, age, price) {
        Person.call(this, name, age)   // this.Person(name, age)
        this.price = price
      }
    
      var s = new Student('Tom', 20, 12000)
      console.log(s.name, s.age, s.price)
  • 原型链+借用构造函数的组合继承

    利用原型链实现对父类型对象的方法继承

    利用super()借用父类型构建函数初始化相同属性

    function Person(name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.setName = function (name) {
        this.name = name
    }
    
    function Student(name, age, price) {
        Person.call(this, name, age) //得到父类型的属性
        this.price = price
    }
    Student.prototype = new Person()  //得到父类型的方法
    Student.prototype.constructor = Student
    Student.prototype.setPrice = function (price) {
        this.price = price
    }
    
    var s = new Student('Tom', 12, 10000)
    s.setPrice(11000)
    s.setName('Bob')
    console.log(s)
    console.log(s.constructor)

3.3 多态

JS对象多态性是与生俱来的:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果,也就是说,给不同的对象发送同一个消息时,这些对象会根据这个消息分别给出不同的反馈。

function Cat() {

}
Cat.prototype.eat = function () {
    console.log("吃猫粮");
}

function Dog() {

}
Dog.prototype.eat = function () {
    console.log("吃狗粮");
}

//发出同一个指令 可以执行不同的结果
function sayEat(obj) {
    obj.eat();
}
sayEat(new Cat)
sayEat(new Dog)
posted @ 2023-01-13 22:29  z_bky  阅读(29)  评论(0)    收藏  举报