JS中创建对象的方法总结

来源:互联网 发布:node网络服务器搭建 编辑:程序博客网 时间:2024/06/05 00:15

1.简单对象字面量方式(适合只创建一个对象的情况)

<script type="text/javascript">    var person = {};//首先创建一个空对象    //为对象加入属性和方法    person.name = "zhangsan";    person.sayName = function () {        alert(this.name);    };</script>
非常简单,但一般情况下不推荐这种方法。JS good parts书中认为这种写法可读性不够强,作者推荐的是后面一种写法。

2.嵌套对象字面量方式(适合只创建一个对象的情况)

JS good parts中推荐这种写法:

<script type="text/javascript">    var person = {        name: "zhangsan",        sayName: function () {            alert(this.name);        }    };</script>
以下是创建类,然后再创建类的对象,适合创建多个对象的情况:

3.工厂模式

<script type="text/javascript">    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 person = createPerson('zhangsan', 22, 'enginer');    person.sayName();//zhangsan</script>

四.构造函数模式

<script type="text/javascript">    function Person(name, age, job) {        this.name = name;        this.age = age;        this.job = job;        this.sayName = function () {            alert(this.name);        };    };    var person1 = new Person('zhangsan', 22, 'enginer');    person1.sayName();//zhangsan    alert(person1 instanceof Person);//true    alert(person1 instanceof Object);//true        var person2 = new Person('lisi', 25, 'saler');</script>

构造函数模式与工厂模式对比:

1、构造函数模式没有显式的创建对象
2、构造函数模式直接将属性和方法赋给了this对象
3、构造函数模式没有return语句
构造函数模式要创建person的实例,必须使用new操作符,以这种方式调用构造函数实际上会经历4个步骤:
    1、创建一个新对象
    2、将构造函数的作用域赋给新对象
    3、执行构造函数中的代码
    4、返回新对象
创建自定义的构造函数可以将它的实例标识为一种特定的类型。 

构造函数模式的缺点:

每个方法都有在每个实例上重新创建一遍。person1和person2都有一个sayName()的方法,但两个方法不是同一个Function实例。不同实例上的同名函数是不相等的。
创建两个完成同样任务的Function实例没有必要,而且还有this对象在,不需要在执行代码前就把函数绑定在特定对象上,可以像下面这样。

<script type="text/javascript">    function Person(name, age, job) {        this.name = name;        this.age = age;        this.job = job;        this.sayName = sayName;    };    function sayName() {        alert(this.name);    }    var person = new Person('zhangsan', 22, 'enginer');    person.sayName();//zhangsan    alert(person instanceof Person);//true    alert(person instanceof Object);//true</script>
把sayName属性设置成全局的sayName函数,这样,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了同一个函数。
但是,如果对象需要定义很多方法,那么就要定义很多全局函数,自定义的引用类型也没有封装可言了。为了解决上述问题,引入原型模式。

五.原型模式

1.理解原型对象

    我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。prototype是通过调用构造函数而创建的那个对象实例的对象原型,使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

