原生javascript难点总结(1)---面向对象分析以及带来的思考
------*本文默认读者已有面向对象语言(OOP)的基础*------
我们都知道在面向对象语言有三个基本特征 : 封装 ,继承 ,多态。而js初学者一般会觉得js同其他类C语言一样,有类似于Class这样的关键字可以让我们在js中更好的进行面向对象的操作。可事实并非如此。
严格地说,我们并不能称js是一种OOP语言,但是我们可以利用js里面的一些高级特性来进行OOP编程。
----封装
在js中,如何来创建一个对象呢?这非常简单,我们只需要new一个已封装好的函数(就是类C语言中的类),就可以实例化一个对象了。
那我们首先来构造这么一个"类",在构造之前必须知道一个类需要有"变量"和"方法",接着我们就来构造这个"类":
function Parent(){
this.name = "Parent";
this.sayName = function(){
console.log(this.name);
}
}
var p1 = new Parent();
p1.sayName();
怎么样,很简单吧?这样就封装好了一个"类"了。但是这个类看起来很笨重,因为名字是固定的,所以我们需要进行修改并扩展。
function Parent(name){
this.name = name;
this.sayName = function(){
console.log(this.name);
}
}
var p1 = new Parent("yxy");
p1.sayName(); //控制台打印yxy
这样我们封装的"类"就变得有变量,有方法,有外部参数,可复用。看上去已经非常完美了?

