[Javascript 高级程序设计]学习心得记录9 js面向对象
感觉最难的部分就是面向对象了,大学期间学习的是面向过程的c/c++,工作之后也没有深入了解过面向对象,通过这次的学习和回顾,也算是对面向对象有了新的认识。不过,就我在书上学到了结合个人理解随便说说,很可能有理解错误或者不准确的地方。js的对象和其他语言的对象并不完全一样,可以理解为散列表。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。
首先需要知道一个定义,特性。我觉得可以理解为对象的属性的属性,用于描述属性的各种特征。可以用来对对象的属性进行管理,比如属性数据的读写和访问权限。但是我看完了之后,觉得这个功能虽然强大,不过目前用不到,以后碰到的时候再补充吧。下面好好讲讲创建对象和继承。
一,创建对象
前面一篇文章提到了创建对象的一些方法,new Object和对象字面量。创建一个对象还行,创建多个的话代码量就太多了,所以现在有很多其他的创建对象的方法。
1,工厂模式
一句话,用函数创建对象。两句话,创建一个新对象,然后把这个对象指针赋给新变量。
function createPerson(name, age, job){var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function(){alert(this.name);}; return o;}var person1 = createPerson("Nicholas", 29, "Software Engineer");var person2 = createPerson("Greg", 27, "Doctor");person1.sayName(); //"Nicholas"person2.sayName(); //"Greg"
但是这样创建的话,每个对象的类型都是object,无法识别区别。
2,构造函数模式
像Object和Array这样的原生构造函数,可以通过new调用来直接创建对象。而我们也可以自定义构造函数,从而自定义对象类型的属性和方法。
function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.sayName = function(){alert(this.name);}; }var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");person1.sayName(); //"Nicholas"person2.sayName(); //"Greg"alert(person1 instanceof Object); //truealert(person1 instanceof Person); //truealert(person2 instanceof Object); //truealert(person2 instanceof Person); //truealert(person1.constructor == Person); //truealert(person2.constructor == Person); //truealert(person1.sayName == person2.sayName); //false
用new操作符调用构造函数实际上经过了四步:(1),创建一个新对象。(2),将构造函数的作用域赋给新对象。(3),执行构造函数的代码,给新对象增加属性。(4),返回新对象。这两个生成的对象都有一个构造函数属性constructor,指向构造函数Person。对于构造函数这一类特殊函数,还有值得说明的地方。
首先,构造函数是可以直接调用的,直接把属性赋给window对象。
function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.sayName = function(){alert(this.name);};}var person = new Person("Nicholas", 29, "Software Engineer");person.sayName(); //"Nicholas"Person("Greg", 27, "Doctor"); //adds to windowwindow.sayName(); //"Greg"var o = new Object();Person.call(o, "Kristen", 25, "Nurse");o.sayName(); //"Kristen"
其次,构造函数有一个问题就是,如果像上面那么定义的话,会定义很多sayName函数,但是都是做同一件事情(每次实例构造函数都会执行内部代码)。明显不太好,把方法定义放在函数外部可以解决这个问题。
function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.sayName = sayName;}function sayName(){alert(this.name);}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");person1.sayName(); //"Nicholas"person2.sayName(); //"Greg"alert(person1 instanceof Object); //truealert(person1 instanceof Person); //truealert(person2 instanceof Object); //truealert(person2 instanceof Person); //truealert(person1.constructor == Person); //truealert(person2.constructor == Person); //truealert(person1.sayName == person2.sayName); //true
不过这样也有问题,直接打字书上的原话:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是,如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个定义的引用类型就没有丝毫的封装性可言了。好在这些问题都可以通过原型模式解决。
3,原型模式
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。实际上,prototype就是原型对象,不必在构造函数中定义对象实例的信息,而是可以直接把这些信息添加到原型对象中。
function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "Software Engineer";Person.prototype.sayName = function(){alert(this.name);};var person1 = new Person();person1.sayName(); //"Nicholas"var person2 = new Person();person2.sayName(); //"Nicholas"alert(person1.sayName == person2.sayName); //truealert(Person.prototype.isPrototypeOf(person1)); //truealert(Person.prototype.isPrototypeOf(person2)); //true//only works if Object.getPrototypeOf() is availableif (Object.getPrototypeOf){alert(Object.getPrototypeOf(person1) == Person.prototype); //truealert(Object.getPrototypeOf(person1).name); //"Nicholas"}
个人理解,这种方法就是改良工厂方法,相当于把工厂方法中的新建对象和赋值的过程,变成了把原型对象作为原型对象属性赋给新对象的过程。同时我们这个时候修改实例的属性时,会在实例下添加属性。而在我们调用实例的属性时,会先查找新对象新增加的属性,然后找找原型对象的属性。可以通过in,判断属性是否存在。可以通过hasOwnProperty(),判断属性是在实例还是原型。(就不举例了)
function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "Software Engineer";Person.prototype.sayName = function(){alert(this.name);};var person1 = new Person();var person2 = new Person();person1.name = "Greg";alert(person1.name); //"Greg" ?from instancealert(person2.name); //"Nicholas" ?from prototype
可以直接重写原型对象,这样更方便,不过这样一来,本来从构造函数获取的原型对象的构造函数属性就变成Object了,需要重定义一下。
function Person(){}Person.prototype = {constructor : Person,name : "Nicholas",age : 29,job: "Software Engineer",sayName : function () {alert(this.name);}};var friend = new Person();alert(friend instanceof Object); //truealert(friend instanceof Person); //truealert(friend.constructor == Person); //truealert(friend.constructor == Object); //false
与此同时,重写原型对象会切断现有原型和任何之前已经存在的对象实例之间的联系;它们引用的仍然是最初的原型。
然而,原型对象方法有一个致命缺点,对于包含引用类型的属性,修改实例对象会影响原型对象从而影响所有对象。
function Person(){}Person.prototype = {constructor: Person,name : "Nicholas",age : 29,job : "Software Engineer",friends : ["Shelby", "Court"],sayName : function () {alert(this.name);}};var person1 = new Person();var person2 = new Person();person1.friends.push("Van");alert(person1.friends); //"Shelby,Court,Van"alert(person2.friends); //"Shelby,Court,Van"alert(person1.friends === person2.friends); //true
4,组合使用构造函数模式和原型模式
讲了这么多有问题的方法,讲一个能用而且最常用的方法吧。组合了构造函数模式和原型模式,构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性。
function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.friends = ["Shelby", "Court"];}Person.prototype = {constructor: Person,sayName : function () {alert(this.name);}};var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");person1.friends.push("Van");alert(person1.friends); //"Shelby,Court,Van"alert(person2.friends); //"Shelby,Court"alert(person1.friends === person2.friends); //falsealert(person1.sayName === person2.sayName); //true
5.动态原型模式
这个我看了几遍都没看懂,看了知乎上面的一个解释瞬间明白了。可以注意到第四种组合方法里面,原型对象是采用重写方法的,当必须采用字面量方法时,那就会碰到构造函数模式的问题,多次定义相同函数,这个时候加个判断问题就迎刃而解了。
function Person(name, age, job){//propertiesthis.name = name;this.age = age;this.job = job;//methodsif (typeof this.sayName != "function"){Person.prototype.sayName = function(){alert(this.name);};}}var friend = new Person("Nicholas", 29, "Software Engineer");friend.sayName();
此外还有寄生构造函数模式和稳妥构造函数模式就不说了。
二,继承
继承和创建有很多类似的地方,但是现在用的少,以后想起来再写吧。
[Javascript 高级程序设计]学习心得记录9 js面向对象相关推荐
- [Javascript 高级程序设计]学习心得记录11 js的BOM
BOM(浏览器对象模型)是web中js的核心,而BOM的核心对象就是window对象.在浏览器中,window对象有双重角色,它既是通过js访问浏览器的一个接口,又是规定的Global对象.此外,还有 ...
- [Javascript 高级程序设计]学习心得记录10 js函数表达式
在前面说对象的时候已经提到了函数对象,对函数的定义参数的传递包括通过argumentd.callee实现递归.这篇博客我会继续深入讲解js中的函数表达式. 一,闭包 关于闭包的概念,可以先看看http ...
- [Javascript 高级程序设计]学习心得记录 函数参数传递与引用
最近开始啃js的红宝书:<Javascript 高级程序设计>,偶有心得,记录一下. 先上代码 function howManyArgs() {alert(arguments.length ...
- [Javascript 高级程序设计]学习心得记录2 Javascript的垃圾回收机制
Javascript 是自动垃圾收集机制,不需要像c/c++的开发人员一样担心内存泄漏问题.这种垃圾收集机制通过找出那些不再使用的变量,释放其占用的内存从而达到垃圾回收的效果.而如何如何找出那些不再使 ...
- [Javascript 高级程序设计]学习心得记录6 变量和作用域
js的变量和其他语言的变量区别还是挺大的,它只是在特定时间用于保存特定值的一个名字而已,js的变量高度灵活,同时又很容易出问题,需要专门学习. 一,基本类型和引用类型的值 基本类型值指数据的五种基本类 ...
- [Javascript 高级程序设计]学习心得记录3 根据对象数组的属性进行排序
配合sort(),将排序的标准属性传入排序函数,在进行排序的时候将该属性取出来就行了. function createComparisonFunction(propertyName) {return ...
- JavaScript高级程序设计学习笔记(三)
分享一下第五章(引用类型)的笔记,内容比较多,我拆成了两部分,今天这部分是关于Object.Array.Date和RegExp类型的. 以下的笔记是书上一些我以前学习的时候,没有太重视的js基础知识, ...
- javascript高级程序设计学习笔记
javascript高级程序设计,当枕头书已经好久了~zz 现在觉得自己在js的开发上遇到了一些瓶颈,归根究底还是基础太薄弱,所以重新刷一遍js高程希望有更新的认识. 一.javascript简介 ...
- javascript高级程序设计学习之数值转换 |Number(),parseInt(),parseFloat()
2019独角兽企业重金招聘Python工程师标准>>> 将非数值转换成数值的函数有三个:Number(),parseInt(),parseFloat(); 小记tip:Number( ...
最新文章
- Java 获取 Julian Day (Calendar)
- LeetCode 167. Two Sum II - Input array is sorted
- 华为手机媒体音量自动静音_华为手机的音量键原来这么牛逼,这五大玩法,97.777%的人不知道...
- SpringMVC+Json构建基于Restful风格的应用
- JavaScript-遍历数组
- ASP.NET MVC 4 (二)控制器
- 10g gtx 光纤通信测试_光纤通信optisystem实验
- 安装 mariadb全套教程
- 2019北邮网安考研经验
- hbase时间同步造成region severs的问题
- Shell实现俄罗斯方块小游戏
- SSL-ZYC 牛车
- 2021年安全员-C证(陕西省)考试资料及安全员-C证(陕西省)新版试题
- from Crypto.Cipher import AES报错解决【WindowsLinux】
- 编写C语言程序:输入一个n,计算从1到n的和
- 来,我教你怎么优雅删除数据
- 鱼眼:一:一分钟详解鱼眼镜头标定基本原理及实现
- PAT甲级 1032 Sharing (25分) 测试点5陷阱
- 产品经理必备的五款办公软件
- 智能社 Javascript之Node.Js-经典全套教程(价值300元)