javacsript创建对象的几种方式:

对象的创建:

一、单独声明定义一个对象

    // 创建一个对象的两种方法;
    // 方法1:new Object() 一个空白的,Object没有任何功能、属性的对象;不能在Array数值、Date日期、RegExp正则等对象上定义,否则可能会改变原有对象已定义的方法/属性;
    let obj1 = new Object();    // 一个空的对象创建;
    obj1.name = 'xiaoming';
    obj1.age = 30;
    obj1.say = function(){
    	console.log('我的名字叫:' + obj1.name + ',我的年龄是' + obj1.age);  // 此处obj1 可以改成this,这样其他对象也能调用此方法;
    }

    obj1.say();   // 我的名字叫:xiaoming,我的年龄是30;

    // 方法2:字面量的方式;
    let obj2 = {
    	name:'xiaogou',   // 对象整理赋给一个变量,则此处是',';
    	age:40,
    	say:function(){
    		console.log('我的名字叫:' + obj2.name + ',我的年龄是' + obj2.age); // 此处obj2 也可以改成this,这样其他对象也能调用此方法;
    	}
    };
    obj2.say();   // 我的名字叫:xiaogou,我的年龄是40

缺点:需要(利用一个接口)一个个的创建对象,每创建一个新的对象,则需要重复写许多相同的代码;可以改进一下;用函数将具体实现相同功能的代码包裹起来;

工厂模式

    // 创建工厂模式;
    function createPeople(name,age){   // 函数传递利用参数;
    	//原料;
    	let o = new Object();  //原料o,一个空的对象;
    	//加工;
    	o.name = name;       // 给原料安装个螺丝,打个蜡.....;
    	o.age = age;
    	o.say = function(){
    		console.log('我的名字叫:' + o.name + ',我的年龄是' + o.age);  // 同样,这里的o也可以改成this;
    	}
    	//出厂;
    	return o;
    }
    let people1 = createPeople('xiaoming',30);    // 新创建返回的对象O 赋给 people1;
    people1.say();  // 我的名字叫:xiaoming,我的年龄是30;
    let people2 = createPeople('xiaogou',40);
    people2.say();   // 我的名字叫:xiaogou,我的年龄是40;
    console.log(people1 instanceof Object);   // true;
    console.log(people1 instanceof createPeople);  // false;

缺点:1、创建的对象都是Object类型,没有办法知道其确切类型;对象中所有的函数都需要重新的定义,浪费内存;

构造函数模式;

    // 创建构造函数模式,首字母大写;
    function People(name,age){   // 函数传递利用参数;
    	
    	//let this = new Object();  默认有定义绑定,系统替做;
    	
    	this.name = name;       
    	this.age = age;
    	this.say = function(){
    		console.log('我的名字叫:' + this.name + ',我的年龄是' + this.age);  
    	}
    	/***this.say = new Function("console.log('我的名字叫:' + this.name + ',我的年龄是' + this.age)")**/  // 创建一个新的函数对象实例;
    	//return this;  返回,系统替做;
    }
    let people1 = new People('xiaoming',30);    
    people1.say();  // 我的名字叫:xiaoming,我的年龄是30;
    let people2 = new People('xiaogou',40);
    people2.say();   // 我的名字叫:xiaogou,我的年龄是40;
    console.log(people1 instanceof Object);   // true;
    console.log(people1 instanceof People);  // true;   //可以知道peopel的特定类型(属于People,而不是数组Array..日期Data..)

缺点:虽然知道了实例对象的确切类型,但是还是存在着每创建一个对象实例,所有的相同的属性、函数要重复定义,浪费内存;

原型模式:

    // 创建原型模式;
    function People(name,age){   // 函数传递利用参数;
    	
    	this.name = name;       
    	this.age = age;
    }
    /***  彻底改变原型:
    People.prototype = {
	    say:function(){
	        console.log('我的名字叫:' + this.name + ',我的年龄是' + this.age);
	    }
	    //  由于重新定义了原型,那么constructor会改变,需要重新手工设置;既:
	    constrctor:People;
	    //  由于重新定义了原型,那么此新的原型会无法作用在其之前创建的实例上;
    }

    ****/
    // 原型中追加方法:  
    People.prototype.say = function(){
    	console.log('我的名字叫:' + this.name + ',我的年龄是' + this.age);
    }
    let people1 = new People('xiaoming',30);    
    people1.say();  // 我的名字叫:xiaoming,我的年龄是30;   先在people1自己的实例中找say()方法,如果自己的实例中没有say方法,则去其原型中找;如果都没有则为undefine;
    let people2 = new People('xiaogou',40);
    people2.say();   // 我的名字叫:xiaogou,我的年龄是40;
    console.log(people1 instanceof Object);   // true;
    console.log(people1 instanceof People);  // true;   //可以知道peopel的特定类型(属于People,而不是数组Array..日期Data..)

缺点:打破了面向对象的封装特性,一个类所有的属性和方法都应该定义在类中,而此方法,共用的属性和相同方法的函数都定义在了类的外部;

