前  言

絮叨絮叨

    学习了JS之后,不知道大家觉得怎们样呢?

    今天我们就来讲一下JS中最重要的一个环节,JavaScript中的面向对象编程OOP,这里的东西有点难,也有点绕。

    可是!

    不要灰心,现在我么就一起来学习一下吧。

一、 OOP基础

1.1什么是面向对象编程

1、语言的分类:
  ① 面向机器:汇编语言
  ② 面向过程:C语言
  ③ 面向对象:C++ Java PHP等
2、面向过程于面向对象
  ① 面向过程:专注于如何去解决一个问题的过程步骤。编程特点是由一个个的函数实现每一步的过程步骤,没有类和对象的概念。
  ② 面向对象:专注于由哪一个对象来解决这个问题。编程特点是出现了一个个的类,从类中拿到对象,由这个对象去解决这个问题。
  
  
  对于调用者来说,面向过程需要调用者自己去实现各种函数,
  而面向对象,只需要告诉调用者对象对象中具体方法的功能,而不需要调用者了解方法中的实现细节
  
  [面向对象的三大特征] :继承、封装、多态
  JS可以模拟实现继承和封装,但是无法模拟实现多态,所以我们说JS是一门基于对象的语言,而并非是面向对象的语言!

1.2类和对象

1、类:一类具有相同特征(属性)和行为(方法)的集合。
  eg:人类 --> 属性:身高、体重、性别;方法:吃饭、说话、走路

2、对象:从类中拿出具有确定属性值和方法的个体叫做对象
  eg:张三 --> 属性:身高180体重180 ; 方法:说话 -->我叫张三,身高180

3、类和对象的关系:
  类是抽象的,对象是具体的(类是对象的抽象化,对象是类的具体化)

  类是一个抽象的概念,只能说类有属性和方法,但是不能给属性赋具体的值。
  比如,人类有姓名,但是不能说人类的姓名叫什么。

  对象是一个具体的个例,是将类中的属性进行具体赋值而来的个体。
  比如,张三是人类的一个个体,可以说张三的姓名叫张三。
  也就是张三对人类的每一个属性进行了具体的赋值,那么张三就是有人类产生的一个对象
  
4、使用类和对象的步骤:
① 创建一个类(构造函数):类名必须使用大驼峰法则。即每个单词首字母都要大写

② 通过类,实例化(new)出一个对象。

function 类名(属性1){
this.属性1 = 属性1;
this.方法 = function(){
//方法中要调用自身属性,必须使用this.属性
}
} var obj = new 类名(属性1的具体值);
obj.属性; //调用属性
obj.方法; //调用方法

③ 注意事项:
  >>>通过类名,new出一个对象的过程,叫做“累的实例化”
  >>>类中的this,会在实例化的时候指向新new出的对象。
  所以,this.属性 this.方法 实际上是将属性和方法绑定在即将new出的对象上面。
  >>>在类中,要调用自身属性必须this.属性名。如果直接使用变量名,则无法访问对应的属性。
  >>>类名必须使用大驼峰法则,注意与普通函数区分。

5、两个重要属性:
  ① constructor:返回当前对象的构造函数。
  >>>zhangsan.contructor == Person; √

  ②instanceof:检测一个对象,是不是一个类的实例
  >>>lisi instanceof Person                 √ lisi是通过Person类new出的
  >>>lisi instanceof Person                 √ 所有对象都是Object的实例
  >>>Person instanceof Object           √ 函数本事也是对象

6、广义对象与狭义对象;
  ① 狭义对象:只有属性和方法,除此之外没有任何其他内容。
  var obj = {}    var obj = new Object();
  ② 广义对象:除了用字面量声明的基本数据类型之外,JS中万物皆对象。换句话说,只要能够添加属性和方法的变量,都可以称之为对象

以下是两个小栗子:

第一个:

var s = "123";              //不是对象
s.name = "aaa";
console.log(typeof(s)); //String
console.log(s.name); //undefined 字面量声明的字符串不是对象,不能添加属性

第二个:

var s = new String("123");   //是对象
s.name = "aaa";
console.log(typeof(s)); //Object
console.log(s.name); //"aaa" 使用new声明的字符串不是对象,可以添加属性和方法

以下是创建一个类的小栗子:

 <script type="text/javascript">
function Person(name,age){
this.name = name;
this.age = age; this.say = function(content){
//在类中访问类自身的属性,必须使用this.属性调用
alert("我叫"+this.name+",今年"+this.age+"岁啦!我说了一句话:"+content);
}
} var zhangsan = new Person("张三",18);
zhangsan.say("哈哈哈");
</script>


