未加星标

深入浅出JavaScript之原型链&继承

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二04 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏
深入浅出javascript之原型链&继承

5小时前来源:cnblogs

Javascript语言的继承机制,它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。

这部分知识也是JavaScript里的核心重点之一,同时也是一个难点。我把学习笔记整理了一下,方便大家学习,同时自己也加深印象。这部分代码的细节很多,需要反复推敲。那我们就开始吧。

小试身手

原型链例子(要点写在注释里,可以把代码复制到浏览器里测试,下同)

function foo{} //通过function foo{}定义一个函数对象 foo.prototype.z = 3; //函数默认带个prototype对象属性 (typeof foo.prototype;//"object") var obj =new foo; //我们通过new foo构造器的方式构造了一个新的对象 obj.y = 2; //通过赋值添加两个属性给obj obj.x = 1; //通过这种方式构造对象,对象的原型会指向构造函数的prototype属性,也就是foo.prototype obj.x; // 1 //当访问obj.x时,发现obj上有x属性,所以返回1 obj.y; // 2 //当访问obj.y时,发现obj上有y属性,所以返回2 obj.z; // 3 //当访问obj.z时,发现obj上没有z属性,那怎么办呢?它不会停止查找,它会查找它的原型,也就是foo.prototype,这时找到z了,所以返回3//我们用字面量创建的对象或者函数的默认prototype对象,实际上它也是有原型的,它的原型指向Object.prototype,然后Object.prototype也是有原型的,它的原型指向null。 //那这里的Object.prototype有什么作用呢?typeof obj.toString; // ‘function' //我们发现typeof obj.toString是一个函数,但是不管在对象上还是对象的原型上都没有toString方法,因为在它原型链的末端null之前都有个Object.prototype方法,//而toString正是Object.prototype上面的方法。这也解释了为什么JS基本上所有对象都有toString方法'z' in obj; // true //obj.z是从foo.prototype继承而来的,所以'z' in obj返回了trueobj.hasOwnProperty('z'); // false //但是obj.hasOwnProperty('z')返回了false,表示z不是obj直接对象上的,而是对象的原型链上面的属性。(hsaOwnProperty也是Object.prototype上的方法)
深入浅出JavaScript之原型链&继承

刚才我们访问x,y和z,分别通过原型链去查找,我们可以知道:当我们访问对象的某属性时,而该对象上没有相应属性时,那么它会通过原型链向上查找,一直找到null还没有话,就会返回undefined。

基于原型的继承
深入浅出JavaScript之原型链&继承
prototype属性与原型
深入浅出JavaScript之原型链&继承
深入浅出JavaScript之原型链&继承

我们再来看看Foo.prototype是什么样的结构,当我们用函数声明去创建一个空函数的时候,那么这个函数就有个prototype属性,并且它默认有两个属性,constructor和__proto__,

constructor属性会指向它本身Foo,__proto__是在chrome中暴露的(不是一个标准属性,知道就行),那么Foo.prototype的原型会指向Object.prototype。因此Object.prototype上

的一些方法toString,valueOf才会被每个一般的对象所使用。

总结一下:我们这里有个Foo函数,这个函数有个prototype的对象属性,它的作用就是当使用new Foo去构造实例的时候,这个构造器的prototype属性会用作new出来的这些对象的原型。

所以我们要搞清楚,prototype和原型是两回事,prototype是函数对象上的预设属性,原型通常是构造器上的prototype属性。

