JS相关Web前端之路Web 前端开发

js创建对象的模式

2017-09-30  本文已影响19人  我不是大熊

Object构造函数或对象字面量都可以用来创建对象,但会产生大量重复代码,所以要选择恰当的模式进行快速地创建对象:

1.工厂模式

工厂模式抽象了创建具体对象的过程,简单说就是把重复的代码封装起来:

    function creatPerosn(name, age) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.sayName = function () {
            alert(this.name);
        }
        return o;
    }
    var person1 = creatPerson("名字1",20);
    var perosn2 = creatPerson("名字2",30);

缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)

2.构造函数模式
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayName = function () {
            alert(this.name);
        };
    }
    var person1 = new Person('名字1',20);
    var person2 = new Person('名字2',30);
    person1.sayName();
    person2.sayName();
2.1与工厂模式不同的地方:
2.2要创建Person实例,必须使用new操作符,调用构造函数经历以下步骤:
2.3构造函数的问题

每个方法都要在每个实例上重新创建一遍,如person1和person2的sayName方法根本不需要多次创建,浪费内存,因此引出了一个思路:怎样设置类的共享方法和共享属性。

3原型模式

构造函数的prototype属性是一个指针,指向一个对象,这个对象用途是包含可以由特定类型的所有实例共享的属性和方法。示例:

    function Person() {

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

在实现中无法访问到[[prototype]],但可以通过isPrototype()方法确定对象之间是否存在这种关系。

接上面
    alert(Person.prototype.isPrototypeOf(person1));//true
    alert(Person.prototype.isPrototypeOf(person2));//true
3.1原型和属性相关小点总结:
    function Person(age) {
        this.age = age;
    }
    Person.prototype.name = 'jaychou';
    var person1 = new Person(20);
    alert(person1.hasOwnProperty("name"));//存在于原型中,返回false
    alert(person1.hasOwnProperty("age"));//存在于实例中,返回true
单独使用in操作符:
    function Person(age) {
        this.age = age;
    }
    Person.prototype.name = 'jaychou';
    var person1 = new Person(20);
    alert(person1.hasOwnProperty("name"));//存在于原型中,返回false
    alert(person1.hasOwnProperty("age"));//存在于实例中,返回true
    alert("name" in person1);//存在于原型中,返回true
    alert("age" in person1);//存在于实例中,返回true

for-in循环的使用:
    function Person(age,height) {
        this.age = age;
        this.height = height;
    }
    Person.prototype.name = 'jaychou';
    var person1 = new Person(20,'180cm');
    Object.defineProperties(person1,{
        weight:{
            value:'75kg',
            configurable:true,
            enumerable:true,
            writable:true
        },
        work:{
            value:'software engineer',
            configurable:true,
            enumerable:false,
            writable:true
        }
    })
    for(var prop in person1){
        //依次弹出属性:age,height,weight,name,不会弹出work,因为work的enumerable为false
        alert(prop);
    }
    alert("work" in person1);//为true,因为work属性只是不能被枚举,不妨碍被访问
    function Person(age,height) {
        this.age = age;
        this.height = height;
    }
    Person.prototype.name = 'jaychou';
    Person.prototype.hobby = 'football';
    var person1 = new Person(20,'180cm');
    Object.defineProperties(person1,{
        weight:{
            value:'75kg',
            configurable:true,
            enumerable:true,
            writable:true
        },
        work:{
            value:'software engineer',
            configurable:true,
            enumerable:false,
            writable:true
        }
    })
    alert(Object.keys(person1));//返回age,height,weight,并不会返回name和hobby原型属性
    alert(Object.keys(Person.prototype));//返回name和hobby
接上面
    //返回age,height,weight,work,即使work不可枚举
    alert(Object.getOwnPropertyNames(person1));
    //返回name,hobby,constructor,即使constructor不可枚举
    alert(Object.getOwnPropertyNames(Person.prototype));
3.2更简单的原型写法

如果为原型添加多个属性和方法,为了更好的视觉效果,可以用一个包含所有属性和方法的对象字面量来重写整个原型对象,并且添加好不可枚举的constructor属性。但是注意的是,如果重写整个原型对象,那么就是切断了构造函数与最初原型之间的联系了。

    function Person() {
        
    }
    Person.prototype = {
        name:'jaychou',
        age:20,
        job:'software engineer',
        sayName:function () {
           alert(this.name); 
        }
    }
    Object.defineProperty(Person.prototype,"constructor",{
       // 让constructor不可枚举
       enumerable:false,
        value:Person
    });
3.3原型的动态性

如下面的例子,先是创建了实例friend,然后才添加了原型方法sayName,但调用的时候是正常的,因为会查找sayName。

    function Person() {
    }
    var friend = new Person();
    Person.prototype.sayName = function () {
        alert('kk');
    }
    friend.sayName();//正常调用

重写整个原型对象导致的错误示例:

    function Person() {

    }
    var friend = new Person();
    Person.prototype = {
        name:'jaychou',
        age:20,
        job:'software engineer',
        sayName:function () {
           alert(this.name);
        }
    }
    Object.defineProperty(Person.prototype,"constructor",{
       // 让constructor不可枚举
       enumerable:false,
        value:Person
    });
    friend.sayName();//调用错误,因为friend指向的是旧的原型对象而不是新的
3.4原生对象的原型

原型模式不仅体现在自定义类型方面,所有原生的引用类型也是采用原型模式,所有原生引用类型都在其构造函数的原型上定义了相关的方法。

3.5原型对象的问题
4.组合使用构造函数模式和原型模式

组合使用的好处:构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。结果:每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。还支持向构造函数传递参数。可谓是集两种模式之长。

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
    }
    Person.prototype = {
        constructor:Person,
        sayName:function () {
            alert(this.name);
        }
    }
    var person1 = new Person('jay',29,'singer');
    var person2 = new Person('jack',30,'software engineer');
    person1.sayName();//jay
    person2.sayName();//jack
    alert(person1.sayName === person2.sayName);//true
5.动态原型模式

动态原型模式的用处:把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(如有必要的情况下),又保持了同时使用构造函数和原型的优点。简单说,就是通过检查某个应该存在的方法是否有效来决定是否需要初始化原型。

    function Person(name, age, job) {
        //属性
        this.name = name;
        this.age = age;
        this.job = job;
        //方法
        if(typeof this.sayName != "function"){
            console.log('设置方法');//这里只会在初次调用构造函数时才会执行
            Person.prototype.sayName = function () {
                alert(this.name);
            };
            Person.prototype.sayAge = function () {
                alert(this.age);
            }
        }
    }
    var friend = new Person("jay",20,"software engineer");
    var friend1 = new Person("jaychou",30,"saler");
6.寄生构造函数模式
    function SpecialArray() {
        //创建数组
        var values = new Array();
        //添加传进来的值
        values.push.apply(values,arguments);
        //添加额外方法
        values.toPipedString = function () {
            return this.join("-");
        };
        //返回数组
        return values;
    }
    var colors = new SpecialArray("red","blue","black");
    alert(colors.toPipedString());

7.稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。与寄生构造函数模式相似,区别:①新创建对象的实例方法不引用this,②不使用new操作符调用构造函数。

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

        //添加方法
        o.sayName = function () {
            alert(name);
        }
        //返回对象
        return o;
    }
    var friend = Person('jaychou',30,'saler');
    friend.sayName();
上一篇下一篇

猜你喜欢

热点阅读