美文网首页
Javascript基础系列之对象创建

Javascript基础系列之对象创建

作者: snailTJ | 来源:发表于2018-08-01 14:49 被阅读0次

创建对象

创建对象方式有很多,每一种都有自己的特点,可以根据不同场景去选择创建对象的方式

工厂模式

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 person1 = createPerson("Nicholas", 29, "Software Engineer"); 

优点:消除了对象间的耦合,将所有实例化的代码集中在一个位置防止代码重复

缺点:对象无法识别,因为所有的实例都指向一个原型

构造函数模式

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}
var person1 = new Person("Nicholas", 29, "Software Engineer"); 

优点:实例可以识别为一个特定的类型(自己定义)

缺点:每次创建实例时,每个方法都要重新创建一次

原型模式

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person(); 

优点:所有的属性和方法都共享,不需要每次实例化都重新创建一次

缺点:并且不能初始化参数

理解原型对象

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。就拿前面的例子来说,Person.prototype. constructor指向 Person

创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;至于其他方法,则都是从Object继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox、Safari和 Chrome 在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则完全不可见的。不过,要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间

[图片上传失败...(image-beef89-1533106139264)]

虽然在所有实现中都无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系

lert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true 

ECMAScript 5 增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值

alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas" 

当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性

function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
person1.name = null;
alert(person1.name);//null,设置为null不会回复指向原型的连接
delete person1.name;//通过操作符delete,会恢复指向原型的连接
alert(person1.name); //"Nicholas"——来自原型

原型与 in 操作符

有两种方式使用in操作符:单独使用和在for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中

同时使用hasOwnProperty()方法和in操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中

function hasPrototypeProperty(object, name){
    return !object.hasOwnProperty(name) && (name in object);
} 

由于 in 操作符只要通过对象能够访问到属性就返回 true,hasOwnProperty()只在属性存在于实例中时才返回 true,因此只要 in 操作符返回true而hasOwnProperty()返回 false,就可以确定属性是原型中的属性

更简单的原型语法

function Person(){
}
Person.prototype = {
    name : "Nicholas",
    age : 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
}; 

在上面的代码中,我们将Person.prototype设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外:constructor属性不再指向Person了。前面曾经介绍过,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的prototype对象,因此constructor 属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向 Person 函数此时,尽管 instanceof操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了

var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true 

如果 constructor 的值真的很重要,可以像下面这样特意将它设置回适当的值。

function Person(){
}
Person.prototype = {
    constructor : Person,
    name : "Nicholas",
    age : 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
}; 

以上代码特意包含了一个constructor属性,并将它的值设置为Person,从而确保了通过该属性能够访问到适当的值

组合使式

构造模式和原型模式组合

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}
Person.prototype = {
    constructor : Person,
    sayName : function(){
        alert(this.name);
    }
}
var person1 = new Person("Nicholas", 29, "Software Engineer"); 

优点:每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用

动态原型模式

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

缺点:使用动态原型模式时,不能使用对象字面量重写原型。

寄生构造函数模式

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("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas" 

除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。

构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值

稳妥构造函数模式

function Person(name, age, job){
    //创建要返回的对象
    var o = new Object(); 
    //可以在这里定义私有变量和函数
    //添加方法
    o.sayName = function(){
        alert(name);
     };
    //返回对象
    return o;
} 

var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas" 

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象

稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同

  • 一是新创建对象的实例方法不引用 this
  • 二是不使用 new 操作符调用构造函数

相关文章

网友评论

      本文标题:Javascript基础系列之对象创建

      本文链接:https://www.haomeiwen.com/subject/svqrvftx.html