JavaScript学习总结(十七)——Javascript原型链的原理

一、JavaScript原型链

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

比如现在有如下的代码:

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

 1 /*扩展Object类,添加Clone,JS实现克隆的方法*/
 2 Object.prototype.Clone = function(){
 3     var objClone;
 4     if (this.constructor == Object){
 5         objClone = new this.constructor();
 6     }else{
 7         objClone = new this.constructor(this.valueOf());
 8     }
 9     for(var key in this){
10         if ( objClone[key] != this[key] ){
11             if ( typeof(this[key]) == ‘object‘ ){
12                 objClone[key] = this[key].Clone();
13             }else{
14                 objClone[key] = this[key];
15             }
16         }
17     }
18     objClone.toString = this.toString;
19     objClone.valueOf = this.valueOf;
20     return objClone;
21 }
22
23 /*扩展Object类,添加Extend方法来实现JS继承, 目标对象将拥有源对象的所有属性和方法*/
24 Object.prototype.Extend = function (objDestination, objSource) {
25     for (var key in objSource) {
26         if (objSource.hasOwnProperty(key) && objDestination[key] === undefined) {
27             objDestination[key] = objSource[key];
28         }
29     }
30     return objDestination;
31 }

定义Person类

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

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

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

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

运行结果:

  

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

1  /*定义一个Person类*/
2  function Person(_name,_age){
3      this.name = _name;
4      this.age = _age;
5  }
6  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运算符是如何工作的

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

1 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。

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

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

在火狐下的运行结果是:

  

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

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

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

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

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

1 <script type="text/javascript">
2         var Person = function () { };
3         Person.prototype.Say = function () {
4             alert("Person say");
5         }
6         var p = new Person();
7         p.Say();
8 </script>

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

  首先

1 var p=new Person();

  可以得出

1 p.__proto__=Person.prototype

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

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

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

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

 1 <script type="text/javascript">
 2         var Person = function () { };
 3         Person.prototype.Say = function () {
 4             alert("Person say");
 5         }
 6         Person.prototype.Salary = 50000;
 7         var Programmer = function () { };
 8         Programmer.prototype = new Person();//让程序员类从人这个类继承
 9         Programmer.prototype.WriteCode = function () {
10             alert("programmer writes code");
11         };
12         Programmer.prototype.Salary = 500;
13         var p = new Programmer();
14         p.Say();
15         p.WriteCode();
16         alert(p.Salary);
17 </script>

  我们来做这样的推导:

1 var p=new Programmer();

  可以得出

1 p.__proto__=Programmer.prototype;

  而在上面我们指定了

1 Programmer.prototype=new Person();

  我们来这样拆分,

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

  那么:

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

  由根据上面得到

1 p.__proto__=Programmer.prototype

  可以得到:

1 p.__proto__.__proto__=Person.prototype

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

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

1 function getProperty(obj, prop) {
2     if (obj.hasOwnProperty(prop))
3         return obj[prop];
4     else if (obj.__proto__ !== null)
5         return getProperty(obj.__proto__, prop);//递归
6     else
7         return undefined;
8 }

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

 1  <script type="text/javascript">
 2     /*查找obj对象的prop属性*/
 3      function getProperty(obj, prop) {
 4         if (obj.hasOwnProperty(prop))
 5             return obj[prop];
 6         else if (obj.__proto__ !== null)
 7             return getProperty(obj.__proto__, prop);//递归
 8         else
 9             return undefined;
10     }
11
12     var Person = function () { };//定义Person类
13     Person.prototype.Say = function () {
14         alert("Person say");
15     }
16     Person.prototype.Salary = 50000;
17
18     var Programmer = function () { };//定义Programmer类
19     //Programmer.prototype = new Person();//让程序员类从人这个类继承,写法一
20     Programmer.prototype = Person.prototype;//让程序员类从人这个类继承,写法二
21     Programmer.prototype.WriteCode = function () {
22         alert("programmer writes code");
23     };
24     Programmer.prototype.Salary = 500;
25     var p = new Programmer();
26     var SayFn = getProperty(p,"Say");//查找p对象的Say方法
27     SayFn.call(p);//调用找到的Say方法
28 </script>

在火狐下的运行结果:

  

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

JavaScript学习总结(十七)——Javascript原型链的原理

时间: 2024-10-05 05:07:51

