JavaScript创建对象7种方式

0. Object.create 和对象字面量

先说说创建对象的一般方法,最简单就是对象字面量。
var book={
“titile”:”JS”,
“sub-titile”:”DG”,
“author”:{
firstname:”David”,
lastname:”xxx”
}
};

这种方法对于属性有一定的要求,就是如果属性名有连接符,其他符号就必须用字符串表示,一般可以不需要。
对象直接量是一个表达式,这个表达式每次运算都创建并初始化一个新的对象。每次计算对象直接量的时候都会计算它的每个属性值。 也就是说,如果在一个重复调用的函数的循环体中使用了对象直接量,它会创建很多新对象。

所有对象直接量创建的对象具有同一个原型对象,就是Object.prototype

Object.create
这个方法用于创建一个新对象,第一个参数是这个对象的原型。 Object.create() 提供第二个可选参数,用于对对象的属性进一步描述。

var o1=Object.create({x:1,y:2});
var o2=Object.create(null); //o2不继承任何属性和方法
var o3=Object.create(Object.prototype);//和new Object()效果一样。

new 操作符会把构造函数创建的对象的原型设置为构造函数的prototype属性的值~!

虽然上面两个方法都可以创建单个对象,但是明显缺点:使用同一个接口创建很多对象,产生大量重复代码,
而且仅仅是改变一下值,也要改变传递的对象。
·~~

Object.creaet({x:1,y:2,z:3})
Object.creaet({x:1,y:3,z:3})
Object.creaet({x:3,y:3,z:1})
//对象直接量也是如此

1.工厂模式

function createPeople(name,age,job){
    var o=new Object(); //Object.create(Object.prototype)
    o.name=name;
    o.age=age;
    o.job=job;
    o.sayName=function(){
    }
    return o;
}

Usage

var person1=createPeople("NIco",12,"banker");
var person2=createPeople("NIco",13,"banker");

优缺点
工厂模式虽然解决了创建多个相似对象的问题,但是却没有解决对象识别的问题,也就是怎么知道一个对象的类型?

2.构造函数模式

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=function(){
    //...
    };
}

var person1=new Person('xxx',29,"S");

不同之处:

  • 没有显式创建对象
  • 没有return语句
  • 直接将属性和方法赋给this对象
  • 使用了new关键字

大写字母只是构造函数的一个约定而已。

构造函数的问题
构造函数的每个方法都要在每个实例上重新创建一遍。在前面的例子中,就是那个sayName方法,方法在不同对象上是不同的实例。

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=new Function("alert(this.name)"); //与声明函数在逻辑上等价
}

因为其实创建函数也是创建一个对象,JS中函数也是对象~ 所以不同实例上的同名函数是不相等的。
例如:
person1.sayName==person2.sayName //false

new关键字的实质

  • 隐式创建一个新对象
  • 将构造函数的作用域赋给新对象(this指向了这个对象)
  • 把构造函数的方法和属性赋给这个对象(也就是执行这个构造函数)
  • 隐式返回this指向的这个新对象

可以看到我们的这个函数的原型把constructor指向了本身。即 准确说我们每次创建一个函数,都会有一个原型。
这里写图片描述

var p1=new Person('3a',12,'xx');

对于这个新的对象来说,它的原型就是prototype属性指向的那个对象~
这里写图片描述

3.原型模式

由前面的介绍,可以知道原型是每个函数都有的一个属性,这个属性是一个指针,指向一个对象。
这个对象的用途:
包含了可以由特定类型的所有实例共享的方法和属性。

prototype就是通过调用构造函数创建的那个对象实例的原型对象
换句话说吗,也就是说所有的实例都会共享原型对象的属性和方法!

function Person(){
}

Person.prototype.name="Nicolas";
Person.prototype.age=29;
Person.prototype.job="software engineer";
Person.prototype.sayName=function(){
};

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

比较实例的方法发现相等,没有重复创建!

p1.sayName==p2.sayName; //true

更简单的原型模式:

function Person(){}
Person.prototype={
    name:"Nicolas",
    age:29,
    job:"Soft Engineer"
    sayName:function(){
    }
};

