------*本文默认读者已有面向对象语言(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)---面向对象分析以及带来的思考的更多相关文章

  1. C#构造方法(函数) C#方法重载 C#字段和属性 MUI实现上拉加载和下拉刷新 SVN常用功能介绍(二) SVN常用功能介绍(一) ASP.NET常用内置对象之——Server sql server——子查询 C#接口 字符串的本质 AJAX原生JavaScript写法

    C#构造方法(函数)   一.概括 1.通常创建一个对象的方法如图: 通过  Student tom = new Student(); 创建tom对象,这种创建实例的形式被称为构造方法. 简述:用来初 ...

  2. 悟透JavaScript(理解JS面向对象的好文章)

    引子 编程世界里只存在两种基本元素,一个是数据,一个是代码.编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力. 数据天生就是文静的,总想保持自己固有的本色:而代码却天生活泼,总想改变这个 ...

  3. 原生javascript模仿win8等待进度条。

    一.序言 一直很中意win8等待提示圆圈进度条.win8刚出来那会,感觉好神奇!苦于当时没思路,没去研究.通过最近网上找找资料,终于给搞出来了!先上Demo,献丑了!预览请看:win8进度条. 二.简 ...

  4. 用原生javascript模拟经典FC游戏公路争霸

    #用原生javascript模拟经典FC游戏公路争霸 前几天看了园子里面的随笔 [原生javascript开发仿微信打飞机小游戏](http://www.cnblogs.com/Mr-Nobody/p ...

  5. 第五章 JavaScript对象及初识面向对象

    第五章   JavaScript对象及初识面向对象 一.对象 在JavaScript中,所有事物都是对象,如字符串.数值.数组.函数等. 在JavaScript对象分为内置对象和自定义对象,要处理一些 ...

  6. 原生JavaScript运动功能系列(五):定时定点运动

    原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现 原生JavaScript运动功能系列(二):缓冲运动 原生JavaScript运动功能系列(三):多物体多值运动 原生JavaS ...

  7. 原生JavaScript运动功能系列(四):多物体多值链式运动

    原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现 原生JavaScript运动功能系列(二):缓冲运动 原生JavaScript运动功能系列(三):多物体多值运动 多物体多值链式 ...

  8. JavaScript常用,继承,原生JavaScript实现classList

    原文链接:http://caibaojian.com/8-javascript-attention.html 基于 Class 的组件最佳实践(Class Based Components) 基于 C ...

  9. 原生 Javascript 编写五子棋

    原文地址:原生 Javascript 编写五子棋 博客地址:http://www.extlight.com 一.背景 近一个月没写 Javascript 代码,有点生疏.正好浏览网页时弹出五子棋的游戏 ...

随机推荐

  1. exists查询中子表可以是

    exists查询中子表可以是’或则具体某一列 ,查询结果一致,因为exists只会返回 true或者false,一个boolean型的值

  2. asp.net之动态页面和静态页面的区别

    asp.net之动态页面和静态页面的区别 当我开始接触web开发的时候,首先学到的是html.css.js这一类网页语言,通过布局可以搭建出一个静态网站,效果也跟我们上网时经常看到的一些网站一样了.于 ...

  3. uniq和sort的用法

    uniq和sort都是按行操作的linux命令. sort按文本行排序,如下所示的log文件:直接sort log即可将其排序. 容易忽略的是sort -n命令,在如下例子中将看到 如果直接sort则 ...

  4. C++常量的引用 const

    如果是对一个常量进行引用,则编译器首先建立一个临时变量,然后将该常量的值置入临时变量中,对该引用的操作就是对该临时变量的操作.对C++常量引用可以用其它任何引用来初始化:但不能改变. 关于引用的初始化 ...

  5. php 解决大流量网站访问量问题

    当一个网站发展为知名网站的时候(如新浪,腾讯,网易,雅虎),网站的访问量通常都会非常大,如果使用虚拟主机的话,网站就会因为访问量过大而引起 服务器性能问题,这是很多人的烦恼,有人使用取消RSS等错误的 ...

  6. JavaScript Ajax + Promise

    AJAX 在现代浏览器上写AJAX主要依靠XMLHttpRequest对象: function success(text) { var textarea = document.getElementBy ...

  7. 关于webapp的一个webframe问题

    最近重启ios webapp的项目,将之前的框架拿过来发现出现了错误,错误出现在写JSAlart控件的WebFrame上,xcode会报WebFrame是未定义的错误.由于之前使用的是ios5的 sd ...

  8. php函数的初步使用

    通过调用函数,实现打印半金字塔.全金字塔.空心金字塔.菱形.空心菱形 调用例程 huaTuMain.php 被调用函数 huaTu.php

  9. github基础命令

    github被zf断断续续的墙掉,只能多试几次;习惯用svn了,作为git新手,把svn跟git命令对比了一下,瞬间发现好方便记忆了: (1)获取代码仓库克隆:https://github.com/c ...

  10. struts中如何将前台的值能在action中获取到

    如何获取值----三种方式(属性驱动,对象驱动,模型驱动)  A:属性驱动 必须生成get,set方法  B:对象驱动 给对象也必须生成get,set方法  c模型驱动 模型驱动需要action去实现 ...