<script type="text/javascript">    function Person() {        this.address = "haidian";//实例对象属性    };    Person.prototype.name = "zhangsan";//原型对象属性    Person.prototype.age = 22;    Person.prototype.job = "enginer";    Person.prototype.sayName = function () {        alert(this.name);    };    var person1 = new Person();    alert(person1.name);//zhangsan    var person2 = new Person();    alert(person2.name);//zhangsan        alert(person1 instanceof Person);//true    alert(Person.prototype.constructor);//Person对象    alert(person1.constructor);//Person对象</script>
首先,解析器会问实例person1是否有实例属性name,如果有,就返回。
如果没有,就继续去person1的原型中搜索原型属性name,如果有就返回。
如果没有,再继续向person1的原型的原型中搜索。


2.原型相关方法

isPrototypeOf()确定实例和原型对象之间的关联:

Person.prototype.isPrototypeOf(person1);//true
Object.getPrototypeOf()返回的是[[prototype]]的值:

Object.getPrototypeOf(person1)//Person {name: “Yvette”, age: 26, job: “engineer”} 返回的是Person的原型对象。Object.getPrototypeOf(person1) === Person.prototype;//trueObject.getPrototypeOf(person1).name;//zhangsan
hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中,只有给定属性存在于实例中,才会返回true:
person1.hasOwnProperty(“name”);//falseperson1.hasOwnProperty(“address”);//true

3.原型与in操作符

有两种方式使用in操作符:单独使用和在for-in循环中使用:

单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性在于实例中还是原型中。 

使用for in循环,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括实例中的属性,也包括存在于原型中的属性。如果实例中的属性屏蔽了原型中不可枚举的属性,那么也会返回。IE9之前的版本实现上有一个Bug,屏蔽不可枚举属性的实例属性不会在for-in中返回。

<script type="text/javascript">    function Person() {        this.address = "haidian";//实例对象属性    };    Person.prototype.name = "zhangsan";//原型对象属性    Person.prototype.age = 22;    Person.prototype.job = "enginer";    Person.prototype.sayName = function () {        alert(this.name);    };    var person1 = new Person();    for (var prop in Person.prototype) {        if (prop == 'address') {            alert('address');//由于Person.prototype中无address, address是实例属性,不是原型属性,所以条件不满足,不输出address        }        else if (prop == 'name') {            alert('name');//name        }    }    for (var prop in person1) {//person1是通过构造函数创建的,所以既包括实例属性又包括原型属性        if (prop == 'address') {            alert('address');//address        }        else if (prop == 'name') {            alert('name');//name        }    }</script>

4.原型简写

<script type="text/javascript">    function Person() {        this.address = "haidian";//实例对象属性    };    Person.prototype = {        name: 'zhangsan',        age: 22,        job: 'enginer',        sayName: function () {            alert(this.name);        }    };    var person1 = new Person();    alert(person1 instanceof Person);//true    alert(Person.prototype.constructor);//Object    alert(person1.constructor);//Object</script>
原型简写导致了person1.constructor不再指向Person,而是指向了Object。如果constructor很重要,则需要特意将其设为适当的值,如:

 Person.prototype = {        constructor:Person,        name: 'zhangsan',        age: 22,        job: 'enginer',        sayName: function () {            alert(this.name);        }    };
但是这种方式会导致constructor属性变成可枚举。
如果想设置为不可枚举的(默认不可枚举),可以使用:
Person.prototype = {        name: 'zhangsan',        age: 22,        job: 'enginer',        sayName: function () {            alert(this.name);        }    };    Object.defineProperty(Person.prototype, 'constructor', {        enumerable: false,        value:Person    });

5.原型的动态性

由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来。
如果重写整个原型对象,情况就不一样了。调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。实例中的指针仅指向原型,而不指向构造函数。

<script type="text/javascript">    function Person() {        this.address = "haidian";//实例对象属性    };    var person1 = new Person();    Person.prototype = {        name: 'zhangsan',        age: 22,        job: 'enginer',        sayName: function () {            alert(this.name);        }    };    Object.defineProperty(Person.prototype, 'constructor', {        enumerable: false,        value:Person    });    alert(person1.name);//undefined</script>
Person.prototype指向的是原本的原型对象,而不会指向新的原型对象。

6.原型对象模式的缺点

原型模式最大问题是由其共享的本性所导致的。
对于包含引用类型值的属性来说,问题较为突出

<script type="text/javascript">    function Person() {        this.address = "haidian";//实例对象属性    };    Person.prototype = {        name: 'zhangsan',        age: 22,        friends:['a','b'],        job: 'enginer',        sayName: function () {            alert(this.name);        }    };    Object.defineProperty(Person.prototype, 'constructor', {        enumerable: false,        value:Person    });    var person1 = new Person();    var person2 = new Person();    person1.friends.push('c');    alert(person1.friends);//a,b,c    alert(person2.friends);//a,b,c</script>
本意只想修改person1的friends,但是却导致person2的friends属性值也改变了。因此我们很少单独使用原型模式。而是常用组合使用构造函数模式和原型模式。

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

创建自定义类型的最常用的方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性,这样每个实例都有自己的一份实例属性的副本,又同时共享着对方法的引用,最大限度的节省了内存。

<script type="text/javascript">    function Person(name,age,job) {//定义类        this.name = name;        this.age = age;        this.job = job;    };    Person.prototype = {        sayName: function () {            alert(this.name);        }    };    Object.defineProperty(Person.prototype, 'constructor', {        enumerable: false,        value:Person    });    var person1 = new Person('zhangsan',22,'enginer');//创建对象    var person2 = new Person('lisi',23,'saler');       alert(person1.name);//zhangsan    alert(person2.name);//lisi</script>

<script>    function Person(name,age,address,options) { //或var Person=function(name,age,address,options) {//定义类        this.name = name;        this.age = age;        this.address = address;        var defaultOptions = {phone:135353445,gender:1};        this.options = $.extend(defaultOptions, options);        this.subject = {};    }    Person.prototype = {        alert: function () {            alert(this.name + "-" + this.age + "-" + this.address);            return this;        },        view: function () {            alert(this.options.phone + "-" + this.options.gender);        }    };    Person.Example = new Person("longxi", 22, "北京", { phone: 1111111, gender: 0 }).alert().view();//创建对象并执行方法</script>

可以使用命名空间对类进行限定:

<script>    window.MyNamespace = window.MyNamespace || {};//var MyNamespace=MyNamespace||{}    MyNamespace.Person = function (name, age, address, options) {        this.name = name;        this.age = age;        this.address = address;        var defaultOptions = {phone:135353445,gender:1};        this.options = $.extend(defaultOptions, options);        this.subject = {};    }    MyNamespace.Person.prototype = {        alert: function () {            alert(this.name + "-" + this.age + "-" + this.address);            return this;        },        view: function () {            alert(this.options.phone + "-" + this.options.gender);        }    };    MyNamespace.Person.Example = new MyNamespace.Person("longxi", 22, "北京", { phone: 1111111, gender: 0 }).alert().view();</script>

<script>    window.MyNamespace = window.MyNamespace || {};//var MyNamespace=MyNamespace||{}     function person (name, age, address, options) {        this.name = name;        this.age = age;        this.address = address;        var defaultOptions = {phone:135353445,gender:1};        this.options = $.extend(defaultOptions, options);        this.subject = {};    }     person.prototype = {        alert: function () {            alert(this.name + "-" + this.age + "-" + this.address);            return this;        },        view: function () {            alert(this.options.phone + "-" + this.options.gender);        }     };     MyNamespace.Person = person;    MyNamespace.Person.Example = new MyNamespace.Person("longxi", 22, "北京", { phone: 1111111, gender: 0 }).alert().view();</script>

七.动态原型模式:

动态原型的方式同混合的构造函数/原型方式原理相似。唯一的区别就是赋予对象方法的位置。

<script type="text/javascript">    function Person(name, age) {        this.name = name;        this.age = age;        if (typeof Person._initialized == "undefined") {            Person.prototype.sayName = function () {                alert(this.name);            }            Person._initialized = true;        }    };</script>
动态原型方式是使用一个标志来判断是否已经给原型赋予了方法。这样可以保证该方法只创建一次。