Javascript原型链的原理详解


Posted in Javascript onJanuary 05, 2016

本文实例分析了Javascript原型链的原理。分享给大家供大家参考,具体如下:

一、JavaScript原型链

ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。在JavaScript中,用 __proto__ 属性来表示一个对象的原型链。当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止!

比如现在有如下的代码:

扩展Object类,添加Clone和Extend方法

/*扩展Object类,添加Clone,JS实现克隆的方法*/
Object.prototype.Clone = function(){
  var objClone;
  if (this.constructor == Object){
    objClone = new this.constructor(); 
  }else{
    objClone = new this.constructor(this.valueOf()); 
  }
  for(var key in this){
    if ( objClone[key] != this[key] ){ 
      if ( typeof(this[key]) == 'object' ){ 
        objClone[key] = this[key].Clone();
      }else{
        objClone[key] = this[key];
      }
    }
  }
  objClone.toString = this.toString;
  objClone.valueOf = this.valueOf;
  return objClone; 
}
/*扩展Object类,添加Extend方法来实现JS继承, 目标对象将拥有源对象的所有属性和方法*/
Object.prototype.Extend = function (objDestination, objSource) {
  for (var key in objSource) {
    if (objSource.hasOwnProperty(key) && objDestination[key] === undefined) {
      objDestination[key] = objSource[key];
    }
  }
  return objDestination;
}

定义Person类

/*定义一个Person类*/
 function Person(_name,_age){
   this.name = _name;
   this.age = _age;
}

在JavaScript中,Object类是所有类的父类,所以Person类从Object类继承,继承了Object类的所有public属性和public方法,包括Object类新添加的Clone和Extend方法

可以用如下的代码证明,Person类确实是继承了Object类

document.write("<pre>");
var p = new Person("孤傲苍狼",24);//创建一个人,名字是孤傲苍狼
var cloneP = p.Clone();//p调用在Object类中定义的Clone方法来克隆自己,如果能得到一个cloneP,那就证明了Person类确实是继承了Object类,所以就拥有了Clone
document.writeln("p是使用Person类以构造函数的方式创建出来的对象,p.name = "+p.name+",p.age = "+p.age);
document.writeln("cloneP是p调用Clone方法克隆出来的对象,cloneP.name = "+cloneP.name+",cloneP.age = "+cloneP.age);
document.writeln("cloneP对象和p对象是两个相互独立的对象,这两个对象的内存地址肯定是不相等,p == cloneP的结果是:"+(p == cloneP));
cloneP.name="白虎神皇";//修改cloneP的名字
document.writeln("cloneP的name被修改了,cloneP.name = "+cloneP.name);
document.writeln("cloneP的name修改了,但是不影响到p,p.name = "+p.name);
document.write("</pre>");

运行结果:

Javascript原型链的原理详解

那么Person类通过神马方式来继承Object类的呢,是使用原型(prototye)的方式继承的:

/*定义一个Person类*/
function Person(_name,_age){
   this.name = _name;
   this.age = _age;
}
Person.prototype = new Object();//让Person类继承Object类

由于JavaScript规定,任何类都继承自Object类,所以"Person.prototype = new Object();//让Person类继承Object类"即使我们不写,我猜想JavaScript引擎也会自动帮我们加上这句话,或者是使用"Person.prototype = Object.prototype;"这种方式,让Person类去继承Object类。"Person.prototype = new Object();",其实这样就相当于Object对象是Person的一个原型,这样就相当于了把Object对象的属性和方法复制到了Person上了。

二、new运算符是如何工作的

我们先看看这样一段代码:

var p  = new Person("孤傲苍狼",24);//创建一个人,名字是孤傲苍狼

很简单的一段代码,我们来看看这个new究竟做了什么?我们可以把new的过程拆分成以下三步:

1.var p={}; 初始化一个对象p。

2. p.__proto__=Person.prototype;,将对象p的 __proto__ 属性设置为 Person.prototype

3.Person.call(p,"孤傲苍狼",24);调用构造函数Person来初始化p。

关键在于第二步,我们来证明一下:

var p = new Person("孤傲苍狼",24);//创建一个人,名字是孤傲苍狼
alert("p.__proto__ === Person.prototype的结果是:"+(p.__proto__ === Person.prototype));

在火狐下的运行结果是:

