深刻探究js的原型与原型链

最近在看《javascript高级程序设计》,看完以后,以为感触,而后我今天又看到了一篇文章,说的很搞笑。就想整理下本身所学的。javascript

首先,若是咱们把ECMAScript的对象想象为散列表,即一组名值对,其中值能够是数据或函数。java

那究竟对象、原型对象、构造函数、继承、原型链、 原型属性的共享、原型的动态性、原型的总体重写呢,来一组简单粗暴的描述哈,仍是挺搞笑的。数组

1)人是人他妈生的,妖是妖他妈生的。人和妖都是对象实例,而人他妈和妖他妈就是原型。原型也是对象,叫原型对象。函数

2)人他妈和人他爸啪啪啪能生出一堆人宝宝、妖他妈和妖他爸啪啪啪能生出一堆妖宝宝,啪啪啪就是构造函数,俗称造人。测试

3)人他妈会记录啪啪啪的信息,因此能够经过人他妈找到啪啪啪的信息,也就是说能经过原型对象找到构造函数。this

4)人他妈能够生不少宝宝,但这些宝宝只有一个妈妈,这就是原型的惟一性。prototype

5)人他妈也是由人他妈他妈生的,经过人他妈找到人他妈他妈,再经过人他妈他妈找到人他妈他妈……,这个关系叫作原型链。设计

6)原型链并非无限的,当你经过人他妈一直往上找,最后发现你会发现人他妈他妈他妈……的他妈都不是人,也就是原型链最终指向null。对象

7)人他妈生的人会有人的样子,妖他妈生的妖会有妖的丑陋,这叫继承。继承

8)你继承了你妈的肤色,你妈继承了你妈他妈的肤色,你妈他妈……,这就是原型链的继承。

9)你谈对象了,她妈让你带上房产证去提货,你若没有,那她妈会问你妈有没有,你妈没有那她妈会问你妈她妈有没有……这就是原型链的向上搜索。

10)你会继承你妈的样子,可是你也能够去染发洗剪吹,就是说对象的属性能够自定义,会覆盖继承获得的属性。

11)虽然你洗剪吹了染成黄毛了,但你不能改变你妈的样子,你妈生的弟弟妹妹跟你的黄毛洗剪吹没一点关系,就是说对象实例不能改动原型的属性。

12)可是你家被你玩火烧了的话,那就是说你家你妈家你弟们家都被烧了,这就是原型属性的共享。

13)你妈外号阿珍,邻居大娘都叫你阿珍儿,但你妈头发从飘柔作成了金毛狮王后,隔壁大婶都改口叫你包租仔,这叫原型的动态性。

14)你妈爱漂亮,又跑到韩国整形,整到你妈他妈都认不出来,即便你妈头发换回飘柔了,但隔壁邻居仍是叫你金毛狮王子。由于没人认出你妈,整形后的你妈已经回炉再造了,这就是原型的总体重写。

 哈哈,是否是简单粗暴,还特别容易理解。

ECMAScript中有两种属性:数据属性和访问器属性
(1)数据属性:

[[Configurable]] 表示可否经过delete删除属性从而从新定义属性,可否修改属性,或者可否把属性修改成访问器属性
[[Enumerable]] 表示可以经过for-in循环返回属性
[[Writable]]表示可否修改属性的值
[[Value]]包含这个属性的数据值,
修改默认的属性使用 Object.defineProperty() 这个方法

(2)访问器属性:包含getter和setter函数。getter:负责返回有效的值,setter函数传入新值,访问器属性的4个特性:
[[Configurable]]表示可否经过delete删除属性从而从新定义属性,可否修改属性的特性,可否把属性修改成数据属性。默认为true.
[[Enumerable]]表示可否经过for-in循环返回属性。
[[Get]]在读取属性时调用的函数,默认值为undefined
[[Set]]在写入属性时调用的函数,默认值为undefined

访问器的属性用Object.defineProperty()来定义

书上给了一个例子,来挺好理解的:

var book = {
  _year:2004,
  //_year前面的下划线是一种经常使用的记号,**用来表示只能经过对象方法访问的属性**
  edition:1
};
Object.defineProperty(book,"year",{
  get:function(){
    return this._year;
  }
  set:function(newYear){
    if(newValue >2004){
      this._year = newValue;
      this.edition += newValue -2004;
    }
  }
});
//定义访问器的旧有方法
book.__defineGetter__("year",function(){
  return this._year;
})
book.__defineSetter__("year",function(newValue){
  if(newValue >2014){
    this._year = newValue;
    this.edition += newValue -2004;
  }
})

