建立对象
Object 组织函数或对象字面量都可以用来建立单个对象。但这个要领的瑕玷异常显著:统一个接口建立很可耐多对象会发生大批的反复代码。为了处理这个题目,人们最先运用工场形式的一种变体。
工场形式(摒弃,不引荐)
这个形式没有处理对象辨认的题目(即如何晓得一个对象的范例)。如:
详细的建立单个对象:
var person = {};
person.name = "Oliver";
person.age = 18;
person.sayName = function(){
return this.Name;
};
改变成工场形式:
function createPerson(name,age){
var obj = {};
obj.name = name;
obj.age = age;
obj.sayName = function(){
return this.name
};
return obj; //注重这里要返回obj 对象,如许才能把obj 对象传给createPerson 变量。
}
var newPerson = createPerson("Oliver",18);
组织函数形式
组织函数可以建立特定范例的对象。所以,可以建立自定义的组织函数,从而定义自定义对象范例的属性和要领。如:
function Person(name,age){ //注重大小写,组织函数应当把第一个字幕大写化
this.name = name;
this.age = age;
this.sayName = function (){
return this.name;
};
}
var newPerson = new Person("Oliver",18);
document.write(newPerson.name); //Oliver
document.write(newPerson.age); //18
document.write(newPerson.sayName()); //Oliver
确切相称轻易
这里一定要记着,组织函数都应当以一个大写字母开首,用来辨别其他的函数
又如:
function Person(name,age){ //注重大小写,组织函数应当把第一个字幕大写化
this.name = name;
this.age = age;
this.sayName = function (){
return this.name;
};
}
var person1 = new Person("Oliver",18);
var person2 = new Person("Troy",24);
document.write(person1.constructor == Person); //true
document.write(person2.constructor == Person); //true
这里的person1 和person2 离别保存着Person 的一个差别的实例。两个对象都有一个constructor(组织函数)属性,该属性指向Person。
在上面这个例子中,person1 和person2 等于Object 的实例,同时也是Person 的实例。可以经由过程instanceof 操作符来考证:
console.log((person1 instanceof Object) && (person2 instanceof Person)); //true
以这类体式格局建立组织函数是定义在Global 中的(window 对象)
将组织函数当作函数
任何函数,只需经由过程new 操作符来挪用,那它就可以坐位组织函数;而任何函数,假如不经由过程new 操作符来挪用,那它就跟平常函数没区分。如下面这个组织函数:
function Car(name,color,sound){
this.name = name;
this.color = color;
this.sound = function(){
return sound;
};
console.log(this.name + " " + this.color + " " + this.sound());
}
假如当作组织函数来运用:
var benz = new Car("C200","White","Boom Boom"); //C200 White Boom Boom
假如作为平常函数来挪用:
Car("Benz","White","Boom!"); //Benz White Boom!
console.log(window.name + window.color + window.sound()); //BenzWhiteBoom!
假如在另一个对象的作用域中挪用:
var cars = {};
Car.call(cars,"Benz","White","Boom Boom!");
document.write(cars.sound()); //Boom Boom!
组织函数的题目
题目是每一个要领都要在每一个实例是从新建立一遍。可用经由过程把内部的函数转移到外部来处理这些题目。如:
function Car(name,color){
this.name = name;
this.color = color;
this.show = show;
}
function show(){
console.log(this.name + this.color);
}
var benz = new Car("Benz","white");
benz.show(); //Benzwhite
但这个题目是完整没有了封装性可言。不过可以经由过程原型形式来处理。
原型形式
function Person(){};
Person.prototype.name = "Oliver";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
person1.sayName(); //Oliver
var person2 = new Person();
person2.sayName(); //Oliver;
console.log(person1.sayName == person2.sayName); //true
与组织函数差别的是,新对象的这些属性和要领是由一切实例同享的。这里两个新的person 接见的都是统一组属性和统一个sayName() 函数。
明白原型对象
以上面的Person 为例,Person 组织函数内里存在一个prototype 属性,这个属性指向原型对象Person Prototype,该Person Prototype 内里包括了constructor 属性,该属性又指向组织函数Person。组织函数的实例包括了一个[[Prototype]]的内部属性,该内部属性则指向Person Prototype。如:
function Person(){};
Person.prototype.name = "Oliver";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
person1.sayName(); //Oliver
var person2 = new Person();
person2.sayName(); //Oliver;
console.log(Person.prototype);
/*
age: 18
constructor: function Person() {}
name: "Oliver"
sayName: function () {
__proto__: Object
*/
console.log(Person.prototype.constructor);
//function Person() {}
console.log(Object.getPrototypeOf(person1));
/*
age: 18
constructor: function Person() {}
name: "Oliver"
sayName: function () {
__proto__: Object
*/
关于组织函数、原型属性以及实例之间的关联,拜见《js高等程序设计》一书中第6.2.3 章节。
两个要领:isPrototypeOf()
和Object.getProtytypeOf()
(ECMAScript 5)。前者是用来肯定[[Prototype]];后者是用来返回[[Prototype]]值。如:
console.log(Person.prototype.isPrototypeOf(person1)); //true
console.log(Object.getPrototypeOf(person1).name); //Oliver
console.log(Object.getPrototypeOf(person1));
/*
age: 18
constructor: function Person() {}
name: "Oliver"
sayName: function () {
__proto__: Object
*/
为对象增加一个属性时,这个属性会屏障原型对象中的同名属性,然则并不会修正谁人属性。假如运用delete 删除这个属性,就可以从新接见原型中的属性。如:
function Person(){};
Person.prototype.name = "Oliver";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
person1.sayName(); //Oliver 原型中的Name
person1.name = "Troy";
person1.sayName(); //Troy 实例中的Name
delete person1.name;
person1.sayName(); //Oliver 原型中的Name
每次读取某个对象的某个属性,都邑从实例自身最先搜刮,假如没有找到给定名字的属性,则会在原型对象中再次搜刮。
要领hasOwnProperty()
检测属性假如在对象实例中时,返回true。如:
console.log(person1.hasOwnProperty("age")); //false age属性来自于原型
console.log(person1.hasOwnProperty("name")); //true name属性来自于实例
原型与in 操作符
两种要领运用in 操作符:零丁运用和for-in 轮回中运用。
零丁运用时,in 返回true 申明该属性存在于实例或原型中。如:
function Person(){};
Person.prototype.name = "Oliver";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
person1.name = "Troy";
person1.sayName(); //Troy 实例中的Name
console.log("name" in person1); //true name属性在实例或原型中
console.log(person1.hasOwnProperty("name")); //true name属性在实例中
//上面两个就申明name属性一定在实例中
又如:
function Person(){};
Person.prototype.name = "Oliver";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
person1.name = "Troy";
person1.sayName(); //Troy 实例中的Name
var person2 = new Person();
console.log("name" in person1); //true name属性在实例或原型中
console.log(person1.hasOwnProperty("name")); //true name属性在实例中
//上面两个就申明name属性一定在实例中
console.log("name" in person2); //true
console.log(person2.hasOwnProperty("name")); //false
//上面两个申明name属性一定在原型中
自定义一个函数hasPrototypeProperty(object,name)
;即同时运用上面两个要领来肯定属性究竟是不是是存在于实例中。如:
function Person(){};
Person.prototype.name = "Oliver";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
person1.name = "Troy";
person1.sayName(); //Troy 实例中的Name
var person2 = new Person();
function hasPrototypeProperty(object,name){
console.log((name in object) && !(object.hasOwnProperty(name)))
}
hasPrototypeProperty(person2,"name"); //true name属性是在原型中
hasPrototypeProperty(person1,"name"); //false name属性是在实例中
用Object.defineProperty()
要领定义的属性:
function Person(){};
Person.prototype.name = "Oliver";
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
Object.defineProperty(person1, "age", {
value: 18
})
console.log(person1.hasOwnProperty("age")); //true age属性是实例属性
关于for-in、[[enumerable]]、defineProperty、hasOwnProperty 的例子:
var person1 = {
age: 18
};
Object.defineProperty(person1, "name", {
value: "Oliver",
enumerable: true
})
for(var x in person1){
console.log(x);
}
console.log(person1.hasOwnProperty("name")); //true
又如:
function Person(age){
this.age = age;
}
var person1 = new Person(18);
Object.defineProperty(person1, "name", {
value: "Oliver",
enumerable: false
})
for(var x in person1){
console.log(x); //用defineProperty 定义的name 属性是实例属性,这里不会罗列到
}
console.log(person1.hasOwnProperty("name")); //true
又如:
function Person(){};
Person.prototype.age = 18;
var person1 = new Person();
Object.defineProperty(person1, "name", {
value: "Oliver",
enumerable: false
})
for(x in person1){
console.log(x); //这里依然不回罗列到自定义的name 实例属性
}
然则:
function Person(){};
Person.prototype.age = 18;
Person.prototype.name = "Oliver";
var person1 = new Person();
Object.defineProperty(person1, "name", {
enumerable: false
})
for(x in person1){
console.log(x); //这里则返回罗列到自定义的name 原型属性
}
原型属性的[[enumerable]]设置为false,挪用for-in 依然可以被罗列到。
别的,Object.keys()
要领可以返回一切可罗列属性的字符串数组:
function Person(){};
Person.prototype.age = 18;
Person.prototype.name = "Oliver";
var person1 = new Person();
Object.defineProperty(person1, "sound", {
value: "miao~",
enumerable: true //可罗列
});
Object.defineProperty(person1, "sound2", {
value: "wang~",
enumerable: false //不可罗列
});
console.log(Object.keys(Person.prototype)); //["age", "name"]
console.log(Object.keys(person1)); //["sound"]
Object.getOwnPropertyName()
要领,则可以返回不管能否罗列的一切实例属性:
function Person(){};
Person.prototype.age = 18;
Person.prototype.name = "Oliver";
var person1 = new Person();
Object.defineProperty(person1, "sound", {
value: "miao~",
enumerable: true //可罗列
});
Object.defineProperty(person1, "sound2", {
value: "wang~",
enumerable: false //不可罗列
});
console.log(Object.keys(Person.prototype)); //["age", "name"]
console.log(Object.keys(person1)); //["sound"]
console.log(Object.getOwnPropertyNames(Person.prototype)); //["constructor", "age", "name"]
console.log(Object.getOwnPropertyNames(person1)); //["sound","sound2"]
更简朴的原型语法
即字面量要领:
function Person(){};
Person.prototype = {
name: "Oliver",
age: 18,
sayName: function(){
console.log(this.name);
}
};
var person1 = new Person();
console.log(Person.prototype.constructor); //不再指向Person()组织函数
function People(){};
People.prototype.name = "Troy";
People.prototype.age = 26;
People.prototype.sayName = function(){
console.log(this.name);
};
var people1 = new People();
console.log(People.prototype.constructor); //这里则指向People()组织函数
上面第一种就是字面量要领。然则由此带来的题目是,他的原型对象中的constructor 属性将不再指向上个例子中的Person() 组织函数了。(实在我们本质上是重写了prototype对象)
假如constructor 值真的异常重要,则只须要把它设置回恰当的值就可以了:
function Person(){};
Person.prototype = {
constructor: Person,
name: "Oliver",
age: 18,
sayName: function(){
console.log(this.name);
}
};
var person1 = new Person();
console.log(Person.prototype.constructor); //从新指向Person()组织函数
function People(){};
People.prototype.name = "Troy";
People.prototype.age = 26;
People.prototype.sayName = function(){
console.log(this.name);
};
var people1 = new People();
console.log(People.prototype.constructor); //这里则指向People()组织函数
但是用字面量的要领致使的题目依然没有结束,以上面这类体式格局重设constructor 属性会致使[[Enumerable]]特征被设置为true。因此在支撑ECMAScript 5 的js 引擎中可以用Object.defineProperty()
要领把它修正为false:
function Person(){};
Person.prototype = {
constructor: Person,
name: "Oliver",
age: 18,
sayName: function(){
console.log(this.name);
}
};
var person1 = new Person();
console.log(Person.prototype.constructor);
for (var x in person1){
console.log(x); //这里会涌现constructor,然则我们实际上不应当让他可以被罗列出
}
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false
});
for (var x in person1){
console.log(x); //这里就不会涌现constructor 了,然则这类要领只支撑ECMAScript 5的js 引擎
}
/*
[Log] function Person() {} (repetition.html, line 130)
[Log] constructor (repetition.html, line 132)
[Log] name (repetition.html, line 132)
[Log] age (repetition.html, line 132)
[Log] sayName (repetition.html, line 132)
[Log] name (repetition.html, line 140)
[Log] age (repetition.html, line 140)
[Log] sayName (repetition.html, line 140)
*/
原型的动态性
我们对原型对象所做的任何修正都能马上从实例上回响反映出来。由于实例与原型之间的链接只不过是一个指针而不是副本:
function Person(){};
var person = new Person(); //person在Person()组织函数修正之前建立的
Person.prototype.name = "Oliver";
console.log(person.name); //依然会涌现及时的变化
然则假如重写了prototype 则就差别了,由于实例的[[Prototype]]会指向原型对象,假如修正了本来的原型对象,则就是切断了组织函数与最初原型之间的联络:
function Person(){};
var person = new Person();
Person.prototype = { //这里重写了Person.prototype,属于新的Person.prototype
constructor: Person,
name: "Oliver"
}
console.log(person.name); //原型对象被修正了,指针依然指向旧的Person.prototype
从这里就可以很显著看出,Person.prototype={},实际上字面量要领就是重写了原型对象。假如是Person.prototype.name=”Oliver”,则并非重写而是修正,不会建立“新的原型对象。”
《js高等程序设计》一书中6.2.3 中的图6-3 很清晰的形貌了该道理
原生对象的原型
一切原生的援用范例(Object、Array、String等等)都在其组织函数的原型上定义了要领。同时,我们也可以给原生对象自定义要领:
var array = new Array();
Array.prototype.name = function(){
console.log("Array")
};
array.push("hello ","there");
console.log(array);
array.name();
也可以修正:
var array = new Array();
Array.prototype.toString = function(){
return("Array")
};
array.push("hello ","there");
console.log(array.toString());
//如许就抹去了toString()要领
猛烈不引荐修正和重写原生对象的原型
原型对象的题目
就是包括援用范例值的属性来讲,题目比较严重。详细体现在原型中的属性被实例同享:
function Person(){};
Person.prototype = {
constructor: Person,
name: "Oliver",
age: 18,
friends: ["Troy","Alice"]
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Ellen");
console.log(person1.friends); //["Troy", "Alice", "Ellen"]
console.log(person2.friends); //["Troy", "Alice", "Ellen"]
//二者完整相同,由于原型中的该属性被实例所同享,push()要领只是修正了person1.friends,没有重写
person1.friends = ["Troy", "Alice"];
console.log(person1.friends); //["Troy", "Alice"]
console.log(person2.friends); //["Troy", "Alice", "Ellen"]
//虽然可以经由过程重写和掩盖来处理该题目,然则依然异常贫苦
console.log(person1.hasOwnProperty("friends")); //true;
console.log(person2.hasOwnProperty("friends")); //false;
//这里就可以看到,重写能处理题目是由于重写致使它建立了实例属性"friends"
这里可以看出,假如我们的初志像如许只是想建立一个同享的数组,那末固然不会有什么题目;然则实例平常都邑有本身的属性,所以不应当零丁运用原型形式。而是组合运用组织函数形式和原型形式。
组合运用组织函数形式和原型形式
这是一种用来定义援用范例的一种默许形式:
function Person(name,age){
this.name = name;
this.age = age;
this.friends = [];
} //独享的部份
Person.prototype = {
constructor: Person,
sayName: function(){
return this.name;
}
} //同享的部份
var person1 = new Person("Oliver",18);
var person2 = new Person("Troy",24);
person1.friends.push("Alice","Mark");
person2.friends.push("Mac");
console.log(person1.friends.toString());
console.log(person2.friends.toString());
/*
[Log] Alice,Mark (repetition.html, line 228)
[Log] Mac (repetition.html, line 229)
*/
动态原型形式
可以经由过程搜检某个应当存在的要领是不是有用,来决议是不是须要初始化原型:
function Person(name,age){
this.name = name;
this.age = age;
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
return (this.name);
};
}
}
var person = new Person("Oliver",18);
console.log(person.sayName()); //Oliver
实际上就是把下面代码封装在了组织函数中:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
return(this.name);
};
var person = new Person("Troy",24);
console.log(person.sayName()); //Troy
寄生组织函数形式
世纪撒好惆怅跟工场形式一样。发起在可以运用其他形式的情况下,不要运用该形式。
稳妥组织函数形式
稳妥对象,指的是没有大众属性,且其要领也不援用this 的对象如:
function Person(name,age){
var obj = new Object();
obj.sayName = function(){
console.log(name);
};
return obj;
}
var person1 = Person("Oliver",18);
person1.sayName(); //Oliver