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 相关文章推荐
DWZ table的原生分页浅谈
Mar 01 Javascript
JQuery的Ajax跨域请求原理概述及实例
Apr 26 Javascript
浅析LigerUi开发中谨慎载入common.css文件
Jul 09 Javascript
使用OpenLayers3 添加地图鼠标右键菜单
Dec 29 Javascript
基于Bootstrap实现下拉菜单项和表单导航条(两个菜单项,一个下拉菜单和登录表单导航条)
Jul 22 Javascript
JS动态的把左边列表添加到右边的实现代码(可上下移动)
Nov 17 Javascript
JS常用加密编码与算法实例总结
Dec 22 Javascript
详解node child_process模块学习笔记
Jan 24 Javascript
微信小程序 image组件遇到的问题
May 28 Javascript
javascript设计模式 ? 状态模式原理与用法实例分析
Apr 22 Javascript
vue3+typescript实现图片懒加载插件
Oct 26 Javascript
JavaScript Html实现移动端红包雨功能页面
Jan 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
PHP聊天室技术
2006/10/09 PHP
php树型类实例
2014/12/05 PHP
php简单实现快速排序的方法
2015/04/04 PHP
PHP+MySQL实现的简单投票系统实例
2016/02/24 PHP
Laravel路由设定和子路由设定实例分析
2016/03/30 PHP
PHP图片加水印实现方法
2016/05/06 PHP
PHP批量删除jQuery操作
2017/07/23 PHP
laravel 5.4 + vue + vux + element的环境搭配过程介绍
2018/04/26 PHP
YII框架模块化处理操作示例
2019/04/26 PHP
翻译整理的jQuery使用查询手册
2007/03/07 Javascript
javascript右下角弹层及自动隐藏(自己编写)
2013/11/20 Javascript
JS实现点击上移下移LI行数据的方法
2015/08/05 Javascript
JavaScript的ExtJS框架中数面板TreePanel的使用实例解析
2016/05/21 Javascript
JS阻止事件冒泡行为和闭包的方法
2016/06/16 Javascript
深入浅析JS Function()构造函数
2016/08/22 Javascript
localStorage实现便签小程序
2016/11/28 Javascript
深入理解Vue官方文档梳理之全局API
2017/11/22 Javascript
在 React、Vue项目中使用SVG的方法
2018/02/09 Javascript
详解IOS微信上Vue单页面应用JSSDK签名失败解决方案
2018/11/14 Javascript
JavaScript Date对象功能与用法学习记录
2020/04/28 Javascript
[01:29:46]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第二局
2016/02/27 DOTA
一篇不错的Python入门教程
2007/02/08 Python
python 基础学习第二弹 类属性和实例属性
2012/08/27 Python
python读取excel表格生成erlang数据
2017/08/26 Python
解决matplotlib库show()方法不显示图片的问题
2018/05/24 Python
Python 查看list中是否含有某元素的方法
2018/06/27 Python
浅谈python的dataframe与series的创建方法
2018/11/12 Python
python-numpy-指数分布实例详解
2019/12/07 Python
python实现AdaBoost算法的示例
2020/10/03 Python
HTML5 贪吃蛇游戏实现思路及源代码
2013/09/03 HTML / CSS
大学毕业生简单自荐信
2013/11/05 职场文书
购房个人委托书范本
2014/10/11 职场文书
协议书格式模板
2016/03/24 职场文书
javaScript Array api梳理
2021/03/31 Javascript
MySQL数据库必备之条件查询语句
2021/10/15 MySQL
python神经网络学习 使用Keras进行简单分类
2022/05/04 Python