美文网首页面向对象
JavaScript面向对象《JavaScript高级编程》笔记

JavaScript面向对象《JavaScript高级编程》笔记

作者: 进击的前端 | 来源:发表于2016-08-24 14:12 被阅读137次

    之前写过 原型链,写过new,写过create
    现在发现都是面向对象的知识点。

    理解对象

    无序属性的集合,其属性可以包含基本值、对象或者函数

    对象的属性

    属性分成访问属性和数据属性

    Object.defineProperty

    这篇文章讲得比较详细Object.defineProperty
    定义多个属性,Object.defineProperties()

    var book = {};
    Object.defineProperties(book, {
        _year: {
            value: 2004
        },
        edition: {
            value: 1
        },
        year: {
            get: function(){
                return this._year;
            },
            set: function(newValue){
                if (newValue > 2004) {
                    this._year = newValue;
                    this.edition += newValue - 2004;
                }
            }
        }
    });
    

    这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有configurable、enumerable、get 和set;如果是数据属性,这个对象的属性有configurable、enumerable、writable 和value。

    Object.getOwnPropertyDescriptor

    访问多个属性

    创建对象

    工厂模式

    function createPerson(name, age, job){
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.sayName = function(){
            alert(this.name);
        };
        return o;
    }
    var person1 = createPerson("Nicholas", 29, "Software Engineer");
    var person2 = createPerson("Greg", 27, "Doctor");
    

    构造器模式

    function Person(name, age, job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function(){
            alert(this.name);
        };
    }
    var person1 = new Person("Nicholas", 29, "Software Engineer");
    var person2 = new Person("Greg", 27, "Doctor");
    
    alert(person1.constructor == Person); //true
    alert(person2.constructor == Person); //true
    
    alert(person1 instanceof Object); //true
    alert(person1 instanceof Person); //true
    alert(person2 instanceof Object); //true
    alert(person2 instanceof Person); //true
    

    曾经总结过 new,要创建Person 的新实例,必须使用new 操作符。以这种方式调用构造函数实际上会经历以下4
    个步骤:
    (1) 创建一个新对象;
    (2) 将构造函数的作用域赋给新对象(因此this 就指向了这个新对象);
    (3) 执行构造函数中的代码(为这个新对象添加属性);
    (4) 返回新对象。

    将构造函数当作函数

    // 当作构造函数使用
    var person = new Person("Nicholas", 29, "Software Engineer");
    person.sayName(); //"Nicholas"
    // 作为普通函数调用
    Person("Greg", 27, "Doctor"); // 添加到window
    window.sayName(); //"Greg"
    // 在另一个对象的作用域中调用
    var o = new Object();
    Person.call(o, "Kristen", 25, "Nurse");
    o.sayName(); //"Kristen";
    

    构造函数的问题

    alert(person1.sayName == person2.sayName); //false

    原型模式

    关于原型链
    无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype 属性所在函数的指针。就拿前面的例子来说,Person.prototype. constructor 指向Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。

    function Person(){
    }
    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "Software Engineer";
    Person.prototype.sayName = function(){
        alert(this.name);
    };
    var person1 = new Person();
    person1.sayName(); //"Nicholas"
    var person2 = new Person();
    person2.sayName(); //"Nicholas"
    alert(person1.sayName == person2.sayName); //true
    
    alert(Person.prototype.isPrototypeOf(person1)); //true
    alert(Person.prototype.isPrototypeOf(person2)); //true
    
    //es5
    alert(Object.getPrototypeOf(person1) == Person.prototype); //true
    alert(Object.getPrototypeOf(person1).name); //"Nicholas"
    
    alert(person1.hasOwnProperty("name")); //false
    
    
    

    关于in
    由于in 操作符只要通过对象能够访问到属性就返回true,hasOwnProperty()只在属性存在于实例中时才返回true,因此只要in 操作符返回true 而hasOwnProperty()返回false,就可以确定属性是原型中的属性

    person1.name = "Greg";
    alert("name" in person1); //true
    alert("name" in person2); //true
    

    for in

    bject.keys()和Object.getOwnProperty-
    Names()方法都可以用来替代for-in 循环

    function Person(){
    }
    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "Software Engineer";
    Person.prototype.sayName = function(){
    alert(this.name);
    };
    var keys = Object.keys(Person.prototype);
    alert(keys); //"name,age,job,sayName"
    var p1 = new Person();
    p1.name = "Rob";
    p1.age = 31;
    var p1keys = Object.keys(p1);
    alert(p1keys); //"name,age"
    
    var keys = Object.getOwnPropertyNames(Person.prototype);
    alert(keys); //"constructor,name,age,job,sayName"
    
    。Object.keys()和Object.getOwnProperty-
    Names()方法都可以用来替代for-in 循环。
    

    如果重新定义prototype,那么constructor是默认不指向构造函数的,需要自己指回来。

    function Person(){
    }
    Person.prototype = {
        constructor : Person,
        name : "Nicholas",
        age : 29,
        job: "Software Engineer",
        sayName : function () {
            alert(this.name);
        }
    };
    

    如果不想被枚举,

    //重设构造函数,只适用于ECMAScript 5 兼容的浏览器
    Object.defineProperty(Person.prototype, "constructor", {
      enumerable: false,
      value: Person
    });
    

    构造模式和原型模式组合(经典)

    function Person(name, age, job){
      this.name = name;
      this.age = age;
      this.job = job;
      this.friends = ["Shelby", "Court"];
    }
    Person.prototype = {
      constructor : Person,
      sayName : function(){
        alert(this.name);
      }
    }
    var person1 = new Person("Nicholas", 29, "Software Engineer");
    var person2 = new Person("Greg", 27, "Doctor");
    person1.friends.push("Van");
    alert(person1.friends); //"Shelby,Count,Van"
    alert(person2.friends); //"Shelby,Count"
    alert(person1.friends === person2.friends); //false
    alert(person1.sayName === person2.sayName); //true
    

    动态原型模式

    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);
            };
        }
    }
    

    寄生构造函数模式

    function Person(name, age, job){
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.sayName = function(){
            alert(this.name);
        };
        return o;
    }
    var friend = new Person("Nicholas", 29, "Software Engineer");
    friend.sayName(); //"Nicholas"
    

    稳妥构造函数模式

    function Person(name, age, job){
      //创建要返回的对象
      var o = new Object();
        //可以在这里定义私有变量和函数
        //添加方法
        o.sayName = function(){
            alert(name);
        };
        //返回对象
        return o;
    }
    

    继承

    原型链继承

    function SuperType(){
        this.property = true;
    }
    SuperType.prototype.getSuperValue = function(){
        return this.property;
    };
    function SubType(){
        this.subproperty = false;
    }
    //继承了SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function (){
        return this.subproperty;
    };
    var instance = new SubType();
    alert(instance.getSuperValue()); //true
    

    原型继承的问题:所有的实例都共享一个属性。

    借用构造函数

    function SuperType(){
        this.colors = ["red", "blue", "green"];
    }
    function SubType(){
    //继承了SuperType
        SuperType.call(this);
    }
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors); //"red,blue,green,black"
    var instance2 = new SubType();
    alert(instance2.colors); //"red,blue,green"
    

    通过使用call()方法(或apply()方法也可以),我们实际上是在(未来将要)新创建的SubType 实例的环境下调用了SuperType 构造函数。
    这样一来,就会在新SubType 对象上执行SuperType()函数中定义的所有对象初始化代码。结果,SubType 的每个实例就都会具有自己的colors 属性的副本了。
    缺点,比如这个例子colors有两个,一个在SubType的原型中,一个在实例对象中。

    传递参数

    function SuperType(name){
        this.name = name;
    }
    function SubType(){
    //继承了SuperType,同时还传递了参数
        SuperType.call(this, "Nicholas");
    //实例属性
        this.age = 29;
    }
    var instance = new SubType();
    alert(instance.name); //"Nicholas";
    alert(instance.age); //29
    

    组合继承

    function SuperType(name){
        this.name = name;
        this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    function SubType(name, age){
    //继承属性
        SuperType.call(this, name);
        this.age = age;
    }
    //继承方法
    SubType.prototype = new SuperType();
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
    var instance1 = new SubType("Nicholas", 29);
    instance1.colors.push("black");
    alert(instance1.colors); //"red,blue,green,black"
    instance1.sayName(); //"Nicholas";
    instance1.sayAge(); //29
    var instance2 = new SubType("Greg", 27);
    alert(instance2.colors); //"red,blue,green"
    instance2.sayName(); //"Greg";
    instance2.sayAge(); //27
    

    原型式继承

    var person = {
        name: "Nicholas",
        friends: ["Shelby", "Court", "Van"]
    };
    var anotherPerson = Object.create(person);
    anotherPerson.name = "Greg";
    anotherPerson.friends.push("Rob");
    var yetAnotherPerson = Object.create(person);
    yetAnotherPerson.name = "Linda";
    yetAnotherPerson.friends.push("Barbie");
    alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
    
    // or
    var anotherPerson = Object.create(person, {
    name: {
    value: "Greg"
    }
    });
    alert(anotherPerson.name); //"Greg"
    

    其实就是Object.create(),这个函数的第二个参数 和defineProperty的第二个参数一样,是属性描述的格式

    寄生式继承

    同样是复制一个新的对象

    
    function createAnother(original){
        var clone = object(original); //通过调用函数创建一个新对象
        clone.sayHi = function(){ //以某种方式来增强这个对象
            alert("hi");
        };
        return clone; //返回这个对象
    }
    

    寄生组合式继承

    function SuperType(name){
        this.name = name;
        this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    function SubType(name, age){
        SuperType.call(this, name); //第二次调用SuperType()
        this.age = age;
    }
    SubType.prototype = new SuperType(); //第一次调用SuperType()
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
    

    这个代码是用来讲缺点的

    function inheritPrototype(subType, superType){
        var prototype = object(superType.prototype); //创建对象
        prototype.constructor = subType; //增强对象
        subType.prototype = prototype; //指定对象
    }
    
    function SuperType(name){
        this.name = name;
        this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    function SubType(name, age){
        SuperType.call(this, name);
        this.age = age;
    }
    inheritPrototype(SubType, SuperType);
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
    

    其实就是只复制原型吧

    关于作用域安全的构造模式的继承

    为了防止没有new 构造函数的时候,this指向全局的问题,先判断instanceof,但是这个在继承使用this的时候会有问题。

    function Polygon(sides){
        if(this instanceof Polygon) {
            this.sides = sides;
            this.getArea = function(){
                return 0;
            }
        }else {
            return new Polygon();
        }
    }
    
    function Rectangle(width,height){
        Polygon.call(this,2);
    }
    
    Rectangle.prototype = new Polygon();
    
    var rect = new Rectangle();
    ``

    相关文章

      网友评论

        本文标题:JavaScript面向对象《JavaScript高级编程》笔记

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