前端学习笔记(九)-面向对象的程序设计

理解对象

  • 内部属性特征:数据属性特征和访问器属性特征
  • 数据属性特征
    • [[Configurable]]:能否通过delete删除属性;能够修改属性的特征;能够把数据属性特征修改为访问器属性特征。默认为true
    • [[Enumerable]]:能否通过for-in循环返回属性。默认为true
    • [[Writable]]:能否修改属性的值。默认为true
    • [[Value]]:这个属性的数据值。默认为undefined
  • 访问器属性特征
    • [[Configurable]]:能否通过delete删除属性;能够修改属性的特征;能够把访问器属性特征修改为数据属性特征。默认为true
    • [[Get]]:读取属性时的行为。默认为undefined -[[Set]]:写入属性时的行为。默认为undefined
  • 修改或定义属性的特征:Object.defineProperty()
    var person={};
    Object.defineProperty(person,"name",{
        writable:false,
        value:"Chris"
    });
复制代码
    var book={
        _year:2014,
        edition:1
    };
    Object.defineProperty(book,"year",{
        get:function(){
            return this._year;
        },
        set:function(newValue){
            if(newValue>2014){
                this._year=newValue;
                this.edition+=newValue-2014;
            }
        }
    })
复制代码
  • 只能通过对象方法访问的属性:前置下划线记号。
  • 定义多个属性:Object.defineProperties()
  • Object.getOwnPropertyDescriptor()