二、 成员属性和静态属性和私有属性

2.1成员属性和成员方法

  • 在构造函数中,总使用this.属性声明。或者在实例化出对象以后使用“对象.属性”追加的,都属于成员属性或成员方法也叫实例属性和实例方法;
  • 成员属性/方法,是属于由类new出的对象的。
  • 需要使用“对象名.属性名”调用。

2.2静态属性与静态方法

  • 通过“类名.属性名”、“类名.方法名”声明的属性和方法,称为静态属性、静态方法,也叫类属性和类方法;
  • 类属性/类方法,是属于类的(属于构造函数)
  • 通过“类名.属性名”调用
  • 成员属性是属于实例化出的对象的,只能使用对象调用。
  • 静态属性是属于构造函数的,只能使用类名调用。

2.3私有属性和私有方法

  • 在构造函数中,使用var声明的变量,私有属性;
  • 在构造函数中,使用function声明的函数,称为私有方法;
function Person(){
var num = 1; //私有属性
function func(){} //私有方法
}
  • 私有属性和私有方法的作用域,只在构造函数内容有效。即,只能在构造函数内部使用,在构造函数中,无论使用对象名还是类名都无法调用

以下是一个小栗子,简单粗暴的说明各种属性和方法:

<script type="text/javascript">
  function Person(name){
this.name = name;//声明成员属性 var sex = "男";
    //console.log(sex); this.sayTime = function(){
alert("我说的当前时间是"+getTime());
}
this.writeTime = function(){
alert("我写的当前时间是"+getTime());
}
function getTime(){
return new Date();
}
  } var zhangsan = new Person("张三");
zhangsan.age = 14;//追加成员属性
alert(zhangsan.name);//调用成员属性 Person.count = "60亿";//声明静态属性
alert(Person.count);//调用静态属性
var lisi = new Person("李四");
console.log(lisi.count);//Undefined 静态属性是属于类的,只能用类名调用 console.log(lisi.sex);
console.log(Person.sex); lisi.sayTime();
lisi.writeTime(); //alert(Person.name); //系统自带成员属性名
</script>
三、 用JS模拟实现封装

1、什么叫封装?
  ① 方法的封装:将内部的函数进行私有化处理,不对外提供调用接口,无法在类外部使用的方法,称为私有方法,即方法的封装。
  ② 属性的封装:将类中的属性进行私有化处理,对外不能直接使用对象名访问(私有属性)。
  同时需要提供专门用于设置和读取私有属性的set/get方法,让外部使用我们提供的方法,对属性进行操作。这就叫属性的封装。

2、注意封装不是拒绝访问,而是限制访问。要求使用者,必须使用我们提供的set/get方法进行属性操作,而不是直接拒绝操作。
  因此,单纯的属性私有化,不能称之为封装!必须要私有化之后,提供对应的set/get方法。

<script type="text/javascript">
function Person(){
var age = 0;
this.setAge = function(ages){
age = ages;
}
this.getAge = function(){
return age;
} /*
* 私有化的方法
*/
function getTime(){
return new Date();
}
}
</script>
四、 JS中的this指向

1、谁最终调用函数,this就指向谁!
  ① this指向谁,不应该考虑函数在哪声明,而应该考虑函数在哪调用!!!
  ② this指向的永远只可能是对象,而不可能是函数!!!
  ③ this指向的对象,叫做函数的上下文context,也叫函数的调用者。

2、this指向的规律!!!(跟函数的调用方式息息相关!)【this指向五大准则】
  ① 通过函数名()调用的,this永远指向window
  ② 通过 对象.方法 调用的,this永远指向对象
  ③ 函数作为数组中的一个元素,用数组下标调用的,this指向这个数组
  ④ 函数作为window内置函数的回调函数使用,this指向window
    setInterval setTimeout 等
  ⑤ 函数作为构造函数,使用new关键字调用,this指向新new出的对象。

如下:

<script type="text/javascript">
function func(){
console.log(this);
}
  //func(); var obj = {
name : "张三",
func : func
}
/*通过函数名()调用的,this永远指向window*/
func();
/*通过 对象.方法 调用的,this永远指向对象*/
obj.func();//狭义对象 window.onclick = function(){
  //document.getElementById("div1").onclick = function(){
      //func(); //最终还是使用func()调用,所以指向window
    //}