book.year = 2015;
alert(book.edition); //2
读取属性的特性:Object.getOwnPropertyDesciptor()取得给定属性的描述符。

最后说回原型链:原型链继承的主要问题在于属性的共享,不少时候咱们只想共享方法而并不想要共享属性,理想中每一个实例应该有独立的属性。所以,原型继承就有了下面的两种改良方式:

1)组合继承

function Mother(age){

  this.age = age;

  this.bobby = ['running','football']

}

Mother.prototype.showAge = function(){

  console.log(this.age);

};

function Person(name,age){

  Mother.call(this.age);         //第二次执行

  this.name = name;

}

Person.prototype = new Mother();    //第一次执行

Person.prototype.constructor = Person;

Person.prototype.showName = function(){

  console.log(this.name);

}

var p1 = new Person('Jack',20);

p1.hobby.push('basketball'); //p1:'Jack';_proto_:20,['running','football']

var p2 = new Person('Mark',18); //p2:'Mark';_proto_:18,['running','football']

(2)寄生组合式继承

function object(o){

  function F(){}

  F.prototype = o;

  return new F();

}

function inheritPrototype(Person,Mother){

  var prototype = object(Mother.prototype);

  prototype.constructor = Person;

  Person.prototype = prototype;

}

function Mother(age){

  this.age = age;

  this.hobby = ['running','football']

}

Mother.prototype.showAge = function(){

  console.log(this.age);

};

function Person(name,age){

  Mother.call(this,age);

  this.name = name;

}

inheritPrototype(Person,Mother);

Person.prototype.showName = function(){

  console.log(this.name);

}

var p1 = new Person('Jack',20);

p1.hobby.push('backetball');    //p1:'Jack';_proto_:20,['running','football']

var p2 = new Person('Mark',18); //p2:'Mark';_proto_:18,['running','football']

js建立对象的各类方法

(1)原型模式

//1原型模式:对象字面量方式

var person = {

  name : 'Jack',

  age:18,

  sayName:function(){alert(this.name);}

};

//2原型模式:Object构造函数方式

var person = new Object();

person.name = 'Jack';

person.age = 18;

person.sayName = function(){

   alert(this.name); 

};

(2)工厂模式

//工厂模式,定义一个函数建立对象

function creatPerson(name,age){

  var person = new Object();

  person.name = name;

  person.age = age;

  person.sayName = function(){

    alert(this.name);

  };

  return person;

}

工厂模式就是批量化生产,简单调用就能够进入造人模式(啪啪啪。。。)指定姓名年龄就能够造一堆小宝宝啦,解放双手。可是因为是工厂暗箱操做的,因此你不能识别这个对象究竟是什么类型、是人仍是狗傻傻分不清(instanceof 测试为 Object),另外每次造人时都要建立一个独立的temp对象,代码臃肿

(3)构造函数

//3构造函数模式,为对象定义一个构造函数

function Person(name,age){

  this.name = name;

  this.age = age;

  this.sayName = function(){

    alert(this.name);

  };

}

var p1 = new Person('Jack',18);    //建立一个p1对象

Person('Jack',18);

构造函数与C++、JAVA中类的构造函数相似,易于理解,另外Person能够做为类型识别(instanceof 测试为 Person 、Object)。可是全部实例依然是独立的,不一样实例的方法实际上是不一样的函数。这里把函数两个字忘了吧,把sayName当作一个对象就好理解了,就是说张三的 sayName 和李四的 sayName是不一样的存在,但显然咱们指望的是共用一个 sayName 以节省内存。

(4)原型模式

//原型模式,直接定义prototype属性

function Person(){}

Person.prototype.name = 'Jack';

Person.prototype.age = 18;

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

//原型模式。字面量定义方式

function Person(){}

Person.prototype = {

  name:'Jack',

  age:18,

  sayName:function(){alert(this.name);}

};

var p1 = new Person();  //name = 'Jack'

var p2 = new Person(); //name = 'Jack'

这里须要注意的是原型属性和方法的共享,即全部实例中都只是引用原型中的属性方法,任何一个地方产生的改动会引发其余实例的变化。

 (5)混合模式

//原型构造函数组合模式

function Person(name,age){

  this.name = name;

  this.age = age;

}

Person.prototype = {

  hobby:['running','football'];

  sayName:function(){alert(this.name);},

  sayAge:function(){alert(this.age);}

}

var p1 = new Person('Jack',20);

var p2 = new Person('Mark',18);

作法是将须要独立的属性方法放入构造函数中,而能够共享的部分则放入原型中,这样作能够最大限度节省内存而又保留对象实例的独立性。

相关文章
相关标签/搜索