美文网首页
面向对象

面向对象

作者: 他在发呆 | 来源:发表于2017-08-06 22:36 被阅读0次

    一、属性类型

    • 数据属性

    • 访问器属性

    1、数据属性
    描述行为: 通过Object.defineProperty()
    Configurable 可删
    Enumerable 可遍历
    Writable 可写
    Value
    2、访问器属性
    描述行为: 通过Object.defineProperty()
    Configurable 可删
    Enumerable 可遍历
    Get 读 默认值是undefined
    Set 写 默认值是undefined

    例: 定义单个访问器属性

            var book = {
                _year: 2004,
                edition: 1
            }
            Object.defineProperty(book, 'year', {
                get: () => {
                    return this._year;
                },
                set: () => {
                    // 操作
                }
            })
    

    定义多个属性

            var book = {};
            Object.defineProperties(book, {
                _year: { // 数据属性
                    configurable: false,
                    enumerable: true,
                    value: 2017
                },
                edition: { // 数据属性
                    value: 1
                },
                _year: { // 访问器属性
                    get: () => {
                        return this._year;
                    },
                    set: () => {
                        // 操作
                    }
                }
            })
    

    二、创建对象

    1. 工厂模式
    2. 构造函数
    3. 原型模式
    4. 组合使用构造函数和原型模式
    5. 动态原型模式
    6. 寄生构造函数模式
    7. 稳妥构造函数模式
    8. 单例模式 (只有一个实例对象)
    1.工厂模式
            function createPerson (name, age) {
                var obj = new Object();
                obj.name = name;
                obj.age = age;
                return obj;
            }
    
            var person1 = createPerson('mark', 29);
            var person2 = createPerson('lili', 25);
    
    工厂模式

    优点: 解决了创建多个相似对象的问题
    缺点: 未解决对象识别问题(即对象类型)

    2.构造函数
            function Person (name, age) {
                this.name = name;
                this.age = age;
                this.sayName = () => {
                    alert(this.name)
                }
            }
            var person2 = new Person('mark', 25);
    
    • 和工厂模式区别:
      未显式地创建对象;
      直接将属性方法赋给this;
      无return;
      大写开头;

    • 调用步骤

    1. 创建一个新对象;
    2. 将构造函数作用域赋给this;
    3. 执行构造函数中的代码;
    4. 返回新对象。

    新创建的person2存着一个实例,
    有一个constructor(构造器)属性,指向Person.
    用来检测对象类型

    检测对象类型

    1>. person2.constructor === Person // true
    2>. person2 instanceof Person // true 常用
        person2 instanceof Object // true
    

    构造函数优缺点
    优: 将来可以将他的实例标识为一种特殊类型。
    缺: 每个方法都要在实例上重新创建一遍。

    解决缺点的一个方法:

            function Person (name, age) {
                this.name = name;
                this.age = age;
                this.sayName = sayName;
            }
            function sayName () { // 把方法写全局
                alert(this.name)
            }
            var person2 = new Person('mark', 25);
    

    引申:call方法

    var o = new Object();
    Person.call(o, 'marry', 29);
    o.sayName() // marry
    
    3.原型模式

    通过prototype

    不必在构造函数中定义对象实例的信息,而是直接将这些信息添加到原型对象中

    function Person () {}
    Person.prototype.name = 'mark';
    Person.prototype.age = 25;
    Person.prototype.sayName = function () {
        alert(this.name)
    };
    var person1 = new Person();
    var person2 = new Person();
    person1.sayName(); // 'mark'
    

    与构造函数的区别:新对象的这些属性和方法是由所有实例共享的
    Person的每个实例person1,person2都包含一个内部属性(proto),该属性仅仅指向了Person.prototype;即与构造函数没有直接关系

    person1.__proto__ === Person.prototype // true
    

    若判断有没有这层关系,可以通过isPrototypeOf()来确定;

    Person.prototype.isPrototypeOf(person1) // true
    
    新增Object.getPrototypeOf()
    Object.getPrototypeOf(person1) === Person.prototype
    
    4.组合使用构造函数和原型模式

    常用来定义引用类型

            function Person (name, age) {
                this.name = name;
                this.age = age;
            }
            Person.prototype = {
                constructor: Person,
                sayName: function () {
                // 这里不能使用箭头函数,否则this始终指向window对象
                    alert(this.name)
                }
            }
            var person1 = new Person('mark', 25);
            person1.sayName()
    
    5.动态原型模式

    把所有信息封装在构造函数中,在构造函数中初始化原型

            function Person (name, age) {
                this.name = name;
                this.age = age;
                if (typeof this.sayName != 'function') {
                    Person.prototype.sayName = () => {
                        alert(this.name)
                    }
                }
            }
            var person1 = new Person('mark', 29);
            person1.sayName()
    
    6.单例模式
            var singleton = function () {
                var privateVariable = 10; // 私有变量
                function pricateFunction () { // 私有函数
                    return false;
                }
                return { // 特权 共有
                    publicProperty: true,
                    publicMethod: function () {
                        privateVariable++;
                        return pricateFunction();
                    }
                }
            }
    
    

    三、继承

    1. 通过原型链
            function SuperType () {
                this.property = true;
            }
            SuperType.prototype.getSuperValue = () => {
                return this.property;
            }
            function SubType () {
                this.subproperty = false
            }
            SubType.prototype = new SuperType();
            SubType.prototype.getSubValue = function () {
                return this.subproperty;
            }
            var instance = new SubType();
            instance.getSubValue();
            instance.getSubValue(); // false
    

    在通过原型链实现继承时,不能使用对象字面量创建原型方法,
    否则会重写原型链

            SubType.prototype = {
                getSubValue: function () {
                    return this.subproperty;
                }
            }
    

    通过原型链实现继承的缺点:

    1. 包含引用类型值的原型(会被所有实例共享);
    2. 创建子类实例时,无法向超类构造函数传参,
      (即/:不能在不影响所有对象的实例下给超类传参)

    为了解决这样的缺点,就出现了借用构造函数

    2.借用构造函数
    (在子类构造函数内部调用超类构造函数)

            function SuperType () {
                this.colors = ['red', 'blue'];
            }
            function SubType () {
                SuperType.call(this) // 调用超类型
            }
            var instance1 = new SubType();
            instance1.colors.push('black');
            console.log(instance1.colors);
            var instance2 = new SubType();
            console.log(instance2.colors);
    
    借用构造函数

    传参:

            function SuperType (name) {
                this.name = name;
            }
            function SubType () {
                SuperType.call(this, 'mark');
                this.age = 29;
            }
            var instance = new SubType();
            instance.name // 'mark'
            instance.age // 29
    

    缺点:无法避免构造函数模式存在的问题, 不能复用。

    3.组合继承 (伪经典继承)
    原型链(原型属性和方法) + 构造函数(实例属性)

            function SuperType (name) {
                this.name = name;
                this.colors = ['red', 'blue'];
            }
            SuperType.prototype.sayName = function () {
                alert(this.name)
            }
            function SubType (name, age) {
                SuperType.call(this, name); // (2)
                this.age = age;
            }
            SubType.prototype = new SuperType();  // (1)
            SubType.prototype.constructor = SubType;
            SubType.prototype.sayAge = function () {
                alert(this.age);
            }
            var instance1 = new SubType('mark', 25);
            instance1.colors.push('black');
            instance1.colors // 'red', 'blue', 'black'
            var instance2 = new SubType('marry', 29);
            instance2.colors // 'red', 'blue'
    

    最常用的继承模式,结合了两者的有点
    缺点:无论什么情况下,都会调用两次超类型构造函数
    一次是创建子类时,即 (1)
    一次是子类内部,即 (2)

    4.原型式继承

            function obj (o) {
                function F () {};
                F.prototype = o;
                return new F();
            }
    

    先创建一个临时构造函数,并将传入的对象作为这个构造函数的原型。
    使用场景: 在不必要使用构造函数,只想让一个对象与另一个对象保持类似。
    包含引用类型的属性都会共享相应的值

    5.寄生式继承 (不能复用)

            function createAnother (original) {
                var clone = object(original);
                clone.sayHi = function () {
                    alert('hi')
                }
                return clone;
            }
    

    创建一个仅用于封装继承过程的函数。
    适用于任何能返回新对象的函数

    6.寄生组合式继承
    解决组合式继承调用两次超类型的缺陷
    用寄生式继承超类的原型,将结果指定给子类型的原型。

            function inheritPrototype (subType, superType) {
                var prototype = Object(subType.prototype); // 创建超类副本
                prototype.constructor = subType;
                // 为创建副本加constructor(弥补因重写原型失去了默认的constructor)
                subType.prototype = prototype;
                // 将创建的对象赋给子类的原型
            }
    
            function SuperType (name) {
                this.name = name;
                this.colors = ['red', 'blue'];
            }
            SuperType.prototype.sayName = function () {
                alert(this.name);
            }
            function SubType (name, age) {
                SuperType.call(this.name);
                this.age = age;
            }
            inheritPrototype(SuperType, SubType);
            SubType.prototype.sayAge = function () {
                alert(this.age)
            }
    

    总结

    工厂模式: 简单的函数创建对象,为对象添加属性和方法,返回对象。
    构造函数:创建自定义引用类型,无法复用。
    原型模式:prototype属性。

    js主要通过原型链实现继承
    (通过一个类型的实例赋值给另一个构造函数的原型) 不适宜单独引用
    解决方法: 借用构造函数
    让每个实例都有自己的属性
    组合继承使用最多。

    四。函数表达式

    argument.callee指向一个正在执行的函数的指针
    递归时不直接使用函数名

    递归

            function factorial (num) {
                if (num <= 1) {
                    return 1;
                } else {
                    return num * arguments.callee(num - 1)
                }
            }
    

    严格模式下不能通过脚本使用。

    闭包

    指有权访问另一个函数作用域中变量的函数

    缺点: 会定义一个全局变量,只能取得包含函数中任何变量的最后一个值

    相关文章

      网友评论

          本文标题:面向对象

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