document.getElementById("div1").onclick = func; //广义对象
} /*函数作为数组中的一个元素,用数组下标调用的,this指向这个数组*/
var arr = [1,2,3,func,4,5,6];
arr[3](); /*函数作为window内置函数的回调函数使用,this指向window*/
setTimeout(func,1000); /*函数作为构造函数,使用new关键字调用,this指向新new出的对象。*/
var objs = new func();
</script>
五、 原型与原型链

5.1__proto__ 与 prototype

1、prototype:函数的原型对象
  ① 只有函数才有prototype,而且所有函数必然有prototype
  ② prototype本身也是一个对象!
  ③ prototype指向了当前函数所在的引用地址

2、__proto__:对象的原型!
  ① 只有对象才有__proto__,而且所有对象必然有__proto__
  ② __proto__也是一个对象,所以也有自己的__proto__,顺着这条线向上找的顺序,就是原型链
  ③ 函数、数组都是对象,都有自己的__proto__

3、实例化一个类,拿到对象的原理?
  实例化一个类的时候,实际上是将新对象的__proto__,指向构造函数所在的prototype

  也就是说:zhangsan.__proto__ == Person.prototype √
  
4、所有对象的__proto__延原型链向上查找,都将指向Object的prototype。
  Object的prototype的原型,指向null

5.2原型链的指向问题

研究原型链的指向,就是要研究各种特殊对象的__proto__的指向问题。

【原型链指向四大准则】
1、通过构造函数,new出的对象。新对象的__proto__指向构造函数的prototype
2、函数的__proto__,指向Function()的prototype
3、函数的prototype的__proto__指向Object的prototype
  (直接使用{}字面声明,或使用new Object拿到的对象的__proto__直接指向Object的prototype)
4、Object的prototype的__proto__,指向null
  (Object作为一个特殊函数,它的__proto__指向Function()的prototype)

以下有一个小栗子:

function Person(){
}
var zhangsan = new Person();

六、 原型属性与原型方法

1、成员属性与成员方法:
  this.name = ""; this.func = function(){}
  >>>属于实例化出的新对象,使用 对象.属性 调用
  
2、静态属性与静态方法:
  Person.name = ""; Person.func = function(){}
  >>>属于类的,用类名.属性调用
  
3、私有属性与私有方法:
  在构造函数中,使用var声明属性,使用function声明方法
  >>>只在类内部能够使用,外部无法使用任何方式调用。
  
4、原型属性与原型方法:
  Person.prototype.name = "";
  Person.prototype.func = function(){}
  >>>将属性或方法写到类的prototype上,在实例化的时候,这些属性和方法会进入到新对象的__proto__上,就可以使用对象名调用。
  
  也就是说,1和4使用对象名访问,2使用类名访问,3只能在函数内部使用
  
5、当访问对象的属性或方法时,会优先使用对象自身上的成员属性和成员方法。
  如果没有找到,则使用__proto__上面的原型属性很原型方法。如果依然没有继续延原型链查找,最后返回Undefined
  
6、习惯上,我们会将属性写为成员属性,讲方法定义为原型方法:
  例如:

function Person(){
this.name = "张三";
}
Person.prototype.say = function(){}

  原因:
    ① 原型属性在定义后不能改变,无法在实例化是进行赋值。所以属性不能使用原型属性。
      但是方法,写完以后基本不用改变,所以,方法可以使用原型方法。
    ② 实例化出对象后,属性全在对象上,方法全在原型上,结构清晰。
    ③ 使用for-in遍历对象时,会将方法和属性全部打印出来。
      而方法往往不需要展示,那么讲方法写在原型上,就可以使用hasOwnProperty将原型方法过滤掉
    ④ 方法写到prototype上,将更加节省内存。
    ⑤ 官方都这么写。
  
7、通过prototype扩展系统内置函数的原型方法

<script type="text/javascript">
function Person(name){
this.name = name;//成员属性
this.age = 15;
var num = 1; //私有属性
}
Person.count = "60亿"; //静态属性
Person.prototype.age = 14;//原型属性 var zhangsan = new Person("张三");
console.log(zhangsan); /*
* 通过prototype扩展系统内置函数的原型方法
*/
//给数组添加一个遍历打印所有值的方法
Array.prototype.eachAll = function(){
for(var i=0;i<this.length; i++){
console.log(this[i]);
}
}
var arr = [1,2,3,4,5,6,7];
arr.eachAll(); String.prototype.addAbc = function(){
return this+"abc";
}
console.log("123456".addAbc());
</script>
七、 实现继承的三种办法

继承:使用一个子类继承一个父类,那么子类可以自动拥有父类中的所有属性和方法,这个过程叫做继承
    >>>继承的方法,发生在两个类之间。

