JS中OOP之模拟封装和继承和this指向详解

时间:2023-02-12 08:55:05

大家好,今天我带大家学习一下js的OOP,

大家都知道,面向对象有三个基本特征,继承,封装和多态,面向对象的语言有那么几种,C++,PHP,JAVA等,而功能强大的JS可以模拟实现面向对象的两大特征,继承,和封装,无法实现多态,所以当有人对问你,js是一门面向对象的语言,你应该反驳他,js是一门基于对象的语言,不是面向对象,他就会觉得你很有学问。哈哈!

首先,我们学习一下,什么是对象呢,那就先了解一下类和对象是什么?

1、类:一类具有相特征(属性)和行为(方法)的集合
eg:人类--->属性,身高,体重,性别,方法:吃饭,说话,走路
2、对象:从类中,拿出具有确定属性值和方法的个体。
eg:张三--->属性,身高180 体重180,方法:说话-->我叫张三,身高180

大家知道什么是类什么是对象了吗?

两者有什么关系呢?

类是抽象的,对象是具体的,类是对象的抽象化,对象是类的具体化
类是一个抽象的概念只能说类有属性和方法,但是不能给属性附具体的值,
对象是一个具体的个例,是将类中的属性,进行具体赋值而来的个体
eg:张三是人类的一个个体,可以说张三的姓名叫张三,也就是张三对人类的每一个属性进行了具体的赋值,那么张三就是由人类产生的一个对象。

是不是通俗易懂呢。

下面我来使用一下类和对象:

首先创建一个类,这里要注意的是,类名必须使用大驼峰法则,即每一个单词的首字母都要大写,这是我说知道唯一一个使用大驼峰法则的地方

方法:

function 类名(属性1){
this.属性1=属性1;
this.方法=function(){
方法中要调用自身属性,必须使用this.属性。
}
}

function Person(name,age){
this.name=name;
this.age=age;
this.say=function(content){
//在类中,要访问类自身的属性,必须使用this,属性调用。
alert("我叫"+this.name+",今年"+this.age+"了!"+content);
}
}

这样我们就创建好了一个类

下面我们实例化出一个对象,

通过 obj=new类名(属性1的具体值);
obj.属性;调用属性
obj.方法();调用方法

var zhangsan = new Person("张三",18);

下面我们要学习一下三个属性和三个方法

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

【静态属性与静态方法】
2、 通过“类名.属性”、“类名.方法”声明的属性和方法,称为静态属性,静态方法。也就类属性和类方法:

类属性/类方法,是属于类的(属于构造函数的)
通过“类名.属性名”调用

3、成员属性时属于实例化出的对象的,只能使用对象调用。
静态属性时属于构造函数的,只能使用类名调用。

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

举个栗子,

function Person(name){
this.name=name;//声明成员属性
var sex = "男";
this.satTime=function(){
alert("说出当前时间是"+getTime());
}
this.writeTime = function(){
alert("我写了当前时间是"+getTime());
}
function getTime(){
return new Data();
} }
var zhangsan = new Person("张三");
zhangsan.age = 14;//追加成员属性 //alert(zhangsan.age);//调用成员属性
Person.count = "60亿";//声明静态属性
console.log(Person.count);//调用静态属性
var lisi = new Person("李四");
console.log(lisi.count);//undefined 静态属性时属于类的,只能用类名调用
console.log(lisi.sex);
console.log(Person.sex);

通俗易懂的,好了下面我们重点来了,就是标题的两大内容之一,封装

  封装

1、 什么叫封装?
① 方法的封装: 将类内部的函数进行私有化处理,不对外提供调用接口,无法在类外部使用的方法,称为私有方法,即方法的封装。
② 属性的封装: 将类中的属性进行私有化处理,对外不能直接使用对象名访问(私有属性)。 同时,需要提供专门用于设置和读取私有属性的set/get方法,让外部使用我们提供的方法,对属性进行操作。 这就叫属性的封装。
2、 注意: 封装不是拒绝访问,而是限制访问。 要求调用者,必须使用我们提供的set/get方法进行属性的操作,而不是直接拒绝操作。
因此,单纯的属性私有化,不能称为封装!必须要私有化之后,提供对应的set/get方法。

