logo资料库

尚硅谷——JavaScript原型.pdf

第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
资料共9页,剩余部分请下载后查看
————————————————————————————— “玩转”Java 系列 JavaScript 原型 一、引入:为多个对象统一添加属性 首先看下面一段程序 return "PersonName="+this.perName+" PersonAge="+this.perAge; this.perName = perName; this.perAge = perAge; this.toString = function(){ }; function Person(perName,perAge){ } var per01 = new Person("Tom", 20); var per02 = new Person("Jerry", 25); console.log(per01.toString()); console.log(per02.toString()); 执行结果: PersonName=Tom PersonAge=20 PersonName=Jerry PersonAge=25 在这段程序中声明了一个构造器函数 Person,使用这个构造器函数创建了 两个对象,并在控制台中进行了显示。 现在如果我们需要给每个 Person 对象都添加一个 gender 属性,最直接的做 function Person(perName,perAge,gender){ this.perName = perName; this.perAge = perAge; this.gender = gender; this.toString = function(){ return "PersonName="+this.perName+" PersonAge="+this.perAge+" 法可能是这样: PersonGender="+this.gender; } var per01 = new Person("Bob", 20, "male"); var per02 = new Person("Kate", 25, "female"); console.log(per01.toString()); console.log(per02.toString()); }; 1
————————————————————————————— “玩转”Java 系列 执行结果: PersonName=Bob PersonAge=20 PersonGender=male PersonName=Kate PersonAge=25 PersonGender=female 可是这样做有一个问题:如果 Person 这个构造器函数不是我们创建的,我 们也不能修改它,还能否添加属性呢? 当然可以,使用 JavaScript 的原型机制就可以做到。 二、原型 1.函数原型 在 JavaScript 中,函数不仅仅是一个可以重用的代码块,而且还可以作为一 种数据使用。在堆空间中为函数分配了它的存储空间,函数名或函数的其他形式 的引用保存了这个存储空间的引用地址。所以 JavaScript 中的函数是一种引用数 据类型,这就是为什么我们说 JavaScript 中的函数也是对象。 那么函数这样的对象有很多特殊的性质,原型就是其中之一。每一个函数 对象都包含一个属性:prototype。当我们声明一个函数时,函数对象就创建好了。 而函数对象创建的同时,系统还会同时创建一个对象,并让函数对象的 prototype 属性指向它。 比如说,当我们执行下面代码时: function Person(perName,perAge){ } //… 就声明了一个函数,系统会将这个函数对象创建出来。内存中应该是这样 的情况: 但是更深层次挖掘一下,我还需要知道创建函数对象的同时,还会创建一 个“原型对象”,由函数对象的 prototype 属性指向这个原型对象。 2
————————————————————————————— “玩转”Java 系列 2.对象原型 函数可以和以它为构造器所创建的所有对象共享原型对象。就比如本文最 初展示的代码中,per01 和 per02 都是 Person 函数创建的,那么 per01 或 per02 就可以通过自身的__proto__属性关联到 Person 对象的原型。 下图表示了它们之间的关系 3
————————————————————————————— “玩转”Java 系列 所以既然函数的原型对象是可以为所有被创建对象共享的,那么就可以将 this.perName = perName; this.perAge = perAge; this.toString = function(){ }; return "PersonName="+this.perName+" PersonAge="+this.perAge; 我们要为所有对象都添加的属性添加到原型对象上。 function Person(perName,perAge,gender){ } var per01 = new Person("Bob", 20); var per02 = new Person("Kate", 25); 4
“玩转”Java 系列 ————————————————————————————— Person.prototype.message = "Atguig is very good"; console.log(per01.toString()+" message="+per01.message); console.log(per02.toString()+" message="+per02.message); 执行结果: PersonName=Bob PersonAge=20 message=Atguig is very good PersonName=Kate PersonAge=25 message=Atguig is very good JavaScript 引擎在读取 per01 对象的 message 属性时先在当前对象本身的空 间内查找,如果能找到则直接返回,如果找不到则沿着__proto__属性找到原型 对象,再在原型对象中查找 message 属性。 三、原型链 在研究了对象原型和函数原型的关系后,我们还可以进一步深入思考:既 然原型对象是一个“对象”,那么这个对象有没有__proto__这个属性呢?当然有! return "PersonName="+this.perName+" PersonAge="+this.perAge; this.perName = perName; this.perAge = perAge; this.toString = function(){ }; function Person(perName,perAge,gender){ } var person = new Person("Tom", 20, "male"); console.log(person.__proto__); console.log(person.__proto__.__proto__); 执行结果: Person {} Object {} 说 明 person.__proto__ 所 指 向 的 对 象 是 由 Person 函 数 创 建 的 , 而 person.__proto__.__proto__所指向的对象是由 Object 函数创建的。 就对象的本质而言,任何一个对象都是以 new 构造器函数的方式创建的, 所以所有对象都和构造器函数共享原型对象。这样说可能你会有疑问,我可以通 过{属性名:属性值}的方式创建对象呀,这里并没有用到构造器函数呀?那么情看 下面的代码: var obj = {"myName":"Jerry","myAge":15}; console.log(obj.constructor); 5
————————————————————————————— console.log(obj.__proto__ === Object.prototype); “玩转”Java 系列 执行结果: function Object() true 说明从本质上来说,任何对象的创建都依赖对应的构造器函数,当然也包 含原型机制。既然如此,那么原型对象本身也是一个对象,这个对象也指向一个 原型对象,那么原型对象的原型对象也是对象,可以继续指向一个原型对象„„ 这就是原型链。 但原型链并不是无止境的,到 Object()函数为止。 var obj = {"myName":"Jerry","myAge":15}; console.log(obj.__proto__.__proto__); 执行结果: null 四、原型的作用 JavaScript 中原型有很广泛的用途,在此我们仅举两例,供大家参考。 1.格式化日期 在 JavaScript 中对日期格式化的支持不是很完善,需要我们自己弥补。但是 用到日期格式化的地方又很多,这毕竟是个基础操作,那如何能够一劳永逸的解 决这个问题呢? ①通过原型机制将格式化日期的函数添加到 Date()函数对象上 代码如下: // 对 Date 的扩展,将 Date 转化为指定格式的 String // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) // 例子: // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 6
“玩转”Java 系列 "M+" : this.getMonth() + 1, // 月份 "d+" : this.getDate(), // 日 "h+" : this.getHours(), // 小时 "m+" : this.getMinutes(), // 分 "s+" : this.getSeconds(), // 秒 "q+" : Math.floor((this.getMonth() + 3) / 3), // 季度 "S" : this.getMilliseconds() ————————————————————————————— // (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 //var time1 = new Date().format("yyyy-MM-dd HH:mm:ss"); // //var time2 = new Date().format("yyyy-MM-dd"); Date.prototype.Format = function(fmt) { // author: meizz } var o = { // 毫秒 }; if (/(y+)/.test(fmt)) for ( var k in o) return fmt; if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "") .substr(4 - RegExp.$1.length)); ②将上述代码保存到 js 文件中 ③使用时引入这个 js 文件即可调用 format()函数格式化日期 2.模拟继承 在 JavaScript 中没有类的概念,用于创建对象的构造器函数很类似于 Java 中的类。而面向对象中的很多思想在 JavaScript 中也只能模拟实现。 ①情景举例 声明一个 Person 构造器函数和一个 Student 构造器函数。 this.theName = theName; this.theAge = theAge; function Person(theName,theAge){ } function Student(theName,theAge,subject){ this.theName = theName; 7
“玩转”Java 系列 ————————————————————————————— this.theAge = theAge; this.subject; } 很明显,这两个函数从语义上来说存在着继承关系,学生是人的一种, Student 对象应该是 Person 对象的实例。同时给姓名和年龄赋值的语句在两个函 数中也是重复的。 所以模拟继承时我们需要解决两个问题: i.将 Student 中的重复代码使用 Person 来代替 ii.让 Student 对象是 Person 的实例,即 student instanceof Person 要返回 true ②提取重复代码 this.theName = theName; this.theAge = theAge; function Person(theName,theAge){ } function Student(theName,theAge,subject){ } Person.apply(this, arguments); this.subject; ③instanceof this.theName = theName; this.theAge = theAge; Person.apply(this, arguments); this.subject; function Person(theName,theAge){ } function Student(theName,theAge,subject){ } Student.prototype = Person.prototype; var student = new Student("Tom", 20, "Java"); console.log(student); console.log(student instanceof Person); 那么这是为什么呢?在 JavaScript 中,判断一个对象是否是某个构造器函数 的实例,就是看分别沿着对象和函数的原型链能否找到同一个原型对象。 8
分享到:
收藏