js 创建对象的几种方式

对象可以通过两种形式定义:声明(文字)形式和构造形式
1)对象字面量:

var obj = {
    key:value
    //...
}

2)构造函数

var obj = new Object();
obj.key = value;

虽然Object构造函数或对象字面量可以用来创建单个对象,但这些方式有个明显的缺点:一个接口创建很多对象,会造成大量的重复代码。为解决这个问题,开始使用工厂模式的一种变体。

工厂模式

function createPerson(name,age,sex){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sex = sex;
    obj.sayName = function(){
        return this.name;
    }
    return obj;
}
var per1 = createPerson('Rose',18,'female');
per1.sayName();//Rose
var per2 = createPerson('Jack',19,'male');
per2.sayName();//Jack

工厂模式虽然解决了多个相似对象的问题,但未解决对象识别的问题(即:怎知一个对象的类型)。

构造函数

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.sayName = function(){
        return this.name;
    }
}
var per1 = new Person('Rose',18,'female');
per1.sayName();//Rose
var per2= new Person('Jack',19,'male');
per2.sayName();//Jack

与工厂模式的不同之处:
1)没有显式地创建对象;
2)直接将属性和方法赋给了this对象;
3)没有return语句返回对象。

根据惯例,构造函数有几点特点:1)构造函数以大写字母开头,非构造函数以小写字母开头;2)创建构造函数的新实例时,必须使用new操作符。

使用构造函数的主要问题是:同样的操作,但每个方法都需在实例上创建一遍

通过把函数转移到构造函数外,来解决这个问题

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.sayName = sayName;
}
function sayName(){
    return this.name;
}
var per1 = new Person('Rose',18,'female');
per1.sayName();//Rose
var per2= new Person('Jack',19,'male');
per2.sayName();//Jack

我们将sayName属性设置成全局的sayName函数,若对象定义了多个方法,那么我们将定义多个全局函数,就无封装性可言了,因此我们使用原型模式解决问题。

原型模式

function Person(){}
Person.prototype.name = 'Rose';
Person.prototype.age = 18;
Person.prototype.sex = 'female';
Person.prototype.sayName = function(){
    return this.name;
}
var per1 = new Person();
per1.sayName();//Rose
var per2 = new Person();
per2.sayName();//ROse
console.log(per1.sayName() === per2.sayName());//true,新对象的这些属性和方法是由所有实例共享的。

更简单的原型语法:用一个包含所有属性和方法的对象字面量来重写整个原型对象

function Person(){}
Person.prototype = {
    constructor: Person,
    name: 'Rose',
    age: 18,
    sex: 'female',
    sayName: function(){
        return this.name;
    }
}
var per1 = new Person();
per1.sayName();//Rose
var per2 = new Person();
per2.sayName();//Rose

注:将Person.prototype设置为等于一个以对象字面量形式创建的新对象,最终结果不变,但constructor属性不再指向Person,本质上完全重写了默认的prototype对象,因此constructor属性也变成了新对象的constructor,即指向Object构造函数。若constructor的值很重要,可以特意将它设置回适当的值,如:constructor: Person

原型模式最大的问题就是由其共享特性造成的,对于包含引用类型的属性来说,问题就更加明显了。

function Person(){}
Person.prototype = {
    constructor: Person,
    name: 'Rose',
    age: 18,
    sex: 'female',
    friends: ['Jack','Joe'],
    sayName: function(){
        return this.name;
    }
}
var per1 = new Person();
per1.friends.push('John');
var per2 = new Person();
console.log(per1.friends);//["Jack", "Joe", "John"]
console.log(per2.friends);//["Jack", "Joe", "John"]
console.log(per1.friends === per2.friends);//true

由于friends数组属性存在于Person.prototype属性中,而非per1中,所以在per1中修改friends数组的属性值,也同样会反应在per2中,这并不是我们想要的结果,这就是我们很少看到单独使用原型模型的问题所在。

组合使用构造函数和原型模式

构造函数模式定义实例属性,原型模式定义共享方法和共享属性

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.friends = ['JJ','Joe'];
}
Person.prototype = {
    constructor: Person,
    sayName: function(){
        return this.name;
    }
}
var per1 = new Person('Rose',18,'female');
per1.friends.push('John');
var per2 = new Person('Jack',19,'male');
console.log(per1.friends);//["JJ", "Joe", "John"]
console.log(per2.friends);//["JJ", "Joe"]
console.log(per1.friends === per2.friends);//false
console.log(per1.sayName === per2.sayName);//true

构造函数和原型模式混成的模式,是ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。

动态原型模式

在其他OO语言经验的开发人员在看到独立的构造函数和原型时,很可能会感到困惑。动态原型模式致力于解决这个问题,它将所有信息都封装在构造函数中。

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    if(typeof this.sayName != 'function'){
        Person.prototype.sayName = function(){
            return this.name;
        }
    }
}
var per1 = new Person('Rose',18,'female');
per1.sayName();

使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

寄生构造函数模式

基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

function Person(name,age,sex){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sex = sex;
    obj.sayName = function(){
        return this.name;
    }
    return obj;
}
var per1 = new Person('Rose',18,'female');
per1.sayName();

除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式与工厂模式一模一样。
注:返回的对象域构造函数或者与构造函数的原型属性之间没有关系,即:构造函数返回的对象与在构造函数外部创建的对象没有什么不同,因此,不能依赖 instanceof 操作符来确定对象类型。

稳妥构造函数模式

稳妥对象:指没有公共属性,而且其方法也不引用this对象(禁止使用new和this)。

function Person(name,age,sex){
    var obj = new Object();//创建要返回的对象
    obj.name = name;//定义私有变量
    obj.age = age;
    obj.sex = sex;
    obj.sayName = function(){//创建方法
        return this.name;
    }
    return obj;//返回对象
}
var per1 = Person('Rose',18,'female');
per1.sayName();//Rose

per1就是一个稳妥对象,除了sayName方法,没有其它途径去获取Person的name属性。
注:与寄生构造函数模式类似,使用稳妥构造函数创建的对象与构造函数没有什么关系,因此使用instanceof操作符对这种对象也没有意义。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值