上面是知识点,大家知道了吗,简单的讲,就是将属性保存起来,只让用户通过提供的方法去访问,修改数据,当然,如果你是前端高手,你就可以无视,直接修改源码。这当然是外话了 --!!

下面我们来一段实例代码,让你们感受下封装的过程

function Person(name, age1) {
this.name = name;
// this.age = age;
var age = 0;
this.setAge = function(ages) {
if(ages > 0 && ages <= 120) {
age = ages;
} else {
alert("年龄赋值失败!");
}
}
// 当实例化类拿到对象时,可以直接通过类名的()传入年龄,设置私有属性
if(age1 != undefined) this.setAge(age1);
this.getAge = function() {
  return age;
}
//私有化的方法,只能在类内部被其他方法调用,而不能对外提供功能。 这就是方法的封装! function getTime() {
return new Date();
}
}
// var zhangsan = new Person("张三");
// zhangsan.setAge(99);
// alert("张三的年龄是:"+zhangsan.getAge()); var lisi = new Person("李四", 999);
//lisi.setAge(110);
alert("李四的年龄是:" + lisi.getAge()); function Person(){
var age = 0;
this.getAge=function(){
return age;
}
this.setAge=function(age1){
age = age1;
}
function func(){}
}

封装其实很简单,当我们把属性封装起来之后,我们就不能直接访问类的私有属性,我们只可以通过getAge(),setAge()访问设置age属性,这样就模拟实现了第一个功能,封装,

继承

等等,我忽略了一个重要的问题,那就是this指向问题,大家可能问上面我们出现了this,this到底是什么呢,现在我们详细解释一下

简单来说

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

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

以上是我所总结的this指向,this也就这几中情况了,其他还没遇到过,如果有其他,请大佬在评论告诉我,先谢了,

在举个栗子

    var fullname = 'John Doe';
var obj = {
fullname: 'Colin Ihrig',
prop: {
fullname: 'Aurelio De Rosa',
getFullname: function() {
return this.fullname;
}
}
};
console.log(obj.prop.getFullname());
// 函数的最终调用者 obj.prop var test = obj.prop.getFullname;
console.log(test());
// 函数的最终调用者 test() this-> window obj.func = obj.prop.getFullname;
console.log(obj.func());
// 函数最终调用者是obj var arr = [obj.prop.getFullname,1,2];
arr.fullname = "JiangHao";
console.log(arr[0]());
// 函数最终调用者数组

吃了这个栗子,是不是清楚多了,不要被歪的所迷惑,你就找谁最终调用了这个函数就行,

然后是原型和原型链,这个是很重要的知识点,大家一定要掌握好,因为继承需要准备很多知识点,大家耐心学完,

【__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

【原型链的指向问题】
研究原型链的指向,就是要研究各种特殊对象的__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

这是点先放这里了解释的很清楚,大家看了之后就清楚原型了,继承就需要几个知识点而已,

一、扩展Object实现继承

     ①声明父类
function Parent(){}
声明子类
function Son(){}
②通过prototype给object类添加一个扩展方法:
Object.prototype.extend=function(parent){
for(var i in parent){
this[i]=parent[i];
}
}
③分别拿到父类对象,和子类对象;
var p = new Person();
var s = new Student();
④用子类对象,调用扩展方法,实现继承操作;
s.extend(p);
3、实现继承的原理:
通过循环,将父类对象的所有属性和方法全部付给子类属性,关键点在for-in循环,即使不扩张object,也能通过简单的循环实现操作
4、扩展Object继承的缺点,
①,无法通过一次实例化,直接拿到完整的子类对象,而需要先拿到父类对象和子类对象两个对象,在手动合并
②扩展Object的继承方法,也会保留只在子类的对象上
/**/
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("我在学习!");
}
} Object.prototype.extend1 = function(parent){
for(var i in parent){
this[i]=parent[i];
}
}
var p = new Person("张三",12);
var s = new Student("123456");
s.extend1(p);
console.log(s);