这种写法跟前面的一样, 注意的是前面的函数声明是必须的。 因为创建一个函数,就同时就会创建它的prototype对象,这个对象会自动获得constructor属性。
而不声明这个函数的话,这个原型对象的constructor属性就是指向Object构造函数了~!!

[[Enumberable]]
这种方式设置constructor属性会导致它的这个 [[Enumberable]]属性被设置为true。 默认下原生的constructor属性是不可枚举的。

原型模式的问题
省略了为构造函数传递参数的环节,所有实例默认下都取得相同的属性值。
所有属性是被实例共享的,基本值的属性是可以通过添加一个同名属性来覆盖。
但是包含引用类型的属性值,就不同了~
我们修改了这个属性的值,就会反映到所有实例对象上!

function Person(){}
Person.prototype={
    name:"Nicolas",
    age:29,
    friend:["SH","WW"],
    job:"Soft Engineer"
    sayName:function(){
    }
};
var p1=new Person();
var p2=new Person();
p1.friend.push('Van'); //

无论是p1还是p2的数组都改变了。

4.组合构造模式和原型模式

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
}
Person.prototype={
    name:"Nicolas",
    age:29,
    friend:["SH","WW"],
    job:"Soft Engineer"
    sayName:function(){

    }
};

构造函数模式定义实例属性
原型模式定义方法和共享的属性。
每个实例有自己的一份实例属性的副本,同时共享方法的引用
应用最广泛,最认同的一种方式

5.动态原型模式

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    if(typeof this.sayName!="function"){
        Person.prototype.sayName=function(){
        //..
        }
    }
}

var friend=new Person("ss",29,'xxx');

区别:
仅仅是在于如果sayName方法如果不存在的时候,才会将它添加到原型中。 这段代码只是在初次调用构造函数执行。
此后原型已经完全初始化了。 记住,这里对原型做的修改都会在所有实例中反映!
这种方法,检查方法有没有被定义,也就是说原型可以在后面更新。
比如:

Person.prototype.xxx=function(){
}

你用一个之前创建过的实例访问这个函数是合法的

值得注意的是
不要像这样重写:
Person.prototype={
xxx:
yyy:
}

因为这样会切断现有实例与新原型的联系!

6.寄生构造函数模式

function Person(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 friend=new Person("ss",29,'xxx');

创建了一个对象,并把方法和属性都封装都里面。

用处
默认返回新的对象实例,也可以在需要返回值的时候返回值~
用在特殊情况下创建构造函数。
如下面这个,我们不能直接修改Array()这个内置的构造函数,所以我们使用这个模式新建一个对象(数组)。

function SpecialArray(){
    //创建数组
    var values=[];
    //添加值
    values.push.apply(values,arguments);
    //添加方法
    values.toPipeString=function(){
        return this.join("|");
    }
    return values;
}

返回值的问题
我们new操作符,默认是会返回一个新的实例对象(new创建一个对象,将该函数作用域赋给对象即绑定this,更重要的应该是创建一个连接到该函数的prototype成员)
而我们在构造函数末尾写一个return语句就可以重写构造函数时返回的值。
这也是为什么上面的写法是返回一个数组。

寄生构造函数模式的问题

  1. 返回的对象与构造函数或者构造函数的原型对象没有什么关系。 在构造函数内部和外部创建都一样
  2. 不能依赖instanceof 确定对象类型。

所以:

var colors=new SpecialArray("blue",'rer','cos')
var colorsx=new SpecialArray("blue",'rear','cosx');
colors.toPipeString==colorsx.toPipeString
//false

函数对象被重新创建了, 所以不建议使用这种模式

7.稳妥构造函数模式

durable object 稳妥对象:
没有公共属性,其方法不引用this的对象。
用处
一些安全的环境中,禁止使用this和new,或者防止数据被其他应用程序改动时使用。

function Person(name,age,job){
    var o=new Object();
    //定义私有变量和函数
    o.sayName=function(){
        alert(name);
    };
    return o;
}

这种模式,除了sayName方法外没有方法可以访问私有变量name!

var friend=new Person("ss",29,'xxx');
friend.sayName(); //ss

问题
同样,它创建的对象与构造函数之间也没有什么关系,不能用instanceof操作符来区分对象的类型。

总结
定义在原型中的属性和方法是被实例共享的,因为它这些函数或者属性都是通过原型链搜索得到的,搜索!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值