完全是笔记,99.9%摘录自高程3,方便学习复习。
- 对象认识
- “类”
- 原型
- 继承
一、对象认识
最原始的创建对象方式:
1 var person = new Object(); //new一个Object对象 person 2 person.name = "hank"; //给person添加属性name,初值为hank 3 person.age = 28; //.. 4 person.job = "eat"; //... 5 person.sayName = function(){ //给person添加方法sayName 6 alert(this.name); 7 }
看上去有点笨重,后来就流行字面量了:
1 var person{ 2 name : "hank", 3 age : 28, 4 job : "eat", 5 sayName : function(){ 6 alert(this.name); 7 } 8 }
以上两种的缺点:如果创建很多对象,就会产生大量的重复代码。所以,开始有“类”的出现了。一个类,想要多少实例,自己new。
二、类(意义上的)
1.工厂模式
ECMAScript无法创建类。那么,就用一个函数将第一种封装起来。如此这般,可通过调用ceartPerson来创建多个相似对象。但是无法识别对象类型.
1 function creatPerson(name,age,job){ 2 var o = new Object(); //new 3 o.name = name; //添加属性 4 o.age = age; //.. 5 o.job = job; //.. 6 o.sayName = function(){ //添加方法 7 alert(this.name); 8 } 9 return o; //将new好的对象返回 10 }
2.构造函数模式
1 function Person(name, age, job){ 2 //没有new对象 3 this.name = name; //属性和方法赋值给this对象 4 this.age = age; 5 this.job = job; 6 this.sayName = function(){ 7 alert(this.name); 8 }; 9 //没有return 10 } 11 var person1 = new Person("Nicholas", 29, "Software Engineer"); 12 var person2 = new Person("Greg", 27, "Doctor");
person1和person2分别保存着Person的一个不同的实例。都有一个constructor(构造函数)属性,指向Person“person1.constructor == Person
”。如此这般之后,就能instanceof出构造函数(对象类型)而不都是Object了。
-调用方式
1 // 当作构造函数使用 2 var person = new Person("Nicholas", 29, "Software Engineer"); 3 person.sayName(); //"Nicholas" 4 // 作为普通函数调用 5 Person("Greg", 27, "Doctor"); // 添加到 window 6 window.sayName(); //"Greg" 7 // 在另一个对象的作用域中调用 8 var o = new Object(); 9 Person.call(o, "Kristen", 25, "Nurse");// 添加到 o 10 o.sayName(); //"Kristen"
缺点:每个方法都要在每个实例上重新创建一遍。不同实例上的同名函数是不相等的,所以alert(person1.sayName == person2.sayName); //false
。通过把函数定义转移到构造函数外部来解决这个问题
1 function Person(name, age, job){ 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 this.sayName = sayName; 6 } 7 function sayName(){ 8 alert(this.name); 9 } 10 var person1 = new Person("Nicholas", 29, "Software Engineer"); 11 var person2 = new Person("Greg", 27, "Doctor"); 12 alert(person1.sayName == person2.sayName); //true
可是新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。好在,这些问题可以通过使用原型模式来解决。
三、原型
每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象。这个对象使得所有实例共享属性和方法。
1 function Person(){ 2 } 3 Person.prototype.name = "Nicholas"; 4 Person.prototype.age = 29; 5 Person.prototype.job = "Software Engineer"; 6 Person.prototype.sayName = function(){ 7 alert(this.name); 8 }; 9 var person1 = new Person(); 10 person1.sayName(); //"Nicholas" 11 var person2 = new Person(); 12 person2.sayName(); //"Nicholas" 13 alert(person1.sayName == person2.sayName); //true
和构造函数模式不同的是,实例共享属性方法。实例访问的实例和方法都是同一个。
查找对象属性:在对象实体中找,找不到就到原型对象中找。能访问,但是不能修改。即,实例中添加同名属性后,只是在该实例中存在。
1 function Person(){ 2 } 3 Person.prototype.name = "Nicholas"; 4 Person.prototype.age = 29; 5 Person.prototype.job = "Software Engineer"; 6 Person.prototype.sayName = function(){ 7 alert(this.name); 8 }; 9 var person1 = new Person(); 10 var person2 = new Person(); 11 person1.name = "Greg"; 12 alert(person1.name); //"Greg" ——来自实例 13 alert(person2.name); //"Nicholas" ——来自原型 14 delete person1.name; 15 alert(person1.name); //"Nicholas"——来自原型
使用 hasOwnProperty() 方法可以检测一个属性是存在于实例中,还是存在于原型中.只在给定属性存在于对象实例中时,才会返回 true。
in的话,是分不清在属性存在于原型还是实例中的。因此只要 in 操作符返回 true 而 hasOwnProperty() 返回 false,就可以确定属性是原型中的属性,下面封装一个方法:
1 function hasPrototypeProperty(object, name){ 2 //属性是原型中的属性 3 return !object.hasOwnProperty(name) && (name in object); 4 }
用字面量写法,让原型模式更简洁
1 function Person(){ 2 } 3 Person.prototype = { 4 name : "Nicholas", 5 age : 29, 6 job: "Software Engineer", 7 sayName : function () { 8 alert(this.name); 9 } 10 };
这样写后,constructor就不会再指向Person,因为重写了Person.prototype。需要重新指定constructor的指向。
1 function Person(){ 2 } 3 Person.prototype = { 4 constructor : Person, 5 name : "Nicholas", 6 age : 29, 7 job: "Software Engineer", 8 sayName : function () { 9 alert(this.name); 10 } 11 };
原型动态性,实例与原型之间的链接是一个指针,而非一个副本。所以,原型上的任何修改都能立即在实例上表现出来,即使是实例创建在先。
1 var friend = new Person(); 2 Person.prototype.sayHi = function(){ 3 alert("hi"); 4 }; 5 friend.sayHi(); //"hi" (没有问题!)
但是如果是重写整个原型对象(字面量形式),那就不一样了(先实例后原型)
1 function Person(){ 2 } 3 var friend = new Person(); 4 Person.prototype = { 5 constructor: Person, 6 name : "Nicholas", 7 age : 29, 8 job : "Software Engineer", 9 sayName : function () { 10 alert(this.name); 11 } 12 }; 13 friend.sayName(); //error
原型模式的缺点,对于包含引用类型值的属性来说,某个实例的属性赋值,会影响到所有实例。
1 function Person(){ 2 } 3 Person.prototype = { 4 constructor: Person, 5 name : "Nicholas", 6 age : 29, 7 job : "Software Engineer", 8 friends : ["Shelby", "Court"], 9 sayName : function () { 10 alert(this.name); 11 } 12 }; 13 var person1 = new Person(); 14 var person2 = new Person(); 15 //person1.friends = [];//如果先创建的话,那么这个friends就是person1的,但是这样就没法保留原型中的值。 16 person1.friends.push("Van");//这个friends是原型的。 17 alert(person1.friends); //"Shelby,Court,Van" 18 alert(person2.friends); //"Shelby,Court,Van" 19 alert(person1.friends === person2.friends); //true
所以开始引入构造函数+原型模式 :构造函数模式用于指定实例属性,原型模式用于定义方法和共享的属性。
1 function Person(name, age, job){ 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 this.friends = ["Shelby", "Court"]; 6 } 7 Person.prototype = { 8 constructor : Person, 9 sayName : function(){ 10 alert(this.name); 11 } 12 } 13 var person1 = new Person("Nicholas", 29, "Software Engineer"); 14 var person2 = new Person("Greg", 27, "Doctor"); 15 person1.friends.push("Van"); 16 alert(person1.friends); //"Shelby,Count,Van" 17 alert(person2.friends); //"Shelby,Count" 18 alert(person1.friends === person2.friends); //false 19 alert(person1.sayName === person2.sayName); //true
最完美的模式来了,动态原型模式。不能使用对象字面量重写原型。和构造函数+原型的区别是,new的时候才会给原型添加属性。
1 function Person(name, age, job){ 2 //属性 3 this.name = name; 4 this.age = age; 5 this.job = job; 6 //方法 7 if (typeof this.sayName != "function"){ 8 Person.prototype.sayName = function(){ 9 alert(this.name); 10 }; 11 } 12 } 13 var friend = new Person("Nicholas", 29, "Software Engineer"); 14 friend.sayName();
四、继承
最完美的继承:寄生组合式继承
1 function object(o){ 2 function F(){} 3 F.prototype = o; 4 return new F(); 5 } 6 function inheritPrototype(subType, superType){ 7 var prototype = object(superType.prototype); //创建对象 8 prototype.constructor = subType; //增强对象 9 subType.prototype = prototype; //指定对象 10 } 11 function SuperType(){ 12 this.property = true; 13 this.arr = [1,2,3]; 14 } 15 SuperType.prototype.getSuperValue=function(){ 16 return this.property; 17 } 18 function SubType(){ 19 SuperType.call(this); 20 this.subproperty = false; 21 } 22 inheritPrototype(SubType,SuperType); 23 24 SubType.prototype.getSubValue=function(){ 25 return this.subproperty; 26 } 27 var o = new SubType(); 28 console.log(o);