试着这样想想。每次创建一个对象,都会创建一个变量,同样也都会创建一个方法。而这个方法对所有对象都只是同一个方法效果,为什么你还要去对这个方法创建多次呢?学过java或c++的人可能会想,你怎么知道这个方法是被创建了多次,而不是引用的同一个呢?嗯?我们来做个测试。
function Parent(name){
this.name = name;
this.sayName = function(){
console.log(this.name);
}
}
var p1 = new Parent("yxy");
var p2 = new Parent("danshengou");
console.log(p1.sayName == p2.sayName); //false
利用"=="可以看到两个方法是不同的,那就是被创建了多次。所以当我们创建了多个对象后,每个对象的每个方法都是不同的!这显然会大大消耗内存,不利于web开发。那如何解决呢?
前面的文章中我提到的js中的"类"都是带双引号的,原因很简单,js不支持类(在ES6的规范中就可以支持了),但为了方便,我们可以称之为"伪类"。但在我们之前的例子中都还不能说得上是伪类!因为完全就没有方法的复用,不是吗?接下来我们会引入一个概念性很强的一个术语:"原型"。
原型,prototype,每一个函数都有一个原型属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。确实是非常不好理解。简单的说,原型属性就是通过调用构造函数而创建的那个对象实例的原型对象。如果你还不清楚,那我推荐你好好地看看<<javascript高级程序设计>>一书的第六章。
在这里先记住为什么我们要在封装中使用原型。如果你还对原型的概念模糊不清,不急,先理解使用它的好处。使用原型的好处是可以让所有对象实例共享它所包含的属性和方法。好像清晰多了?那我们来改装一下前面提到的那个例子。
function Parent(name){
this.name = name;
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
var p1 = new Parent("yxy");
p1.sayName(); //调用原型中的sayName方法,打印出yxy
var p2 = new Parent("danshengou");
console.log(p1.sayName == p2.sayName); //true
console.log(p1.sayName === p2.sayName); //true
在这个例子中,我们用原型模式构造了一个函数。我们实例化了两个对象,用"=="和"==="比较了它的值和地址,发现都是一样的。看来原型真的是很好用啊,大大节省了我们的内存空间。你可能会问,为什么不把变量也放到原型中呢?因为原型是所有对象所共享的,每个对象的属性必须是该对象自己持有的,如果放到原型中,那这些对象便没有任何区别了。这就是我们所谓的"组合继承"。
到目前为止,我们就成功地创建了一个比较完美的js"伪类"了。而且你应该对原型有了很大的了解。
----继承
在你了解了整个封装过程后,你对js可能很失望了,既然有这样复杂的封装,那继承肯定也很难了。
可是,继承的语法其实很简单。
我们先来看看一个继承:
function Parent(name){
this.name = name;
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
function Child(name,position){
this.position = position;
Parent.call(this,name);
}
Child.prototype = new Parent();
Child.prototype.sayPos = function(){
console.log(this.position);
}
var p1 = new Child("yxy","student");
p1.sayName();
p1.sayPos();
有两个地方很吸引我们眼球。
1.Parent.call(this,name)
2.Child.prototype = new Parent();
细讲可能容易绕晕,我从继承的概念入手,首先,子对象继承的是父对象的变量和方法。那我们可以很清晰地从代码作用范围看到,Parent.call(this,name)在构造函数中,显然是在继承变量。而Child.prototype = new Parent(),这个Parent对象不带参数地创建,显然是在继承原型。这样说是不是好理解多了?
Child.prototype = new Parent(),这条语句很简单,我不再去细究它。这里重点要讲的是Parent.call(this,name)这个东西。
Parent.call(this,name)还有另外几种写法:
1.Parent.call(this,arguments[0]);
2.Parent.apply(this,[name]);
3.Parent.apply(this,arguments);
虽然是四个写法都不同,但是效果都是一样的 : 将父类构造函数的上下文引用到子类的构造函数上下文中。
call和apply,都可以用来代替另一个对象调用一个方法。两者可将一个函数的对象上下文从初始的上下文改变为由this指定的新对象。又有点绕?简单地说,就是this对象在当前的上下文中执行了一次Parent函数,并且把参数(this后面那个参数)传递给Parent函数。
我们都知道函数怎么暴露自己内部的属性,就是把它执行一次,就可以在它的父级作用域中访问到。那理解这个apply,call的作用机理就很简单了。
继承语法很简单,但是其继承机制还是需要花时间去理解的。
终于我们把有关js面向对象的步骤给很详细的做了一次,可能大家觉得自己终于可以开始模拟一些有难度的面向对象的实例了。这一次我并不会反对大家,但是你难道没看出来少了什么吗?我来运行一下代码:
function Parent(name){
this.name = name;
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
function Child(name,position){
this.position = position;
//Parent.call(this,name);
Parent.apply(this,[name]);
}
Child.prototype = new Parent();
Child.prototype.sayPos = function(){
console.log(this.position);
}
var p1 = new Child("yxy","student");
console.log(p1.name); //yxy
console.log(p1.position); //student
嗯?我明明想的是将变量私有化啊,可是为什么能访问到呢?细心地人可能早就在继承那个部分的时候就想到了,函数只要一执行,我的变量,方法都会暴露在外面了!其实这根本就不是封装啊!只是装而已。
没错,js并没有private,public这些关键字来定义属性和方法的私有和公有性质。可能你会很失望很失望,前面做了那么多,得来的是一个半瘸子的面向对象。其实不光你这么想,很多开发人员也觉得,因此他们用复杂的名字来命名一些变量,以保证其不会那么容易被访问到。这不失为一种办法,但是难道就没有别的办法?
仔细想想,还有什么办法能将模拟private,public这样的操作呢?我给出一个例子和一个概念,然后大家可以在这上开始自己对js真正面向对象的思考。
概念 : "模块模式"
代码 :
var Parent = function(name,publicAge){
var myName = name;
return {
age : publicAge,
sayName : function(){
console.log(myName);
}
};
};
var p1 = Parent("yxy","20");
p1.sayName(); //yxy
console.log(p1.age); //20
console.log(p1.myName); //undefined
当你能理解封装中访问等级的时候,javascript真正的思考才刚刚开始...
下一篇文章将会讲到"由封装引出的模块化思考以及闭包的运用"。
原生javascript难点总结(1)---面向对象分析以及带来的思考的更多相关文章
- C#构造方法(函数) C#方法重载 C#字段和属性 MUI实现上拉加载和下拉刷新 SVN常用功能介绍(二) SVN常用功能介绍(一) ASP.NET常用内置对象之——Server sql server——子查询 C#接口 字符串的本质 AJAX原生JavaScript写法
C#构造方法(函数) 一.概括 1.通常创建一个对象的方法如图: 通过 Student tom = new Student(); 创建tom对象,这种创建实例的形式被称为构造方法. 简述:用来初 ...
- 悟透JavaScript(理解JS面向对象的好文章)
引子 编程世界里只存在两种基本元素,一个是数据,一个是代码.编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力. 数据天生就是文静的,总想保持自己固有的本色:而代码却天生活泼,总想改变这个 ...
- 原生javascript模仿win8等待进度条。
一.序言 一直很中意win8等待提示圆圈进度条.win8刚出来那会,感觉好神奇!苦于当时没思路,没去研究.通过最近网上找找资料,终于给搞出来了!先上Demo,献丑了!预览请看:win8进度条. 二.简 ...
- 用原生javascript模拟经典FC游戏公路争霸
#用原生javascript模拟经典FC游戏公路争霸 前几天看了园子里面的随笔 [原生javascript开发仿微信打飞机小游戏](http://www.cnblogs.com/Mr-Nobody/p ...
- 第五章 JavaScript对象及初识面向对象
第五章 JavaScript对象及初识面向对象 一.对象 在JavaScript中,所有事物都是对象,如字符串.数值.数组.函数等. 在JavaScript对象分为内置对象和自定义对象,要处理一些 ...
- 原生JavaScript运动功能系列(五):定时定点运动
原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现 原生JavaScript运动功能系列(二):缓冲运动 原生JavaScript运动功能系列(三):多物体多值运动 原生JavaS ...
- 原生JavaScript运动功能系列(四):多物体多值链式运动
原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现 原生JavaScript运动功能系列(二):缓冲运动 原生JavaScript运动功能系列(三):多物体多值运动 多物体多值链式 ...
- JavaScript常用,继承,原生JavaScript实现classList
原文链接:http://caibaojian.com/8-javascript-attention.html 基于 Class 的组件最佳实践(Class Based Components) 基于 C ...
- 原生 Javascript 编写五子棋
原文地址:原生 Javascript 编写五子棋 博客地址:http://www.extlight.com 一.背景 近一个月没写 Javascript 代码,有点生疏.正好浏览网页时弹出五子棋的游戏 ...
随机推荐
- H TML5 之 (3)转动的圆球
HTML5 练手之二,一个能够为之圆心转动的圆球,原理和时钟的非常像,只是要把握转动的时间控制,同时加入了点渐变色 HTML5 练手之二,一个能够为之圆心转动的圆球,原理和时钟的非常像,只是要把握转动 ...
- python基础知识四
函数是重用的程序段.它们允许你给一块语句一个名称, 然后你可以在你的程序的任何地方使用这个名称多次地运行这个语句块.这被成为调用函数.我们已经使用了许多内建的函数,比如len和range. 函数通过d ...
- C#缓存处理
第一种方式: 在ASP.NET中页面缓存的使用方法非常的简单,只需要在aspx页的顶部加这样一句声明即可: <%@ OutputCache Duration="60" Var ...
- Ajax结合Js操作灵活操作表格
Table页面: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head& ...
- 寒假的ACM训练(一)
今天开始ACM训练,选择了刘汝佳的<挑战编程>,暂时算是开始了. 测评的网址: http://www.programming-challenges.com 第一个题目是水题啦.3n+1. ...
- wamp不能使用phpmyadmin,提示“You don't have permission to access /phpmyadmin/ on this server.” 转载
换了win8之后wamp明显不怎么好用了,显示80端口被system占用,后是masql出现了403错误,多番百度谷歌找到了解决方案,这里与大家分享 当你安装完成wamp后,打开localhost或i ...
- android入门到熟练(三)----UI界面
1.TextView 以下只是一部分属性,还有很多属性需要在用到时候再说 <TextView android:textSize="24sp"//文字大小 android:te ...
- BOM 之 window
BOM 之 window 对象 在网页中定义的任何一个对象,变量和函数,都以 window 作为其 Global 对象,因此有权访问别的方法和属性 var age = 26; functi ...
- 【python之旅】python简介和入门
python简介: 一.什么是python python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了打发时间,决心开发一个新的脚本解释程序, ...
- 通过try、except和else的使用来使Python程序更加“强壮”
在执行的程序中,难免会碰到因为一些原因如输入输出导致致命性错误产生的情况(如因为输入的文件名错误而导致无法运行相关的代码.).此时你不希望程序直接挂掉,而是通过显示一些信息,使其平稳的结束.此时,就可 ...