关于面向对象
说到面向对象我可以猜到大家想到的都是面向女朋友(手动滑稽),其实大多数人都想到的是c++、java,但是我认为对于一名coder来说不论什么语言只要在适合面向对象(或者面向过程)的时候我们只需要用语言这项工具把这种思想表达出来即可。
关于Javascript中的面向对象
周围同事对于js的使用只是停留在插件所以只能自力更生,闲的时候在各个q群讨论,啃高程3、逛各大论坛、以及项目实践中挖坑埋坑前后经历一个月才搞清了些眉目。搞清了些眉目说难也不难可能是因为本人总爱拿以前常用的java来对比学习,又比较执拗,所以有些地方总是填不上坑。还是希望大家在学习的时候做适当的对比,尽快熟悉新的思想与模式。
说到js面向对象那么有俩兄弟我们是必须要搞清的,那就是对象原型:prototype与原型对象:_proto_。网上百度的资料一大堆很多很杂,大家找之前一定要明白自己的盲点,然后去找自己能理解接受的,循序渐进是很重要的。本人刚开始的时候直接拿着github上的图看了半天毫无收获,还白白浪费了时间。对于这俩者的解释,请看下表:
表 - 1
名称 | 解释 | 备注 |
---|---|---|
prototype | 指向函数原型(其中包括添加的变量和方法、指向自己的构造器、原型对象) | 只有函数拥有此属性 |
_proto_ | 指向构造器的原型属性 | 不论对象或者函数都有此属性 |
下面用一个简单的新建对象的例子来加深印象:
function ball(name){ this.name = name; } var basketball = new ball('nike-basketball'); console.log(basketball);
结果如下:
_proto_指向构造器的原型属性即Object(ball函数的prototype属性为Object),如下:
test-2.png
在_proto_这个对象中包含了构造器与构造器ball函数的_proto_,constructor中的prototype指向Object符合上述我们所说也符合表格中的描述,constructor中的_proto_指向function()是因为ball()也是由Function构造而来,所以这点也符合我们表格中的描述。
其实这个时候prototype和_proto_我们已经有了了解,其实到这可以结了,但是大家有没有想过表格中的俩行概念是从哪里来的?这俩句是否真的准确?当时学习的时候自己并没有认识到这个问题,这里的坑后面才填上。这里为了让大家不走弯路,我们来看看表 - 1中的概念到底从何而来,js代码执行的时候是否真的如表 - 1中描述的一样。
百度:js new的时候js引擎都干了什么事情,然后我们可以从中得到new的时候有如下操作:(以 var sf = new dota())
- 创建一个空空如也的新对象sf:
var sf = {}
- 将这个空的sf对象的_proto_成员指向了dota函数对象的prototype:
sf._proto_ = dota.prototype
- 利用call函数将sf对象的this指针指向dota:
sf.call(dota)
面向对象的三大特性无疑是封装、继承、多态,下面综合网上资源与自己的理解进行总结。
1> 封装
面向对象,那就意味着“万物皆对象”,假设我们都是从石头(stone)里蹦出来的,我们每个人也是对象,那我们应该怎样才能将自己与其他人分开呢?这里我们就得用到封装了。首先我们看看怎么从石头里出来(-。-),
var stone = { head: 'head', body: 'body', foot: 'foot' }; var person1 = {}; person1.head = 'person1_head'; person1.body = 'person1_body'; person1.foot = 'person1_foot'; var person2 = {}; person2.head = 'person2_head'; person2.body = 'person2_body'; person2.foot = 'person2_foot';
缺点显而易见,太麻烦,那我们能不能把重复的提取出来?
function stone(head , body , foot){ return { head: head, body: body, foot: foot } } var person1 = stone('head1' , 'body1' , 'foot1'); var person2 = stone('head2' , 'body2' , 'foot2');
这样子看起来好像不错啊,但是新的问题来了,person1与person2没联系,也就是说这俩个人看不出来是从盘古开天辟地时掉落的同一块石头里蹦出来的,再简单一点也就是person1不认识person2。how's going on?很简单,因为没有用到原型,在js中原型对象就好像石头,而我们“往外蹦”这个过程就是javascript提供的构造函数模式(所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上)。使用构造函数进行封装如上的test - 1图中代码所示。
2> 继承
js中继承有多种方式,简单的方式有以下4种:
-
使用对象冒充实现继承,可以实现多继承(实现原理:让父类的构造函数成为子类的方法,然后调用该子类的方法,通过this关键字给所有的属性和方法赋值)
funcion stone(name){ this.sname = name; } funciton person(name){ this.parent = stone; this.parent(name); delete this.parent; this.say = function(){ console.log(this.sname); }; } var person1 =new person('Tan'); person1.say();
-
采用call/apply,该方式不能继承原型链(改变函数内部的函数上下文this,使它指向传入函数的具体对象)
- 采用原型链的方式实现继承(实现原理:使子类原型对象指向父类的实例以实现继承,即重写类的原型,弊端是不能直接实现多继承)
- 采用混合模式实现继承
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.hi = function(){ console.log('hi'+this.name+'-'+this.age); }; Person.prototype.LEGS_NUM = 2; 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); this.className = className; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.hi = function(){ console.log('hi '+this.name+'-'+this.age+'-'+this.className); }; var jay = new Student('JAY' , '27' , 'class 3 grade 2'); console.log(jay); jay.hi(); jay.walk();
3> 多态
重写在继承中的后俩个方法中均可实现,重载暂时没有敲代码练习先暂缓笔记。
附录
先有Object还是先有Function?这个问题据说是中级js coder才会考虑的问题,那意味着我晋升了么?(手动滑稽)还是上例子来看。
function setName(obj){ obj.name = 'a'; } var person = new Object(); setName(person); console.log(person.name);
然后贴上在chrome中watch
var person = new Object();
这里Object的图,如下:
点开后显示如下:
watchFunctionPrototype
好了,先说结论,然后再按照图片结果分析。通过查资料加上自己思考,结论是:Object.prototype最最最原始的祖先,最先诞生, 然后Object.prototype构造出Function.prototype,接着Function.prototype构造出Object和Function。
现在来结合图片分析,watchObject中的_proto_指向function(),意味着Object的_proto_指向function()的构造器的原型(原因可以阅读以下我在上面关于面向对象中的内容),这就验证了Function.prototype构造出Object这句话的正确性,单击进入后我们发现funciton()的_proto_指向Object的构造器原型这也就验证了Object.prototype构造出Function.prototype这句话的正确性,到此本来可以结了,但是我们得有“打破砂锅问到底的精神”啊,我想知道祖先Object.prototype到底长啥样啊?然后让我们单击_proto_: Object来一探究竟。如下:
老祖先既没有constructor,也没有proto,真的是白手起家啊。关于网上说Object.prototype指向null这种说法我不论其错对,因为存在即合理,但是只要每种说法都能有自己的道理来支持,那就很不错。我还是小白,希望大家能一起讨论,一起进步!
杂谈
其实作为一门脚本语言,js所处的地位很尴尬。为什么这么说呢?因为各位同学不论是学c还是java或者其他语言都会多多少少用到js,也多多少少的有所涉猎,可能正因为简单易上手导致了人们对于前端工程师的认知停留在静态页面、不用怎么动脑子的层面上(一线城市这种情况很少,但是本人所处二线城市大多认知就是这样)。不敢说学习、交流环境有多好,但是真的和一线城市差很多。本人所在二线小公司那就更不用说了,但是我还是会励志成为一名优秀的前端工程师乃至全栈工程师,努力改变周围人对前端的看法!
有关javascript与nodejs面向对象的编程总结
JavaScript面向对象的程序设计——“创建对象”的注意要点
Javascript 面向对象编程(一):封装
Javascript面向对象编程(二):构造函数的继承
Javascript面向对象编程(三):非构造函数的继承
JS当中的new关键字都干了些什么?
作用域链与原型链
详解prototype与_proto_
网友评论