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中操作JSON总结
Dec 06 Javascript
js和css写一个可以自动隐藏的悬浮框
Mar 05 Javascript
Egret引擎开发指南之创建项目
Sep 03 Javascript
JS实现简易图片轮播效果的方法
Mar 25 Javascript
js生成随机数的过程解析
Nov 24 Javascript
javascript中的后退和刷新实现方法
Nov 10 Javascript
jquery实现刷新随机变化样式特效(tag标签样式)
Feb 03 Javascript
vue 表单输入格式化中文输入法异常问题
May 30 Javascript
解决vue脚手架项目打包后路由视图不显示的问题
Sep 20 Javascript
微信小程序模板消息推送的两种实现方式
Aug 27 Javascript
微信小程序scroll-view点击项自动居中效果的实现
Mar 25 Javascript
js实现时间日期校验
May 26 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设计模式 Chain Of Responsibility (职责链模式)
2011/06/26 PHP
php学习笔记(三)操作符与控制结构
2011/08/06 PHP
详解WordPress中简码格式标签编写的基本方法
2015/12/22 PHP
php实现图片上传、剪切功能
2016/05/07 PHP
PHP+MySQL存储数据常见中文乱码问题小结
2016/06/13 PHP
Smarty缓存机制实例详解【三种缓存方式】
2019/07/20 PHP
jquery div拖动效果示例代码
2013/12/08 Javascript
JS中处理时间之setUTCMinutes()方法的使用
2015/06/12 Javascript
jQuery模拟原生态App上拉刷新下拉加载更多页面及原理
2015/08/10 Javascript
Jquery插件仿百度搜索关键字自动匹配功能
2016/05/11 Javascript
jQuery控制控件文本的长度的操作方法
2016/12/05 Javascript
bootstrapValidator.min.js表单验证插件
2017/02/09 Javascript
jQuery easyui datagird编辑行删除行功能的实现代码
2018/09/20 jQuery
浅谈vue项目4rs vue-router上线后history模式遇到的坑
2018/09/27 Javascript
详解ES6中的Map与Set集合
2019/03/22 Javascript
jQuery实现checkbox全选、反选及删除等操作的方法详解
2019/08/02 jQuery
浅谈TypeScript 用 Webpack/ts-node 运行的配置记录
2019/10/11 Javascript
vue 保留两位小数 不能直接用toFixed(2) 的解决
2020/08/07 Javascript
Vue实现图书管理小案例
2020/12/03 Vue.js
python自定义解析简单xml格式文件的方法
2015/05/11 Python
python判断输入日期为第几天的实例
2018/11/13 Python
python生成九宫格图片
2018/11/19 Python
Python实现E-Mail收集插件实例教程
2019/02/06 Python
如何清空python的变量
2020/07/05 Python
Html5+CSS3+EL表达式问题小结
2020/12/19 HTML / CSS
罗德与泰勒百货官网:Lord & Taylor
2016/08/12 全球购物
机械专业个人求职自荐信格式
2013/09/21 职场文书
技术经理的自我评价范文
2013/12/03 职场文书
毕业班联欢会主持词
2014/03/27 职场文书
2016年学校“3.12”植树节活动总结
2016/03/16 职场文书
表扬稿表扬信的格式及范文
2019/06/24 职场文书
大学生奖学金获奖感言(范文)
2019/08/15 职场文书
详解Js模块化的作用原理和方案
2021/04/29 Javascript
SpringCloud Alibaba 基本开发框架搭建过程
2021/06/13 Java/Android
使用Spring处理x-www-form-urlencoded方式
2021/11/02 Java/Android
js 实现验证码输入框示例详解
2022/09/23 Javascript