创建对象

  • 使用Object构造函数或对象字面量:使用同一个接口创建大量对象会产生许多重复的代码
  • 使用工厂模式
    • 用函数来封装以特定接口创建对象的细节
    • 创建新对象
    • 将属性和方法赋予新对象
    • 使用return返回新对象
    • 无法解决对象识别的问题
      • 对象都是object
        function createPerson(name,age,job){
            //创建新对象Object
            var o=new Object();
            //对象属性
            o.name=name;
            o.age=age;
            o.job=job;
            //对象方法
            o.sayName()=function(){
                alert(this.name);
            }
            
        }
    复制代码
  • 构造函数模式
    • 创建自定义构造函数意味着能够将它的实例标识为一种新的类型
    • 每一个通过构造函数创建的实例,同名函数实际上是不相等的
    function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
        this.sayName=function(){
            alert(this.name);
        }
    }
    var o = new Object();
    Person.call(o,"Kris',25,"Doctor");
    Person.sayName();//"Kris"
复制代码
  • 原型模式
    • 通过调用构造函数创建实例对象的原型对象
    • 所有实例对象共享原型对象的属性和方法
    • 任何函数都有prototype属性,这个属性指向函数的原型对象
    • 原型对象会有一个constructor属性,这个属性指向拥有该prototype属性的函数
    • 对象的实例拥有属性[[Prototype]],指向原型对象
    • 所有实现都无法访问[[Prototype]],但是可以通过isPrototypeOf()方法来确定对象之间的关系。如果实例内部存在指向prototype的指针,那么isPrototypeOf()返回true。Person.prototype.isPrototypeOf(person1)(确定实例与原型之间的关系)
    • 通过Object.getPrototypeOf()可以方便地取得一个对象的原型
    • 不能通过实例对象重写原型中的值,实例对象与原型对象重名的属性会屏蔽掉原型对象中的属性
    • hasOwnProperty()判断属性是否是实例中的,当给定属性存在于对象实例中时才返回true
    • in操作符,单独使用时,无论属性存在于实例还是原型中时都返回true。只有当属性在实例和原型中都搜索不到时才会返回false。
    • for-in循环会返回的是能通过对象访问的,可枚举的属性。屏蔽了原型中不可枚举的实例属性也会在for-in循环中返回。
    • Object.keys()返回所有可枚举属性的字符串
    • Object.getOwnPropertyNames()返回所有属性
        function Person(){
            Person.prototype.name="Kris";
            Person.prototype.age=25;
            Person.prototype.job="Doctor";
            Person.prototype.sayName=function(){
                alert(this.name);
            }
        }
    
    复制代码
    • 原型模式简化语法
        function Person(){
        }
        Person.prototype={
            name:"Kris",
            age:29,
            job:"Doctor",
            sayName:function(){
                alert(this.name);
            }
        }
        //上面的操作改变了Person Prototype中constructor的指向(指向Object)。
        Object.defineProperty(Person.prototype,"constructor",{
            enumerable:false,//默认情况下,constructor是不可枚举的
            value:Person//简化语法会导致constructor指向Object
        })
    复制代码
    • 原型模式的问题,省略了通过构造函数传递初始化参数这一环节。并且对于引用类型的属性,共享成为了问题。
  • 组合使用构造函数模式和原型模式
    • 定义引用类型的一种默认模式
        function Person(name,age,job){
            this.name=name;
            this.age=age;
            this.job=job;
            this.friend=["Bob","Kevin"]//使用构造函数定义实例属性
        }
        Person.prototype={
            constructor:Person,
            sayName:function(){
                alert(this.name);
            }//使用原型模式定义方法以及需要共享的属性
        }
    复制代码
  • 动态原型模式
    • 通过在构造函数中初始化原型
        function Person(name,age,job){
            this.name=name;
            this.age=age;
            this.job=job;
            //初始化原型方法
            if(typeof this.sayName!="function"){
                Person.prototype.sayName=function(){
                    alert(this.name);
                };
            }
        }
    复制代码
  • 寄生构造函数模式
  • 稳妥构造函数模式

继承

  • js只支持实现继承,即extends。依靠原型链实现。
  • 让原型对象等于另一个类型的实例。关键代码SubType.prototype=new SuperType()
    • SubType的原型指向SuperType的原型,SuperType原型的constructor指向SuperType。所以所有SubType实例的constructor都指向SuperType
  • 确定原型与实例的关系
    • instanceof instance instanceof Object
    • isPrototypeOf Object.prototype.isPrototypeOf(instance)
  • 子类重写父类的方法或者子类添加父类中不存在的方法,这些语句需要放在继承语句(即替换原型的语句之后)。并且不能使用对象字面量创建原型方法
  • 原型链继承的问题
    • 创建子类实例不支持传递参数
    • 原型中的引用类型
  • 借用构造函数
        function SubType(){
            //支持传递参数
            SuperType.call(this,"Kris");
            //添加子类属性
            this.age=29;
        }
    复制代码
    • 不支持方法复用
  • 组合继承
    • 使用原型链完成方法继承
    • 使用借用构造函数完成属性和引用类型的继承
        function SubType(name,age){
            //继承属性
            SuperType.call(this,name);
            
            this.age=29;
        }
        SubType.prototype=new SuperType();
        SubType.prototype.constructor=SubType;
        SubType.prototype.sayName()=function(){
            alert(this.name);
        }
    复制代码
  • 原型式继承:Object.create()
    • 本质上是对传入参数对象进行一次浅复制,因此引用类型属性全实例共享
    • 接受参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性
    • 返回值:一个新对象,带着指定的原型对象和属性。
    o = Object.create(Object.prototype, {
      // foo会成为所创建对象的数据属性
        foo: { 
            writable:true,
            configurable:true,
            value: "hello" 
        },
      // bar会成为所创建对象的访问器属性
        bar: {
            configurable: false,
            get: function() { return 10 },
            set: function(value) {
            console.log("Setting `o.bar` to", value);
            }
        }
    });
    复制代码
  • 寄生式继承
    • 对父对象进行一次浅复制
    • 在封装函数中对复制后的对象进行方法增强
    • 无法做到函数复用
        function createAnother(original){
            var clone=Object.create(original);
            clone.sayHi=function(){
                alert("hi");
            };
            return clone;
        }
    复制代码
  • 寄生组合式继承
    • 原有组合继承,无论什么情况下都会调用两次超类的构造函数
    • 使用构造函数来继承属性,使用原型链的混成形式来继承方法
        function inheritPrototype(subType,superType){
            //获取父类的原型副本
            var prototype=Object.create(superType.prototype);
            //为创建的副本添加constructor
            prototype.constructor=subType;
            //将新创建的原型副本指定为子类的原型
            subType.prototype=prototype;
        }
        function SubType(name,age){
            SuperType.call(this,name);
            this.age=age;
        }
        inheritPrototype(SubType,SuperType);
        
    复制代码
    • 只调用一次父类的构造函数,避免在子类的原型上创建多余的属性。是引用类型最理想的继承范式

转载于:https://juejin.im/post/5d08db3be51d45109725fe89

猜你喜欢

转载自blog.csdn.net/weixin_33691700/article/details/93181536