Javascript原型链的原理详解

这段代码会返回true。说明我们步骤2的正确。

注意:__proto__这个属性只有在firefox或者chrome浏览器中才是公开允许访问的,因此,其他基于IE内核的浏览器是不会返回true的。

那么__proto__是什么?在这里简单地说下。每个对象都会在其内部初始化一个属性,就是 __proto__,当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个 __proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。

按照标准,__proto__是不对外公开的,也就是说是个私有属性,在IE下是无法访问__proto__属性的,但是Firefox的引擎将他暴露了出来成为了一个公有的属性,我们可以对外访问和设置。

好,概念说清了,让我们看一下下面这些代码:

<script type="text/javascript">
    var Person = function () { };
    Person.prototype.Say = function () {
      alert("Person say");
    }
    var p = new Person();
    p.Say();
</script>

这段代码很简单,我们看下为什么p可以访问Person的Say。

首先

var p=new Person();

可以得出

p.__proto__=Person.prototype

那么当我们调用p.Say()时,首先p中没有Say这个属性, 于是,他就需要到他的__proto__中去找,也就是Person.prototype,而我们在上面定义了

Person.prototype.Say=function(){
    alert("Person say");
};

于是,就找到了这个方法。

接下来,让我们看个更复杂的。

<script type="text/javascript">
    var Person = function () { };
    Person.prototype.Say = function () {
      alert("Person say");
    }
    Person.prototype.Salary = 50000;
    var Programmer = function () { };
    Programmer.prototype = new Person();//让程序员类从人这个类继承
    Programmer.prototype.WriteCode = function () {
      alert("programmer writes code");
    };
    Programmer.prototype.Salary = 500;
    var p = new Programmer();
    p.Say();
    p.WriteCode();
    alert(p.Salary);
</script>

我们来做这样的推导:

var p=new Programmer();

可以得出

p.__proto__=Programmer.prototype;

而在上面我们指定了

Programmer.prototype=new Person();

我们来这样拆分,

var p1=new Person();
Programmer.prototype=p1;

那么:

p1.__proto__=Person.prototype;
Programmer.prototype.__proto__=Person.prototype;

由根据上面得到

p.__proto__=Programmer.prototype

可以得到:

p.__proto__.__proto__=Person.prototype

好,算清楚了之后我们来看上面的结果,p.Say()。由于p没有Say这个属性,于是去 p.__proto__,也就是Programmer.prototype,也就是p1中去找,由于p1中也没有Say,那就去 p.__proto__.__proto__,也就是Person.prototype中去找,于是就找到了Say方法。这也就是原型链的实现原理。

以下代码展示了JS引擎如何查找属性:

function getProperty(obj, prop) {
  if (obj.hasOwnProperty(prop))
    return obj[prop];
  else if (obj.__proto__ !== null)
    return getProperty(obj.__proto__, prop);//递归
  else
    return undefined;
}

范例:查找p对象的Say方法

<script type="text/javascript">
  /*查找obj对象的prop属性*/
   function getProperty(obj, prop) {
    if (obj.hasOwnProperty(prop))
      return obj[prop];
    else if (obj.__proto__ !== null)
      return getProperty(obj.__proto__, prop);//递归
    else
      return undefined;
  }
  var Person = function () { };//定义Person类
  Person.prototype.Say = function () {
    alert("Person say");
  }
  Person.prototype.Salary = 50000;
  var Programmer = function () { };//定义Programmer类
  //Programmer.prototype = new Person();//让程序员类从人这个类继承,写法一
  Programmer.prototype = Person.prototype;//让程序员类从人这个类继承,写法二
  Programmer.prototype.WriteCode = function () {
    alert("programmer writes code");
  };
  Programmer.prototype.Salary = 500;
  var p = new Programmer();
  var SayFn = getProperty(p,"Say");//查找p对象的Say方法
  SayFn.call(p);//调用找到的Say方法
</script>

在火狐下的运行结果:

Javascript原型链的原理详解

