工厂模式

工厂模式解决了多个对象相似的问题,没有解决对象识别的问题。

    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("Nicholas", 29, "Software Engineer");

通过new操作符调用构造函数,实际经历以下四个步骤
1. 创建一个新对象
2. 将构造函数作用域赋给新对象
3. 执行构造函数中的代码
4. 返回新对象

实例对象都有一个constructor属性,该属性指向person。通过构造函数标识实例对象的特定类型。

    //true
    alert(person1.constructor === Person);

    //false
    alert(person1.constructor === Object);

构造函数模式的问题
同样的方法在每个实例对象中都创建了一遍。

原型模式

    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

每个函数都有一个prototype属性,这属性是一个指针,指向一个对象,这个对象是原型对象。原型对象内包含属性和方法,可以让实例对象共享这些属性和方法。
所有原型对象默认自动获得一个constructor属性指向prototype属性所在的函数。每一个用构造函数创建的实例,会包含一个指向构造函数的原型对象的指针。
Object.isPrototypeof()
Object.getPrototypeof()


当为实例对象添加属性时,这个属性会屏蔽原型对象中保存的同名属性。
使用delete操作符可以完全删除实例属性(delete 操作符会从某个对象上移除指定属性。成功删除的时候回返回 true,否则返回 false。)

obj.hasOwnProperty(prop)检测一个属性在实例中还是原型中

in操作符
两种使用方式:1 单独使用 2 for-in循环中使用
单独使用检测一个属性是否存在于对象或原型中,通过对象能访问的属性返回true。

    alert(person1.hasOwnProperty("name"));//false
    alert("name" in person1);//true

    person1.name = "Greg";
    alert(person1.name);//Greg

    delete person1.name;
    alert(person1.hasOwnProperty("name"));//true
    alert("name" in person1);//true
    alert(person1.name);//Nicholas

for-in循环,返回所有能通过对象访问的,可枚举的属性。(属性特性[[Emunerable]为false的被实例覆盖的属性也是可枚举的])


Object.keys(),接收一个对象,返回一个包含所有可枚举属性的数组。

    person1.name = "Greg";
    person1.xixi = "xixi";
    52                                                                                                                                                                                                                                                                            
    var keys = Object.keys(Person.prototype);
    console.log(keys);// Array(4) [ "name", "age", "job", "sayName" ]

    //传入实例对象,只返回实例对象的属性的数组
    var keys = Object.keys(person1);
    console.log(keys);// Array [ "name", "xixi" ] [ "name" ]

更简单的原型语法

    function Person() { }

    Person.prototype = {
        constructor: Person,
        name: "xixi"
    }

    var keys = Object.keys(Person.prototype);

    //原生constructor属性是不可枚举的,这里重设constructor导致[[Enumerable]]被设为true
    console.log(keys);// Array [ "constructor", "name" ]

    //通过Object.defineProperty把[[Enumerable]]属性设回为false
    Object.defineProperty(Person.prototype, "constructor", {
        enumerable: false,
        value: Person
    });

    var keys = Object.keys(Person.prototype);
    console.log(keys);// Array [ "name" ]

原型的动态性

    function Person() { }

    var p1 = new Person();

    //重写prototype,Person.prototype指向新的原型对象
    Person.prototype = {
        constructor: Person,
        age: 18
    };

    //p1实例在重写原型对象前创建,p1的prototype属性指向原来的Person的原型对象,原来的原型对象中不包含age属性
    console.log(p1.age);//undefined

    var p2 = new Person();
    console.log(p2.age);//18

原型对象的问题

    function Person() { }

    Person.prototype = {
        constructor: Person,
        age: 18,
        friends: ["f1", "f2", "f3"]
    }

    var p1 = new Person();
    var p2 = new Person();

    p1.friends.push('f4');

    //原型中的属性都是被共享的,friends属性为引用类型,对引用类型的操作会反映到所有实例,这不是所希望的
    console.log(p1.friends);//Array(4) [ "f1", "f2", "f3", "f4" ]

    //
    console.log(p2.friends);//Array(4) [ "f1", "f2", "f3", "f4" ]

组合使用构造模式和原型模式

构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性

    function Person(name, age, friends) {
        this.name = name;
        this.age = age;
        this.friends = friends;
     }

    Person.prototype = {
        constructor: Person,
        sayName: function () {
            console.log(this.name);
        }
    };

    var p1 = new Person("Jack", 18, ["f1", "f2", "f3"]);
    var p2 = new Person("Tom", 30, ["f1", "f2", "f3"]);

    p1.friends.push("p4");

    p1.sayName();//Jack
    p2.sayName();//Tom
    console.log(p1.friends);//Array(4) [ "f1", "f2", "f3", "p4" ]
    console.log(p2.friends);//Array(3) [ "f1", "f2", "f3" ]

动态原型模式
把所有信息封装在构造函数中

    function Person(name, age, job) {
        //属性
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["f1", "f2"];

        //方法
        if (typeof this.sayName != "function") {//避免每次调用构造函数时给prototype重复添加属性,只在第一次调用时执行,本身prototype的属性共享于所有实例,不需要每次都添加
            Person.prototype.sayName = function () {
                alert(this.name);
            }
            Person.prototype.sayHello = function () {
                alert("Hello");
            }
        }
    }

    var person = new Person("Jack", 29, "student");
    person.sayName();