7.1扩展Object

1、扩展Object实现继承
  ① 声明一个父类:function Person(){}
  声明一个子类:function Son(){}
  ② 通过prototype给Object类添加一个扩展方法:
 
  ③ 分别拿到父类对象和子类对象:
    var p = new Person();
    var s = new Student();
  ④ 用子类对象,调用扩展方法,实现继承操作;
    s.extend(p);

2、实现继承的原理:
  通过循环将父类对象的所有属性和方法全部赋给子类对象。关键点在于for-in循环,及时不扩展Object,也能通过简单的循环实现操作
3、扩展Object继承的缺点:
  ① 无法通过一次实例化,直接拿到完整的子类对象。而需要先拿到父类和子类对象两个对象,再手动合并
  ② 扩展Object的继承方法,也会保留在子类的对象上。

<script type="text/javascript">
  function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
alert("我叫"+this.name);
}
}
function Student(no){
this.no = no;
this.study = function(){
alert("我在学习!");
}
} // var p = new Person("张三",12);
// var s = new Student("1234567");
// for(var i in p){
// s[i] = p[i];
// } Object.prototype.extend1 = function(parent){
for(var i in parent){
this[i] = parent[i];
}
}
var p = new Person("张三",12);
var s = new Student("1234567");
s.extend1(p); console.log(s); </script>

7.2使用原型继承

1、使用原型实现继承
  ① 定义父类:function Person(){}
    定义子类:function Son(){}
  ② 将父类对象,赋值给子类的prototype
    Son.prorotype = new Person();
  ③ 拿到子类对象时,就会将父类对象的所有属性和方法,添加到__proto__
    var s = new Son();

2、使用原型继承的原理
  将父类对象,赋值给子类的prototype,那么父类对象的属性和方法就会出现在子类的prototype中,
  那么,实例化子类时,子类的prototype又会到子类对象的__proto__中,父类对象的属性好方法,会出现在子类对象的__proto__中。
3、这种继承的特点:
  ① 子类自身的所有属性,都是成员属性;父类继承过来的的属性,都是原型属性
  ② 依然无法通过一部实例化拿到完成的子类对象

<script type="text/javascript">
function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
alert("我叫"+this.name);
}
}
function Student(no){
this.no = no;
this.study = function(){
alert("我在学习!");
}
}
Student.prototype = new Person("张三",14);
var s = new Student("12"); console.log(s); </script>

7.3使用call和apply和bind

 [call/bind/apply]

1、三个函数的作用:通过函数名调用这三个函数,可以强行将函数中的this指定为某个对象

2、三个函数的写法(区别):

  call写法:使用func.call(func的this指向的obj,func参数1,func参数2,...);
  apply写法:使用func.call(func的this指向的obj,[func参数1,func参数2,...]);
  bind写法:使用func.call(func的this指向的obj)(func参数1,func参数2,...);

3、三个函数的唯一区别,在于接受func的参数列表的方式不同,除此之外,功能上没有任何差异!

使用call/bind/apply实现继承

1、 定义父类:function Parent(name){}
  定义子类:定义子类时,在子类中使用三个函数,调用父类,将父类函数的this,指向为子类函数的this

2、实例化子类时,将自动继承父类属性

<script type="text/javascript">
  function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
alert("我叫"+this.name);
}
}
function Student(no,name,age){
this.no = no;
this.study = function(){
alert("我在学习!");
}
Person.call(this,name,age);
} var s = new Student(12,"张三",24);
console.log(s); </script>
八、 闭包
JS中的作用域

1、全局变量:函数外声明的变量称为全局变量;
  局部变量:函数内部使用var声明的变量,称为局部变量;
  
   在JS中之中有函数作用域,没有块级作用域!也就是说,if/for等有{}的结构体,并不能具备自己的作用域。
  
   所以,函数外部不能访问函数内部的局部变量(私有属性)。因为函数内部的变量,在执行完毕以后,就会被释放掉。
  
2、使用闭包,可以访问函数的私有变量!
  JS中,提供了一种“闭包”的概念:在函数内部,定义一个子函数,子函数可以访问副函数的私有变量。
  可以在子函数中进行操作,最后将子函数通过return返回。

3、闭包的作用:
  ① 可以在函数外部访问函数的私有变量;
  ② 让函数内部的变量,可以始终存在于内存中,不会在函数调用完成后立即释放。

<script type="text/javascript">
function func1(){
var num = 1;
function func2(){
return num;
}
return func2;
}
var num = func1()();
console.log(num);
</script>