JavaScript学习总结(十七)——Javascript原型链的原理的相关文章

Javascript讲解系列之一 Prototype原型链

以前没有写博客的习惯,许多的技术积累都是自己稍微总结一下,很少共享,并非自私,而是工作比较忙,前几天接到一个电话面试不理想,才发现公司所用的DOJO并不被外面广泛接受,故而决定把自己所学分享出来,为夯实基础,也为与外界交流思想,形成一种渠道,如需联系,请发送至邮箱:[email protected]. 今天写Javascript系列之第一篇:Prototype原型链.在软件园里随便拉一个码农估计都会写JS,大部分也知道JS是基于原型的语言,但是如果问及JS原生对象(Object,Function

JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承

说好的讲解JavaScript继承,可是迟迟到现在讲解.废话不多说,直接进入正题. 既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考<面向对象JS基础讲解,工厂模式.构造函数模式.原型模式.混合模式.动态原型模式>,接下来讲一般通过那些方法完成JavaScript的继承. 原型链 JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即“子类型.prototype = new 父类型();”,实现方法如下

JavaScript学习总结(九)——Javascript面向(基于)对象编程

一.澄清概念 1.JS中"基于对象=面向对象" 2.JS中没有类(Class),但是它取了一个新的名字叫"原型对象",因此"类=原型对象" 二.类(原型对象)和对象(实例)的区别与联系 1.类(原型对象)是抽象,是概念的,代表一类事物. 2.对象是具体的,实际的,代表一个具体的事物. 3.类(原型对象)是对象实例的模板,对象实例是类的一个个体. 三.抽象的定义 在定义一个类时,实际上就是把一类事物的共有属性和行为提取出来,形成一个物理模型(模板)

JavaScript 变量、函数与原型链

定义 || 赋值 1-函数的定义 函数定义的两种方式: “定义式”函数:function fn(){ alert("哟,哟!"); } “赋值式”函数:var fn = function(){ alert("切可闹!"); } @页面加载时,浏览器会对JavaScript代码进行扫描,并将 定义式函数进行预处理(类似C等的编译).[函数声明提升] 处理完再由上至下执行,遇到赋值式函数 则只是将函数赋值给一个变量,不进行预处理,待调用时才进行处理. @在定义前面调用函

JavaScript中的继承与原型链

先看一个例子 function User(){} var u1 = new User(); console.log(u1.prototype);// undefined 使用对象实例无法访问到prototype console.log(User.prototype);//{},使用构造函数名访问prototype console.log(u1.__proto__);//{},使用对象实例访问prototype的指针 这个是 __proto__ 和prototype最基本的区别:说明构造的对象无p

JavaScript中的继承(原型链)

一.原型链 ECMAScript中将原型链作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 实例1: function SupType() { this.property = true; } SupType.prototype.getSupvalue = function() { return this.property; }; function SubType() { this.subproperty = false; } //原型对象等于一个类型的实例

8条规则图解JavaScript原型链继承原理

原形链是JS难点之一,而且很多书都喜欢用一大堆的文字解释给你听什么什么是原型链,就算有图配上讲解,有的图也是点到为止,很难让人不产生疑惑. 我们先来看一段程序,友情提示sublimeText看更爽: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edg

03. JavaScript高级(3/5)(原型链讲解)

一.原型以及原型链 基础概念: 实例对象的__proto__和构造函数的原型prototype指向相同 实例对象中的__proto__原型指向的是构造函数中的prototype原型 实例对象中的__proto__是原型,是浏览器使用的 构造函数中的prototype是原型,是程序员使用的 实例对象中的proto指向的是构造函数中的prototype所指向的原型对象, 原型链:是一种关系,实例对象和原型对象之间的关系,这种关系是通过实例对象中的原型(proto)来联系的. 改变原型链指向 构造函数

深入javascript面向对象,js的原型链、继承

进阶面向对象----------------------– 在JS源码中,系统对象也是基于原型的程序, 尽量不要去添加和修改系统对象的方法 包装对象----------------------– 基本类型都有自己对应的包装对象 比如String Number Boolean 基本类型会找到对应的包装对象类型,然后包装对象把所有的属性方法给了 基本类型,然后包装对象消失 例如 var str = 'abc'; str.num = 10; //创建一个包装对象,给包装对象加num属性,然后立刻消失.