JavaScript 对象的建立

Object类型是JavaScript中使用最多的一种类型。建立Object实例的方式有多种,接下来一一列举。

1. Object构造函数

var person =  new Object();
person.name = "Brittany";
person.age = 23;
person.job = "web front-end engineer";
person.sayName =  function() {
    console.log( this.name);
};
person.sayName();    // Brittany

2. 对象字面量模式

var person = {
    name: "Brittany",
    age: 23,
    job: "web front-end engineer",
    sayName:  function() {
        console.log( this.name);
    }
};
person.sayName();

虽然Object构造函数或对象字面量均可以用来建立单个对象,但这些方式有个明显的缺点:使用同一个接口建立不少对象,会产生大量的重复代码。为解决这个问题,可使用工厂模式的一种变体。web

3. 工厂模式 

function createPerson(name, age, job) {
     var o =  new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName =  function() {
        console.log( this.name);
    };
     return o;
}

var person1 = createPerson("Brittany", 23, "Software Engineer");
var person2 = createPerson("Sam", 26, "Software Engineer");
console.log( typeof person1);    // Object

工厂模式虽然解决了建立多个类似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。如代码中只能检测出person1为Object类型。随着JavaScript的发展,又一个新模式出现了。数组

4. 构造函数模式

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("Brittany", 23, "Web front-end engineer");
var person2 =  new Person("Closure", 26, "Manager");
person1.sayName();
person2.sayName();
console.log(person1.sayName == person2.sayName);    // false

使用构造函数的主要问题:每一个方法都要在每一个实例上从新建立一遍。如代码中所示,person1的sayName和person2的sayName不相等。能够将函数定义转移到构造函数外部来解决。函数

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

sayName函数的定义转移到了构造函数外部。而在构造函数内部,咱们将sayName属性设置成等于全局的sayName函数。这样一来,因为sayName包含的是一个指向函数的指针,所以person1和person2对象就共享了在全局做用域中定义的同一个sayName()函数。这的确解决了两个函数作同一件事的问题,但是新问题又来了:在全局做用域中定义的函数实际上只能被某个对象调用,这让全局做用域有点名存实亡。而更让让人没法接受的是:若是须要定义不少方法,那就要定义不少个全局函数,因而这个自定义的引用类型就无封装性可言。这些问题可经过使用原型模式来解决。this

5. 原型模式

1)对象建立 

function Person() {}
Person.prototype.name = "Brittany";
Person.prototype.age = 23;
Person.prototype.job = "Web front-end engineer";
Person.prototype.getName =  function() {
    console.log( this.name);
};
var p1 =  new Person();
var p2 =  new Person();

console.log(p1.name);                     // Brittany
console.log(p2.name);                     // Brittany
console.log(p1.getName == p2.getName);    // true

实例中建立的属性会覆盖原型中的同名属性,不能修改原型中的属性。spa

p1.name = "Susan";
console.log(p1.name);                     // Susan

hasOwnProperty()检测一个属性是否存在于实例中。prototype

console.log(p2.hasOwnProperty("name"));   // false
p2.name = "koko";
console.log(p2.hasOwnProperty("name"));   // true
delete p2.name;
console.log(p2.hasOwnProperty("name"));   // false

isPropertyOf()设计

console.log(Person.prototype.isPrototypeOf(p1));    // true
console.log(Person.prototype.isPrototypeOf(p2));    // true

getPropertyOf()指针

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

2)原型与in操做符

in单独使用时,经过对象访问到特定属性时返回true,不管该属性存在于实例中仍是原型中。hasOwnProperty(),经过对象访问到特定属性时返回true,且该属性存在于实例中。 code

var p3 =  new Person();
console.log("name"  in p3);                 // true
console.log(p3.hasOwnProperty("name"));    // false
p3.name = "insist";
console.log(p3.hasOwnProperty("name"));    // true

肯定属性究竟是存在于对象中,仍是存在于原型中。以下函数hasPrototypePropery()返回true表示该属性存在于原型中,而不是存在于实例中。对象

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

for..in循环,全部经过对象可以访问的,可枚举的(enumerated)属性,既包括存在于实例中的属性,也包括存在于原型中的属性。

for( var prop  in p1) {
    console.log(prop);                     // name age job sayName
}

Object.keys(),ECMAScript5的方法,取得对象上全部可枚举的属性,接收一个对象做为参数,返回值是一个包含全部可枚举属性的字符串数组。注意:Person.prototype也是对象。