详解面向对象编程——JavaScriptOOP的更多相关文章

  1. [转]Hadoop集群_WordCount运行详解--MapReduce编程模型

    Hadoop集群_WordCount运行详解--MapReduce编程模型 下面这篇文章写得非常好,有利于初学mapreduce的入门 http://www.nosqldb.cn/1369099810 ...

  2. 详解Python编程中基本的数学计算使用

    详解Python编程中基本的数学计算使用 在Python中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯 ...

  3. volatile关键字的详解-并发编程的体现

    xl_echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! 参 ...

  4. javascript this详解 面向对象

    在面向对象编程语言中,对于this关键字我们是非常熟悉的.比如C++.C#和Java等都提供了这个关键字     虽然在开始学习的时候觉得比较难,但只要理解了,用起来是非常方便和意义确定的.JavaS ...

  5. 信息安全-5:RSA算法详解(已编程实现)[原创]

    转发注明出处:http://www.cnblogs.com/0zcl/p/6120389.html 背景介绍 1976年以前,所有的加密方法都是同一种模式: (1)甲方选择某一种加密规则,对信息进行加 ...

  6. KVC/KVO原理详解及编程指南

    一.简介 1.KVC简介 2.KVO简介 二.KVC相关技术 1.Key和Key Path 2.点语法和KVC 3.一对多关系(To-Many)中的集合访问器方法 4.键值验证(Key-Value V ...

  7. 【转】 KVC/KVO原理详解及编程指南

    原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 前言: 1.本文基本不讲KVC/KVO的用法,只结合网上的资料说说对这种技术的 ...

  8. 转:KVC/KVO原理详解及编程指南

      作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 转载请注明出处 如果觉得文章对你有所帮助,请通过留言或 ...

  9. Java 中的泛型详解-Java编程思想

    Java中的泛型参考了C++的模板,Java的界限是Java泛型的局限. 2.简单泛型 促成泛型出现最引人注目的一个原因就是为了创造容器类. 首先看一个只能持有单个对象的类,这个类可以明确指定其持有的 ...

随机推荐

  1. vue中组件的四种方法总结

    希望对大家有用 全局组件的第一种写法 html: <div id = "app"> <show></show></div> js: ...

  2. 【1414软工助教】团队作业7——Alpha冲刺之事后诸葛亮 得分榜

    题目 团队作业7--Alpha冲刺之事后诸葛亮 往期成绩 个人作业1:四则运算控制台 结对项目1:GUI 个人作业2:案例分析 结对项目2:单元测试 团队作业1:团队展示 团队作业2:需求分析& ...

  3. 【Beta】阶段 第二次Daily Scrum Meeting

    每日任务 1.本次会议为第二次 Meeting会议: 2.本次会议在周二上午9:40,课间休息时间在禹州楼召开,召开本次会议为10分钟. 一.今日站立式会议照片 二.每个人的工作 (有work ite ...

  4. 201521123091 《Java程序设计》第9周学习总结

    Java 第九周总结 第九周的作业. 目录 1.本章学习总结 2.Java Q&A 3.码云上代码提交记录及PTA实验总结 1.本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异 ...

  5. Java学习3——java介绍

    Java程序运行过程: Java两种核心机制: Java虚拟机(Java Virtual Machine),提供程序运行的解释环境,使Java成为一种与平台无关的语言. Java虚拟机可以理解成一个以 ...

  6. 201521123010 《Java程序设计》第2周学习总结

    1. 本周学习总结 这周学习了在JAVA里各种数据类型的使用.各种运算符的使用.表达是的使用,还初步学习了枚举的用法,也掌握了一些枚举和switch语句结合的用法,还了解了一些字符串类.在实验课上也学 ...

  7. 201521123055 《Java程序设计》第14周学习总结

    1. 本章学习总结 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) 在自己建立的数据库上执行常见SQL语句(截图) 利 ...

  8. 201521123080《Java程序设计》第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 流(Stream): Stream是从起源(source)到接收(sink)的有序数据 按照流向分可以分为输入 ...

  9. Markdown 模板

    一个例子: 例子开始 1. 本章学习总结 今天主要学习了三个知识点 封装 继承 多态 2. 书面作业 Q1. java HelloWorld命令中,HelloWorld这个参数是什么含义? 今天学了一 ...

  10. Linux-hexdump命令调试event驱动—详解(13)

    hexdump: 查看文件的内容,比如二进制文件中包含的某些字符串,通常用来调试驱动用 1.调试 键盘驱动 讲解 当我们insmod挂载了键盘驱动后,找到键盘驱动被放在event1设备里, 此时没有按 ...