其实prototype只是一个假象,他在实现原型链中只是起到了一个辅助作用,换句话说,他只是在new的时候有着一定的价值,而原型链的本质,其实在于__proto__。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
js调用webservice中的方法实现思路及代码
Feb 25 Javascript
Node.js 服务器端应用开发框架 -- Hapi.js
Jul 29 Javascript
JS根据key值获取URL中的参数值及把URL的参数转换成json对象
Aug 26 Javascript
js确认框confirm()用法实例详解
Jan 07 Javascript
jQuery使用中可能被XSS攻击的一些危险环节提醒
May 24 Javascript
Javascript小技能总结(推荐)
Jun 02 Javascript
jQuery Ajax 全局调用封装实例代码详解
Jun 02 Javascript
基于substring()和substr()的使用以及区别(实例讲解)
Dec 28 Javascript
jQuery中图片展示插件highslide.js的简单dom
Apr 22 jQuery
vuex与组件联合使用的方法
May 10 Javascript
Vue验证码60秒倒计时功能简单实例代码
Jun 22 Javascript
javascript贪吃蛇游戏设计与实现
Sep 17 Javascript
深入浅析JavaScript系列(13):This? Yes,this!
Jan 05 #Javascript
基于javascript实现图片懒加载
Jan 05 #Javascript
this,this,再次讨论javascript中的this,超全面(经典)
Jan 05 #Javascript
基于javascript实现图片预加载
Jan 05 #Javascript
JavaScript、tab切换完整版(自动切换、鼠标移入停止、移开运行)
Jan 05 #Javascript
javascript设置和获取cookie的方法实例详解
Jan 05 #Javascript
javascript简单比较日期大小的方法
Jan 05 #Javascript
You might like
PHPnow安装服务[apache_pn]失败的问题的解决方法
2010/09/10 PHP
在php中判断一个请求是ajax请求还是普通请求的方法
2011/06/28 PHP
PHP识别二维码的方法(php-zbarcode安装与使用)
2016/07/07 PHP
如何判断php mysqli扩展类是否开启
2016/12/24 PHP
PHP插件PHPMailer发送邮件功能
2017/02/28 PHP
获取Javscript执行函数名称的方法
2006/12/22 Javascript
Jquery 设置标题的自动翻转
2009/10/03 Javascript
JavaScript 对象的属性和方法4种不同的类型
2010/03/19 Javascript
jquery.pagination.js 无刷新分页实现步骤分享
2012/05/23 Javascript
js遍历子节点子元素附属性及方法
2014/08/19 Javascript
微信内置浏览器私有接口WeixinJSBridge介绍
2015/05/25 Javascript
JavaScript面对国际化编程时的一些建议
2015/06/24 Javascript
jQuery实现的鼠标滑过弹出放大图片特效
2016/01/08 Javascript
jQuery插件简单学习实例教程
2016/07/01 Javascript
Bootstrap的Refresh Icon也spin起来
2016/07/13 Javascript
js实现StringBuffer的简单实例
2016/09/02 Javascript
js学习总结之DOM2兼容处理重复问题的解决方法
2017/07/27 Javascript
javascript数组拍平方法总结
2018/01/20 Javascript
vue以组件或者插件的形式实现throttle或者debounce
2019/05/22 Javascript
vue中使用腾讯云Im的示例
2020/10/23 Javascript
详解ES6实现类的私有变量的几种写法
2021/02/10 Javascript
[04:21]狐狸妈带你到现场 DOTA2 TI中国区预选赛线下赛路线指引
2014/05/22 DOTA
[02:14]2016国际邀请赛中国区预选赛Ehome晋级之路
2016/07/01 DOTA
python3.5实现socket通讯示例(TCP)
2017/02/07 Python
对pandas的层次索引与取值的新方法详解
2018/11/06 Python
Python数据类型之列表和元组的方法实例详解
2019/07/08 Python
详解python中GPU版本的opencv常用方法介绍
2020/07/24 Python
Bed Bath & Beyond加拿大官网:购买床上用品、浴巾、厨房电器等
2019/10/04 全球购物
英国设计师珠宝网站:Joshua James Jewellery
2020/03/01 全球购物
职业教育毕业生求职信
2013/11/09 职场文书
初中科学教学反思
2014/01/21 职场文书
体育教师自我鉴定
2014/02/12 职场文书
2014年小学植树节活动方案
2014/03/02 职场文书
五四青年节的活动方案
2014/08/20 职场文书
我的中国心演讲稿
2014/09/04 职场文书
MySQL令人咋舌的隐式转换
2021/04/05 MySQL