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 相关文章推荐
制作特殊字的脚本
Jun 26 Javascript
判断iframe是否加载完成的完美方法
Jan 07 Javascript
js 为label标签和div标签赋值的方法
Aug 08 Javascript
JavaScript实现的多种鼠标拖放效果
Nov 03 Javascript
jQuery实现验证年龄简单思路
Feb 24 Javascript
JavaScript原生对象常用方法总结(推荐)
May 13 Javascript
Javascript中的prototype与继承
Feb 06 Javascript
微信小程序 数据遍历的实现
Apr 05 Javascript
angular6 填坑之sdk的方法
Dec 27 Javascript
记一次Vue.js混入mixin的使用(分权限管理页面)
Apr 17 Javascript
微信小程序模板消息限制实现无限制主动推送的示例代码
Aug 27 Javascript
angula中使用iframe点击后不执行变更检测的问题
May 10 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
模仿OSO的论坛(一)
2006/10/09 PHP
php的POSIX 函数以及进程测试的深入分析
2013/06/03 PHP
Cygwin中安装PHP方法步骤
2015/07/04 PHP
PHP设计模式之工厂模式(Factory Pattern)的讲解
2019/03/21 PHP
Yii2框架配置文件(Application属性)与调试技巧实例分析
2019/05/27 PHP
jquery 常用操作整理 基础入门篇
2009/10/14 Javascript
js完美的div拖拽实例代码
2014/01/22 Javascript
JavaScript  event对象整理及详细介绍
2016/10/10 Javascript
Nodejs进阶:核心模块net入门学习与实例讲解
2016/11/21 NodeJs
微信小程序 scroll-view隐藏滚动条详解
2017/01/16 Javascript
Angularjs 根据一个select的值去设置另一个select的值方法
2018/08/13 Javascript
Web安全之XSS攻击与防御小结
2018/12/13 Javascript
小程序实现订单倒计时功能
2019/04/23 Javascript
详解微信小程序之一键复制到剪切板
2019/04/24 Javascript
vue router动态路由设置参数可选问题
2019/08/21 Javascript
关于vue项目中搜索节流的实现代码
2019/09/17 Javascript
VUE 实现动态给对象增加属性,并触发视图更新操作示例
2019/11/29 Javascript
基于vue与element实现创建试卷相关功能(实例代码)
2020/12/07 Vue.js
Python迭代器和生成器介绍
2015/03/06 Python
python uuid模块使用实例
2015/04/08 Python
python实现将元祖转换成数组的方法
2015/05/04 Python
Python下载指定页面上图片的方法
2016/05/12 Python
Python实现的查询mysql数据库并通过邮件发送信息功能
2018/05/17 Python
django获取from表单multiple-select的value和id的方法
2019/07/19 Python
python+opencv实现车牌定位功能(实例代码)
2019/12/24 Python
如何搭建pytorch环境的方法步骤
2020/05/06 Python
波兰数码相机及配件网上商店: Cyfrowe.pl
2017/06/19 全球购物
New Balance加拿大官方网站:运动鞋和健身服装
2018/11/19 全球购物
韩国最大的购物网站:Gmarket
2019/06/20 全球购物
英国男女豪华配饰和礼品网站:Black.co.uk
2020/02/28 全球购物
酒店采购员岗位职责
2014/03/14 职场文书
小学生反邪教心得体会
2016/01/15 职场文书
个人房屋租赁合同(标准范本)
2019/09/16 职场文书
Python中seaborn库之countplot的数据可视化使用
2021/06/11 Python
Nginx部署vue项目和配置代理的问题解析
2021/08/04 Servers
Python数组变形的几种实现方法
2022/05/30 Python