zoukankan      html  css  js  c++  java
  • js中创建对象的几种方式

    创建对象指创建一个object并给这个对象添加属性和方法,有以下几个方式:

    最基本的:

    var Person={};
    Person.name='tom';
    Person.age='20';
    Person.sayname=function(){
    alert(this.name);
    };

    创建了一个Person对象,并添加了name,age属性,还有一个sayname方法。


    下面是用构造函数方式创建:

    function Person(name,age){
    			this.name=name;
    			this.age=age;
    			this.sayname=function(){
    				alert(this.name);
    			};
    		}
    		var p1=new Person('yom',0);
    		var p2=new Person('tom',99);
    		alert(p1.name);	//yom
    		alert(p2.name); //tom
    		alert(p1.constructor==p2.constructor);//true
    		alert(p1 instanceof Person);//true
    		alert(p2 instanceof Person);//true
    注意:此时实例化的两个实例p1和p2都源自Person,这两个实例都含有constructor属性,该属性指向其构造函数,alert(p1.constructor==p2.constructor);//true可证明,这种方法的不足之处在于两个实例间相互独立,倘如有大量实例,造成很大内存浪费。


    下面用原型方式可以解决实例间的属性不能共享的问题:

    function Person(){}
    		Person.prototype.name='tom';
    		Person.prototype.age=20;
    		Person.prototype.sayname=function(){
    			alert(this.name);
    		};
    		var p1=new Person();
    		
    		alert(p1.name);//tom
    		alert(p2.name);//tom
    		alert(p1.name==p2.name);//true
    上述代码中p1和p2共享Person对象原型中的属性,但,此时并不能通过实例来重写原型中的属性,因为一旦在实例中重新定义属性后就会屏蔽原型中的属性,

    因为此时会优先使用实例中的属性,如果实例中没有该属性,那就上找到原型,下面代码中p1使用的是实例中的name属性,p2使用的是原型中的属性:

    function Person(){}
    		Person.prototype.name='tom';
    		Person.prototype.age=20;
    		Person.prototype.sayname=function(){
    			alert(this.name);
    		};
    		var p1=new Person();
    		
    		alert(p1.name);//tom
    		alert(p2.name);//tom
    		alert(p1.name==p2.name);//true
    		p1.name='jim';//通过实例p1来重新定义name属性,屏蔽了原型中的name(只屏蔽,不修改)
    		alert(p1.name);//jim
    		alert(p2.name);//依然是tom
    上面每添加一个属性都要Person.prototype.XX=XX;  很麻烦,可以用字面量的方式一次性定义,但要注意一个问题,那会重写默认的prorotype,使constructor不再指向Person

    function Person(){}
    		Person.prototype={
    			name:'tom',
    			age:20,
    			sayname:function(){
    				alert(this.name);
    			}
    		};
    		var p1=new Person();
    		var p2=new Person();
    		alert(p1.name);//tom
    		alert(p2.name);//tom
    		alert(p1.name==p2.name);//true
    		p1.name='jim';//通过实例p1来重新定义name属性,屏蔽了原型中的name(只屏蔽,不修改)
    		alert(p1.name);//jim
    		alert(p2.name);//依然是tom
    		alert(p1.constructor==Person);//重写了prototype,constructor不再指向Person,但可手动调整
    要想手动调整在prototype里添加一句就好:

    Person.prototype={
    			constructor:Person,//手动更改
    			name:'tom',
    			age:20,
    			sayname:function(){
    				alert(this.name);
    			}
    		};

    手动更改好后:alert(p1.constructor==Person);为true


    上面提到,通过p1来重新定义name属性不会影响p2中的name,因为p1重新定义name属性后该属性属于p1这个实例中的属性,p2依然适用原型中的属性,所以不受影响。这里最核心的原因是因为name属性的属性值是js的基本数据类型(alert(typeof Person.name);为string),不是引用类型,倘若是引用型数据,那改动p1,p2就有影响了,这也是原型方式的不足之处:

    function Person(){}
    		Person.prototype={
    			constructor:Person,
    			name:['tom','cat'],//name不再是基本数据类型,而是引用型Array
    			age:20,
    			sayname:function(){
    				alert(this.name);
    			}
    		};
    		var p1=new Person();
    		var p2=new Person();
    		alert(p1.name);//tom
    		alert(p2.name);//tom
    		p1.name.push('newname');//通过实例p1来修改引用型name属性
    		alert(p1.name);//'tom','cat','newname'
    		alert(p2.name);//p2也改变:'tom','cat','newname'
    造成p1,p2都会改变的原因是两者都指向同一数组。

    下面就是相对来说最‘完美’,最常见的创建对象的方式:构造函数模式与原型模式并用。其优点是把共有的属性和方法定义在原型中,把实例属性定义在构造函数中,这样,对于不同的实例来说,该共享的共享,该独立的独立。把引用型数值的属性定义到构造函数中,也就解决了上述原型模式中的不足之处:

    function Person(name,age){
    			this.name=name;
    			this.age=age;
    			this.money=[10,100,1000];//这是个引用型,要定义在构造函数中
    		}		
    		Person.prototype={
    			constructor:Person,
    			sayname:function(){
    				alert(this.name);
    			}
    		};
    		var p1 = new Person('tom',20);
    		var p2=new Person('cat',30);
    		alert(p1.money);//10,100,1000
    		alert(p2.money);//10,100,1000
    		p1.money.push(10000); //p1存入10000元
    		alert(p1.money); //10,100,1000,10000
    		alert(p2.money); //p2的money依然不变,还是10,100,1000。此时不再受p1的影响

    还有一种更加“智能”的方式:动态原型模式

    function Person(name,age){
    			this.name=name;
    			this.age=age;
    			this.money=[10,100,1000];//这是个引用型,要定义在构造函数中
    			if(typeof this.sayname!='function')
    			{
    				Person.prototype.sayname=function(){ //注意:此处不能用字面量的方式重写prototype,否则切断实例与新原型的关系
    					alert(this.name);
    				};
    			}
    		}		
    
    		var p1 = new Person('tom',20);
    		var p2=new Person('cat',30);
    		alert(p1.money);//10,100,1000
    		alert(p2.money);//10,100,1000
    		p1.money.push(10000); //p1存入10000元
    		alert(p1.money); //10,100,1000,10000
    		alert(p2.money); //p2的money依然不变,还是10,100,1000。此时不再受p1的影响
    		p1.sayname();//tom
    		p2.sayname();//cat

    除此之外还有寄生构造函数模式和工厂模式,两者的区别是实例化实例的方式不同,其余全部一样:

    所谓寄生就是在function里创建对象,并给对象添加属性后从function中返回该对象

                    function Person(name,age){
    			var o=new Object();//在函数里创建对象
    			o.name=name;
    			o.age=age;
    			o.sayname=function(){
    				alert(this.name);
    			};	
    			return o;  //添加完属性后返回o		
    		}		
    		//寄生构造函数模式用new
    		var p1 = new Person('tom',20);//寄生构造函数模式
    		//工厂模式直接调用Person函数
    		var p2=Person('cat',30);      //工厂模式
    		alert(p1.age);//20
    		alert(p2.age);//30
    		p1.sayname();//tom
    		p2.sayname();//cat

    注意:如果用此文的第一种构造函数模式创建对象,用new和直接调用这两种方式的区别在于this上,另写一篇文章介绍。此文的叙述方式可能存在很多不足或错误,本人还是菜鸟阶段,欢迎批评指正。









  • 相关阅读:
    问题 E: C#判断回文字符串
    hdu 1130 How Many Trees? 【卡特兰数】
    The writing on the wall
    字典树的应用
    完全背包
    多重背包
    hdu 2191 【背包问题】
    最长上升子序列 and 最长公共子序列 问题模板
    hdu 4704 Sum 【费马小定理】
    费马小定理
  • 原文地址:https://www.cnblogs.com/chayangge/p/4288699.html
Copyright © 2011-2022 走看看