扩展继承我感觉很简单,以后要用的时候,只需要拿出扩展的extend1方法,就可以继承了,这个名字大家不要抄啊,可以.a.b都行,高兴就好

二 使用原型实现继承

     ①定义父类:
function Parent(){}
定义子类:
function Son(){}
②将父类对象,赋值给子类的prototype
son.prototype = new Person();
③拿到子类对象,就会将父类对象的所有属性和方法,添加到__proto__
var s = new Son();
使用原型继承的原理:
将父类对象,赋值给子类的prototype,那么父类对象的属性和方法就会出现在子类的prototype中。那么。实例化子类时,子类的prototype又回到子类对象的__proto__中,最终,父亲对象的属性和方法,会出现在子类的对象的__proto__中;
这种继承的特点
①子类自身的所有属性,都是成员属性,父类继承过来的属性,都是原型属性;
②依然无法通过一步实例化拿到完整的子类对象。
        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("zhangsan",14);
var s = new Student();
console.log(s);

原型继承是比较简单的,子类的Prototype指向父类new的对象,就可以实现继承啦!

三,

[call/bind/apply]
1、三个函数的作用:通过函数名调用着三个函数,可以强行将函数中的this指定为某个对象。
2、三个函数的写法(区别):
call写法:func.call(func的this指向的obj,func参数1,func参数2,...);
apply写法:func.apply(func的this指向的obj,[func参数1,func参数2,...]);
bind写法:func.bind(func的this指向的obj)(func参数1,func参数2,...);
3、三个函数的唯一区别,在与接受func的参数列表的方式不同,除此之外,功能上没有任何差异!!

[使用call/bind/apply实现继承]
1、实现步骤:
①定义父类
function Perent(name){}
②定义子类时,在子类中使用三个函数,调用父类,将父类函数的this,指向为子类函数的this:
function Son(no,name){
this.no=no;
Person,call(this,name);
}
③实例化子类时,将自动继承父类属性,
var s = new Son(12,"zhangsan")

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("我在学习");
}
Person.call(this.name,age);
}
var s =new Student(12,"张三",24);
console.log(s);

以上就是js的继承和封装,多态是无法实现的,上面两个方法在实际运用中能起到很不错的效果,让我们少码很多字,

今天就到这里,谢谢大家

