JavaScript之创建对象

Posted wzp-monkey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript之创建对象相关的知识,希望对你有一定的参考价值。

1.工厂模式

ECMAScript中无法创建类,因此使用函数来封装以特定接口创建对象的细节。

例如:

        function createPerson(name,age,job) {
            var o = new Object();
            o.name = name;
            o.age = age;
            o.job = job;
            o.sayName = function() {
                console.log(o.name);
            };
            return o;
        }
        var person1 = createPerson("Nicholas",29,"software Engineer");
        var person2 = createPerson("Greg",27,"Doctor");

  缺点:无法知道对象的类型

2.构造函数模式

        function Person(name,age,job) {
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = function() {
                console.log(this.name);
            };
        }
        var person1 = new Person("Nicholas",29,"software Engineer");
        var person2 = new Person("Greg",27,"Doctor");

  创建Person的新实例,必须使用new操作符。这种方式调用构造函数实际上会经历一下四个步骤:

  (1)创建一个新对象;

  (2)将执行环境的作用域赋给新对象(因此this就指向了这个新对象)

  (3)执行构造函数中的代码(为这个新对象添加属性)

  (4)返回新对象

优点:构造函数能够将它的实例标识为一种特定的类型;

缺点:由于实例方法不共享,因此每个方法都要在每个实例上重新创建一遍。

3.原型模式

每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象包含可以由特定类型的所有实例共享的属性和方法。

例如:

        function Person(){
        }

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

        var person2 = new Person();
        person2.sayName();  //"Niholas"

        console.log(person1.sayName == person2.sayName);  //true

  当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型函对象,ECMA第5版管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox、Safira和Chrome在每个对象上都支持一个_proto_。

isPrototypeOf()

虽然在所有实现上都无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象实例与对象原型的关系。

console.log(Person.prototype.isPrototypeOf(person1));  //true

console.log(Person.prototype.isPrototypeOf(person2));  //true

  

Object.getPrototypeOf()

ECMAScript 5增加了一个新方法,叫Object.getPrototypeOf(),改方法返回[[Prototype]]的值。

console.log(Object.getPrototypeOf(person1) == Person.prototype));  //true

console.log(Object.getPrototypeOf(person1).name));  //"Nicholas" 

hasOwnProperty()

使用hasOwnPropertyOf()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法(不要忘了它是从Object继承而来的)只在给定属性存在于对象实例中,才会返回true。

原型与in操作符

in操作符

in操作符会在通过对象能够访问给定属性时返回true。

因此只要in操作符返回true而hasOwnProperty()返回false,就可以确定属性是原型中的属性。

for-in操作符

在使用for-in 循环时,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性(即将[[Enumerable]]标记为false 的属性)的实例属性也会在for-in 循环中返回。(因为根据规定,所有开发人员定义的属性都是可枚举的)

Object.key()

要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的Object.key()方法。这个方法接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

var keys = Object.keys(Person.prototype);
console.log(keys); //"name,age,job,sayName"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
condole.log(p1keys); //"name,age"

Object.getOwnPropertyNames()

如果你想要得到所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法。

var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName"

更简单的原型语法

使用对象字面量来重写整个原型函数,例如:

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

  注意,以这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true。(默认情况下,原生的constructor属性时不可枚举的)所以可以使用ECMAScript 5的Object,defineProperty()方法重设构造函数。

例如:

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

//重设构造函数,只适用于ECMAScript 5 兼容的浏览器
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});

原型的动态性

可以随时为原型添加属性和方法,并且修改能够立即在虽有对象实例中反映出来。

var friend = new Person();
Person.prototype.sayHi = function(){
alert("hi");
};
friend.sayHi(); //"hi"(没有问题!)

但是如果是重写整个原型对象,那么情况就不一样了。重写原型对象切断了现有原型于任何之前已经存在的对象实例之间的联系。

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

原生对象的原型

所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。

console.log(typeof Array.prototype.sort); //"function"
console.log(typeof String.prototype.substring); //"function"

通过原生对象的类型,不仅可以取得多有默认方法的引用,而且也可以定义新方法。下面的代码就给基本包装类型String添加了一个名为startWith()的方法:

String.prototype.dtartWith = function (text) {
return this.indexOf(text) == 0;
};

var msg = "Hello World!";
console.log(msg.startWith("Hello"));  //true  

原型对象的问题

原型模式的最大问题是由其共享的本性所导致的。

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

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

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

        function Person() {
            this.name = name;
            this.age = age;
            this.job = job;
            this.friends = ["asahrlby", "Count"];
        }

        Person.prototype = {
            constructor: Person,
            sayName: function () {
                console.log(this.name);
            }
        }

        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Gery", 27, "Doctor");

        person1.friends.push("Van");
        console.log(person1.friends); //"Shelby,Count,Van"
        console.log(person2.friends); //"Shelby,Count"
        console.log(person1.friends === person2.friends); //false
        console.log(person1.sayName === person2.sayName); //true

动态原型模式

通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

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);
};  //这段代码只会在初次调用构造函数时才会执行
}
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

寄生构造函数模式

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"

稳妥构造函数模式 

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this 的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this 和new),或者在防止数据被其他应用程序(如Mashup程序)改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new 操作符调用构造函数。

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"

使用寄生函数模式和稳妥构造函数模式创建的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。

 

以上是关于JavaScript之创建对象的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段12——JavaScript的Promise对象

JavaScript之创建对象的模式

javascript之一切皆为对象2

javascript之一切皆为对象2

javascript之BOM对象(二location对象)

常用Javascript代码片段集锦