JS对象的理解

《JavaScript高级程序设计》是一本好书,下面总结一下书中关于原型链的讲解。

本文从对象的创建开始,逐渐引出原型链的背景、作用以及ECMA对原型链的定义
1、理解对象
两种常用的创建对象的方法之一是new一个Object对象,并为它添加属性和事件

var person = new Object();
    person.name = "wzk";
    person.age = 24;
    person.sayName = function(){
  alert(this.name);
};

另一种是使用对象字面量创建对象

var person = {
  name : "wzk",
  age : 24;
  sayName : function(){
      alert(this.name);
  }
}

以类似键值对的形式给出每个变量或者函数的定义,注意以逗号分隔,最后一个属性不需要加逗号
两种方法纯粹是写法不同,首推后者。
以上两种方法适合创建单个对象,创建多个对象将造成代码重复。

2、用接口创建对象
2.1、工厂模式

function createPerson(name ,age){
  var o = new Object();
  o.name = name;
  o.age = age;
  o.sayName = function(){
    alert(this.name);
  }
  return 0;
}

var person1("wzk",24);
var person2("cxl","18");

实际上就是上述第一种方法套在函数中的写法
缺点是无法利用instanceof函数识别创建出来的对象

 

2.2、构造函数模式(惯例首字母大写,与普通函数区别开)

function Person(name,age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    alert(this.name);
  }
}
var person1 = new Person("wzk",24);
var person2 = new Person("cxl",18);

与工厂模式的区别:
1、没有创建Object对象
2、属性方法直接赋给了this对象
3、没有return

实际上,上述函数看起来就像一个普通函数,this在浏览器中就是指window对象
但是使用new创建,就区别于普通函数,成为构造函数
所以,所有构建对象的动作都是由new做的,动作如下:
1、创建一个新对象
2、将构造函数的作用域赋给创建的新对象,即this指向该新对象
3、为新对象添加属性、方法
4、返回这个新对象

可以使用instanceof判断对象类型

alert(person1 instanceof Person); //true
alert(person2 instanceof Person); //true

构造函数的缺点:
由于ECMAScript中,函数是对象
所以实例person1与person2中的sayName()方法是两个
不同的对象,他们被重复创建,而功能却是一样的。

解决办法:
将sayName()函数移到构造函数外,使构造函数内的sayName函数
指向构造函数外的函数,但带来的新问题:
1、只能被特定对象调用
2、封装性被破坏
下面,终于引出了原型模式解决该问题

原型模式:
函数即对象,在我们创建每一个函数时,每个函数都会自动生成
一个prototype(原型)属性。prototype属性指向一个对象,
该函数的所有实例可以共享该对象的属性和方法。
构造函数也是函数,所以由构造函数生成的每一个对象实例都可以通过
prototype访问这个对象的属性和方法。
prototype指向的这个对象即为该函数的原型对象。

function Person(){

}

Person.prototype.name = "wzk";
Person.prototype.age = 24;

Person.prototype.sayName = function(){
    alert(this.name);
}

var person1 = new Person();
var person2 = new Person();
person1.sayName();     //wzk
person2.sayName();     //wzk
alert(person1.sayName == person2.sayName); //true

原型对象:
当创建函数的时候,就会为该函数创建一个prototype属性
,该属性指向函数的原型对象。同时圆形对象会获得一个
constructor(构造函数)属性,指回函数。
即Person.prototype = 原型对象
Person.prototype.constructor = Person

也正是因为这个constuctor属性,person1 instanceof Person才会返回true;

 

如上图所示,实例person1、与person2都包含一个_proto_的内部属性,
与构造函数共同指向这个原型对象。


虽然person1与person2都不包含属性和方法,
但是却可以调用sayName()函数,是通过查找原型对象
上的属性实现的。

当person1调用sayName()方法时,解释器会先在实例对象中
搜索是否有这个函数,没有则继续搜索实例对象指向的原型对象,
有则执行原型的sayName()方法,没有则报错。

注意:
1、实例如果有和原型对象同名的属性,则会覆盖原型对象上的那个属性。
2、通过delete实例上的同名属性,可以重新访问原型对象上的那个属性。
3、实例调用hasOwnProperty可以检测属性是实例本身的,还是来自原型对象的。
person1.hasWonProperty("name") //true则来自本身
//false则来自原型
4、单独使用in操作符,如:"name" in person1,
不论name属性在实例中还是在原型上,只要能访问到,
都返回true
5、for-in循环也可以访问到实例中和原型中的属性,但是
该属性必须是可枚举的([Enumberable]不为false)
6、Object.keys()方法只返回对象本身的所有实例,不包括
原型对象上的函数,即使属性不可枚举。
Object.keys(Person.prototype)会返回constructor对象以及后来添加的属性
object.keys(person1)会返回自己本身的属性,不包括原型对象上的属性
7、字面量写法

Person.prototype = {
    constructor : Person,
    name : "wzk",
    age : 24
};

constructor对象不能漏写,不然instanceof会无效。
8、原型的动态性

var person3 = new Person();

Person.prototype.sayName = function(){
    alert("wzk");
}

person3.sayName(); //"wzk"

先创建实例,后给原型对象添加属性,实例也认识。
其实说白了就是person3的_proto_内部属性始终指向
原型对象,执行person3的sayName()方法,先搜索实例
本身是否存在该方法,本身没有,接着搜索原型对象
当然可以搜索到后添加的这个方法。

9、function Person(){

}

var person4 = new Person();
Person.prototype = {
    constructor:Person,
    name:"wzk",
    age:24,
    sayName:function(){}
}

这种写法,person4将访问不到原型后添加的属性和方法,
因为原本person4和Person指向同一个原型对象,但构造函数
Person由擅自重新定义了prototype指向的对象,切断了与
原来的原型对象的联系,所以person4也访问不到新的原型对象。

但是如果这时重新创建一个person5,则又可以访问到后添加的
这些属性方法了。
10、原生对象的原型
即可以在Array.prototype中找到sort、push、concat等方法
可以在String.prototype中找到substring、match、replace等方法

也可以给原生对象添加自己的新方法(不推荐)
String.prototype.startPos = function(str){
return this.indexOf(str);
}
11、原型对象优点与缺点
优点:1、函数对象不必重复定义,所有实例共享
2、由于实例的_proto_不可见,如果属性重名了则以实例的为准,
保证了原型对象上包含基本值的属性,不会被随便修改
缺点:如果是引用类型的属性,则容易造成原型对象上的属性被某个实例修改
例:Person.prototype指向原型对象上有一个数组,因为数组本身是引用类型的,
这时Person的一个实例person1为数组添加了一个元素,则会反应到所有实例中,
破坏了对象的封装性。

针对原型对象的缺点,只需要将构造函数模式和原型模式结合起来。
将共享的属性和方法定义在原型中,
将实例属性用构造函数来构造。

还有包括动态原型模式、寄生构造函数模式、稳妥构造函数模式等几种变形,书中均有讲解。

 

posted @ 2016-01-14 23:25  olaf  阅读(653)  评论(0编辑  收藏  举报