实现一个class继承另外一个class function Person(name, age) { this.name = name; //直接调用的话,this指向全局对象(this知识点整理) this.age = age; //使用new调用Peoson的话,this会指向原型为Person.prototype的空对象,通过this.name给空对象赋值,最后this作为return值 } Person.prototype.hi = function { //通过Person.prototype.hi创建所有Person实例共享的方法,(可以参考上节的左图:对象的原型会指向构造器的prototype属性,所以想让obj1,obj2,obj3共享一些方法的话,只需在原型对象上一次性地添加属性和方法就可以了); console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now.')//这里的this是全局对象 }; Person.prototype.LEGS_NUM = 2; //再设置一些对Person类的所有实例共享的数据 Person.prototype.ARMS_NUM = 2; Person.prototype.walk = function { console.log(this.name + ' is walking...'); }; function Student(name, age, className) { //每个学生都属于人 Person.call(this, name, age); //在Student这个子类里面先调用一下父类 this.className = className; } //下一步就是我们怎么去把Student的实例继承Person.prototype的一些方法 Student.prototype = Object.create(Person.prototype); //Object.create:创建一个空对象,并且这个对象的原型指向它的参数 //这样子我们可以在访问Student.prototype的时候可以向上查找到Person.prototype,又可以在不影响Person的情况下,创建自己的方法 Student.prototype.constructor = Student; //保持一致性,不设置的话constructor会指向Person Student.prototype.hi = function { //通过Student.prototype.hi这样子的赋值可以覆盖我们基类Person.prototype.hi console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now, and from ' + this.className + '.'); } Student.prototype.learn = function(subject) { //同时,我们又有自己的learn方法 console.log(this.name + 'is learning ' + subject + ' at' + this.className + '.'); }; //testvar yun = new Student('Yunyun', 22, 'Class 3,Grade 2'); yun.hi; //Hi,my name is Yunyun,I'm 22 years old now,and from Class 3, Grade 2. console.log(yun.ARMS_NUM); // 2 //我们本身对象是没有的,对象的原型也就是Student.prototype也没有,但是我们用了继承,继续向上查找,找到了Person.prototype.ARMS_NUM,所以返回2 yun.walk; //Yunyun is walking... yun.learn('math'); //Yunyun is learning math at Class 3,Grade 2.
深入浅出JavaScript之原型链&继承

结合图我们来倒过来分析一下上面代码:我们先通过new Student创建了一个Student的实例yun,yun的原型指向构造器的prototype属性(这里就是Student.prototype), Student.prototype上有hi方法和learn方法,Student.prototype是通过Object.create(Person.prototype)构造的,所以这里的Student.prototype是空对象,并且这个对象的原型指向Person.prototype,接着我们在Person.prototype上也设置了LEGS_NUM,ARMS_NUM属性以及hi,walk方法。然后我们直接定义了一个Person函数,Person.prototype就是一个预置的对象,它本身也会有它的原型,它的原型就是Object.prototype,也正是因为这样,我们随便一个对象才会有hasOwnProperty,valueOf,toString这样些公共的函数,这些函数都是从Object.prototype上来的。这样子就实现了基于原型链的继承。 那我们调用hi,walk,learn方法的时候发生了什么呢?比如我们调用hi方法的时候,我们首先看这个对象yun上有没有hi方法,但是在这个实例中没有所以会向上查找,查找到yun的原型也就是Student.protoype上有这hi方法,所以最终调用的是Student.prototype.hi,调用其他方法也是类似的。

改变prototype

我们知道JavaScript中的prototype原型不像Java中的class,Java中的class一旦写好就很难动态的去改变了,但是JavaScript中的原型实际上也是普通的对象,那就意味着在程序运行的阶段,我们也可以动态的给prototype添加或删除些属性。


深入浅出JavaScript之原型链&继承

在上述代码的基础上,我们已经有yun这个实例了,我们接着来进行实验:

所以说当动态修改prototype的时候,是会影响所有已创建或新创建的实例的,但是修改整个prototype赋值为新的对象的话,对已创建的实例是不会影响的,但是会影响后续的实例。

实现继承的方式

实现继承有多种方式,下面我们还是以Person和Student来分析

总结

做项目的时候才发现这些基础概念有多么的重要,如果不把它们逐个落实了,真的是一不小心就会掉进坑里。后续会继续对JavaScript其他部分以及node作相关总结,欢迎关注。

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: JavaJavaScript浏览器数据删除
分页:12
转载请注明
本文标题:深入浅出JavaScript之原型链&继承
本站链接:http://www.codesec.net/view/479664.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(77)