JS中OOP之模拟封装和继承和this指向详解的更多相关文章

  1. java 学习笔记——类之间的关系之封装、继承与多态的详解

    封装 一个封装的简单例子 封装就是把对象的属性(状态)和方法(行为)结合在一起,并尽可能隐蔽对象的内部细节,成为一个不可分割的独立单位(即对象),对外形成一个边界,只保留有限的对外接口使之与外部发生联 ...

  2. C&plus;&plus;的三大特性:封装、继承和多态性的详解

    封装 所谓封装就是将某些东西包装盒隐藏起来,让外界无法直接使用,只能通过某些特定的方式才能访问.封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是通过外部接口以及特定的访问权限来使 ...

  3. Js中的window&period;parent &comma;window&period;top&comma;window&period;self &comma;window&period;openner详解

    在应用有frameset或者iframe的页面时,parent是父窗口,top是最*父窗口(有的窗口中套了好几层frameset或者iframe),self是当前窗口, opener是用open方法 ...

  4. js中字符串编码函数escape&lpar;&rpar;、encodeURI&lpar;&rpar;、encodeURIComponent&lpar;&rpar;区别详解

    1 escape()函数 定义和用法 escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串. 语法 escape(string) 参数 描述 string 必需.要被转义或 ...

  5. js中OOP小指南

    js中OOP小指南 在指南中,我将尝试解析以面向对象规范聚焦的es6的新特性. 首先, 什么是设计模式 范例是某个事务的例子或模型,在某种情况下,按照一种模式创建了计算机程序. 什么是面向对象 显然你 ...

  6. koa 基础(十七)原生 JS 中的类、静态方法、继承

    1.app.js /** * 原生 JS 中的类.静态方法.继承 * es5中的类和静态方法 */ function Person(name, age) { // 构造函数里面的方法和属性 this. ...

  7. 面向对象(OOP)--OOP基础与this指向详解

      前  言            学过程序语言的都知道,我们的程序语言进化是从“面向机器”.到“面向过程”.再到“面向对象”一步步的发展而来.类似于汇编语言这样的面向机器的语言,随着时代的发展已经逐 ...

  8. java中4种修饰符访问权限的区别及详解全过程

    java中4种修饰符访问权限的区别及详解全过程 http://jingyan.baidu.com/article/fedf0737700b3335ac8977ca.html java中4中修饰符分别为 ...

  9. iOS回顾笔记(03) -- 自定义View的封装和xib文件的使用详解

    iOS回顾笔记(03) -- 自定义View的封装和xib文件的使用详解 iOS开发中,我们常常将一块View封装起来,以便于统一管理内部的子控件.如iOS回顾笔记(02)中的"书&quot ...

随机推荐

  1. FusionCharts的使用方法(php)

    我们公司一直用这个图表统计, 最近整理了一下相关文档,提供大家学习. 首先可以看看 http://www.cnblogs.com/xuhongfei/archive/2013/04/12/301688 ...

  2. PHP MVC 中的MODEL层

    Model层,就是MVC模式中的数据处理层,用来进行数据和商业逻辑的装封 三.实现你的Mode层 Model层,就是MVC模式中的数据处理层,用来进行数据和商业逻辑的装封,进行他的设计的时候设计到三个 ...

  3. 集群NAS技术架构

    http://blog.csdn.net/liuaigui/article/details/6422700

  4. HDU3923-Invoker-polya n次二面体

    polya定理.等价类的个数等于∑颜色数^置换的轮换个数 不可翻转的串当中.直接计算∑m^(gcd(n,i)) ,这里gcd(n,i)就是第i个置换的轮换数. 翻转的情况再分n奇偶讨论. n次二面体都 ...

  5. elasticsearch2

    简单认为是可以在命令行下访问url的一个工具 curl是利用URL语法在命令行方式下工作的开源文件传输工具,使用curl可以简单实现常见的get/post请求. curl -x 指定http请求的方法 ...

  6. win7下的IP-主机名映射

    今天学了个技巧,win7下有个目录:C:\Windows\System32\drivers\etc 该目录下有个文件: hosts 在这个文件里面我们可以映射IP-主机名: 127.0.0.1 loc ...

  7. 查找被锁对象的名称、sid,锁定的类型-1123

    select lk.sid,lk_obj.object_id,obj.object_name,DECODE(LK.LMODE,0,'None',1,'Null',2,'Row-S (SS)',3,'R ...

  8. 基于 svn 服务器及 cocoapods-repo-svn 插件进行组件化私有库的创建

    一.准备 组件化 随着业务需求的增长,在单工程 MVC 模式下,app 代码逐渐变得庞大,面对的高耦合的代码和复杂的功能模块,我们或许就需要进行重构了,以组件化的形式,将需要的组件以 pod 私有库的 ...

  9. 机器学习——KMeans聚类,KMeans原理,参数详解

    0.聚类 聚类就是对大量的未知标注的数据集,按数据的内在相似性将数据集划分为多个类别,使类别内的数据相似度较大而类别间的数据相似度较小,聚类属于无监督的学习方法. 1.内在相似性的度量 聚类是根据数据 ...

  10. structure

    https://wenku.baidu.com/view/a82b12d62dc58bd63186bceb19e8b8f67c1cef04.html?sxts=1541829436140