动态原型模式:

    // 创建动态原型模式;
    function People(name,age){   // 函数传递利用参数;
    	
    	this.name = name;       
    	this.age = age;
    
	   if(typeof this.say != 'function'){   // 第一次创建实例对象的时候,则会执行if语句,创建People的原型对象;
		    People.prototype.say = function(){
		    	console.log('我的名字叫:' + this.name + ',我的年龄是' + this.age);
		    }
		}
    }
    let people1 = new People('xiaoming',30); 
    // 当创建第一个People类的实例对象people1时候,则会执行if语句块;此时,People原型中并无任何自定义的属性、方法;
    // 因此typeof this.say则会返回undefined,而不是function,所以就会执行if中的语句,这些自定义的属性和方法则追加到原型对象中;   
    people1.say();  // 我的名字叫:xiaoming,我的年龄是30;   
    let people2 = new People('xiaogou',40);
    //当第二次利于People类创建people2对象的时候,由于this.say在原型对象中(先从自身对象中找say方法,不存在的话再从原型对象中找say方法)已经存在,则不在执行if语句;
    people2.say();   // 我的名字叫:xiaogou,我的年龄是40;
    console.log(people1 instanceof Object);   // true;
    console.log(people1 instanceof People);  // true;   //可以知道peopel的特定类型(属于People,而不是数组Array..日期Data..)

缺点:比较完美,但有点过时了(相对于es6的class);

寄生构造函数模式

主要用来扩展原生的构造函数的。比如为原生的Array扩展一些方法,如果直接在Array.prototype原型对象上做扩展,则不仅可能会污染其他的数值,还可能将原型对象改乱、改错;

寄生:则意味着新的构造函数,是在Array构造函数的基础上修改扩展的;

    // 创建寄生构造函数模式;  构造函数里和工厂模式几乎一样;
    function Myarray(){
    	//创建一个数组(实例);
    	let array = new Array();
        //添加值;
        array.push.apply(array,arguments);  // 此时arguments,则表示Myarray函数中传递的参数;第一个参数为arguments[0]...
        //此处,利用apply而不用call,是因为call将把arguments[0],arguments[1]...数组当成一个值(数组值)来进行传参;
        //同理,不直接用array.push(arguments),也是因为这样整个arguments[0],arguments[1]...将会当成一个值(数组值)追加到array后面;
        //利用apply主要目的是,apply的参数是数组,既将参数放在数组中,则arguments数组中的元素,一个个的当成array.push()的参数,传入push中追加;
        
        //添加方法;
        array.toPipedString = function(){
        	return this.join('|');
        }
        //返回数组;
        return array;
    }
    //调用方法1:不建议用,因为没有new,既不能确切知道其对象类型;(是Array吗?)
    let color = Myarray('red','green','blue');
    console.log(color.toPipedString());    //red|green|blue;
    console.log(color instanceof Object);  // true;
    console.log(color instanceof Myarray); // false;
    console.log(color instanceof Array);   // true; color是一个数组Array,这个数组不仅有Array所有的方法和属性,还有特性的toPipedString方法;

    //调用方法2:使用new,体现了对象的构建过程;
    //既然利用new了,如果构造函数没有return,那么new在创建实例(A)的时候,系统会默认return创建的(A);如果构造函数中有return 对象(B)的时候, 则将B对象赋给变量(reurn B);
    //由于构造函数中的代码,创建的是新的对象(Array实例),并给此对象添加了属性、方法,所以,新创建的对象实例的proto、constructor指向的是Array,而不是Myarray;
    let color2 = Myarray('red','green','blue');
    console.log(color2.toPipedString());  // red|green|blue;

缺点:没有办法知道实例对象的确切类型,只知道它的原生对象类型;

稳妥构造函数模式:

    // 创建稳妥构造函数模式;  构造函数里和工厂模式大致一样;
    function People(name,age){
    	//创建要返回的对象;
    	let o = new Object();
    	// o.name = name;
    	// o.age = age;   // 注释掉,安全级别较高的属性,直接在object对象上,可以被外界随意修改访问;
    	
    	// 添加方式,安全获取name、age等等;
    	o.sayName = function(){   //只能通过sayName()方法,访问那么的值;
    		alert(name);
    	}
    	o.sayAge = function(){    //只能通过sayAge()方法,访问age的值;
    		alert(age);
    	}
        //返回对象;
        return o;
    }

    // 不能通过new创建,只能当做普通函数调用;
    let friend = People('xiaoming',30);
    friend.sayAge();   // 30;
    friend.sayName();  //xiaoming;

对象的继承:

    // 原型链继承
    function Animal(name){
    	this.name = name ||'Animal';
    	this.sleep = function(){
    		console.log(this.name + '正在睡觉!');
    	}
    	if(typeof this.eat != 'function'){
    		Animal.prototype.eat = function(food){
    		console.log(this.name + '正在吃:' + food);
    	   }
    	}
    }
    
    function Cat(){

    }
    Cat.prototype = new Animal();
    Cat.prototype.name = 'Cat';
    let xiaomao = new Cat();
    xiaomao.sleep();  // Cat正在睡觉!

缺点:

  1. 没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数;(因为传的参数都属于子类的原型上的)
  2. 父类的属性值如果是引用类型的时候,父类的实例(子类的原型)则会存在此属性值,如果有一个子类的实例修改此引用类型的属性值的话,那么其他的子类实例调用此值的时候,也将会受到影响(因为是修改的子类原型上的值);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值