这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
这篇文章主要介绍了 Javascript 原型链的原理, 结合实例形式深入分析了 JavaScript 原型链的原理与使用技巧, 需要的朋友可以参考下
本文实例分析了 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>");
运行结果:
那么 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));
在火狐下的运行结果是:
这段代码会返回 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>
在火狐下的运行结果:
其实 prototype 只是一个假象,他在实现原型链中只是起到了一个辅助作用,换句话说,他只是在 new 的时候有着一定的价值,而原型链的本质,其实在于__proto__。
希望本文所述对大家 JavaScript 程序设计有所帮助。
来源: http://www.phperz.com/article/17/0411/267728.html