var keys = Object.keys(Person.prototype);
console.log(keys);              // ["name age job sayName"]
var p1 =  new Person(); 
console.log(Object.keys(p1));   // []
p1.name = "get";
console.log(Object.keys(p1));   // ["name"]

Object.getOwnPropertyNames(),获得全部实例属性,不管它是否可枚举。

var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);      // ["constructor", "name", "age", "job", "getName"] 
var keys_p1 = Object.getOwnPropertyNames(p1);
console.log(keys_p1);   // []

3)更简洁的原型语法

function Person() {}
Person.prototype = {
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
         return  this.name;
    }
};
var friend =  new Person();
console.log(friend  instanceof Person);       // true
console.log(friend  instanceof Object);       // true
console.log(friend.constructor == Person);   // false
console.log(friend.constructor == Object);   // false

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

经过以下方式,将constructor手动设置为合适的值。

Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
        console.log( this.name);
    }
};

4)原型的动态性

在原型中查找值的过程是一次搜索,所以咱们对原型对象所作的任何修改都可以当即从实例上反映出来——即便是先建立了实例后修改原型也照样如此。

var friend =  new Person();
Person.prototype.sayHi =  function() {
    console.log("hi");
};
friend.sayHi();

尽管能够随时为原型添加属性和方法,而且修改可以当即在全部对象实例中反映出来,但若是是重写整个原型对象,状况就不同了。

function Person() {}
var friend =  new Person();
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
        console.log( this.name);
    }
};
friend.getName();                   // error

如果建立实例放在重写原型对象以后,则不会报错。

5)原生对象的原型

全部原生引用类型(Object、Array、String)都在其构造函数的原型上定义了方法,如:Array.prototype.sort()、String.prototype.subString(), 经过原生对象的原型能够取得全部默认方法的引用,并能够定义新方法。

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

String.prototype.startsWith =  function(text) {
     return  this.indexOf(text) == 0;
};
var msg = "Hello World";
console.log(msg.startsWith("Hello"));            // true

6)原型对象的问题

缺点一:省略了为构造函数传递初始化参数这一环节,结果全部实例在默认状况下将取得相同的属性值。

缺点二:原型中全部属性被许多实例共享,这种共享对于函数很是适合。对于包含基本值属性倒也说得过去,由于经过在实例上添加一个同名属性,能够隐藏原型中对应的属性。但对于包含引用类型值得属性来讲,问题比较突出。

function Person() { }
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    friends: ["pink", "judy", "sam"],
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
        console.log( this.name);
    }
};
var person1 =  new Person();
var person2 =  new Person();
person1.friends.push("leo");
console.log(person1.friends);           // ["pink", "judy", "sam", "leo"]
console.log(person2.friends);            // ["pink", "judy", "sam", "leo"]
console.log(Person.prototype.friends);   // ["pink", "judy", "sam", "leo"]
person1.age = 35;
console.log(person1.age);            // 35
console.log(person2.age);            // 23
console.log(Person.prototype.age);   // 23

person1的friends属性修改影响了person2的friends,可是person1的age属性修改并未影响person2的age属性。

缘由在于:friends数组存在于Person.prototype中而非person1中,所以修改也会经过person2.friends(与person1.friends指向同一个数组)反映出来。而age属性在person1中也存在一份,修改的age属性只是修改person1中的,并不能修改Person.prototype中的age属性。

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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这样,每一个实例都会有本身的一份实例属性的副本,但又同时共享着对方法的引用。

function Person(name, age, job) {
     this.name = name;
     this.age = age;
     this.job = job;
     this.friends = ["aa", "bb", "cc"];
}
Person.prototype = {
    constructor: Person,
    sayName:  function() {
        console.log( this.name);
    }
};
var person1 =  new Person("Brittany", 23, "Web front-end Engineer");
person1.friends.push("dd");      // ["aa", "bb", "cc", "dd"]
console.log(person1.friends);
var person2 =  new Person("Sam", 26, "Web front-end Engineer");
console.log(person2.friends);    // ["aa", "bb", "cc"]
console.log(person1.friends == person2.friends);   // false
console.log(person1.sayName == person2.sayName);   // true


 

 

时间:2014-10-21

地点:合肥

引用:《JavaScript高级程序设计》 

相关文章
相关标签/搜索