Javascript高级程序设计3笔记 - 对象

 

完全是笔记,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);

 

posted @ 2015-07-14 11:32  汇品铺子  阅读(280)  评论(0编辑  收藏  举报