热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OOPinJavascript

写了几篇Vue入门的内容了,今天写点其它的放松一下,简单讲讲javascript中的面相对象。在面向对象的语言中,都有类的概念࿰

写了几篇Vue入门的内容了,今天写点其它的放松一下,简单讲讲Javascript中的面相对象。

在面向对象的语言中,都有类的概念,当然es6中开始Javascript中也有类的概念了,这里以es5为基础开始讲解,毕竟当前即使写的es6代码,一般还是会通过babel等转码器翻译到es5来执行的;

在js中虽然没有类的概念,但是我们却可以创建对象,一般创建对象有两种方式(这里指自定义对象):

1、使用构造函数

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

2、使用字面量

var p={//各种属性
}

这里着重介绍一下构造函数方式创建对象;以上面代码为例,function Person(){},这个和普通函数有什么区别么?其实除了命名约定外(建议首字母大写)声明上没有任何区别,主要是调用方式的不同,构造函数调用使用new 操作符,使用new 操作符调用构造函数,主要经过四个步骤:

1)、创建一个新的对象;

2)、把this指向新对象;

3)、给对象添加属性

4)、返回新对象

对我们上面的代码进行一点简单的修改:

function Person(){this.name= "zhangsan";this.age=10;
};
var p=new Person();
console.log(p.name,p.age) ;
//'zhangsan 10'

再对比上面使用new操作符调用构造函数的过程,是不是很容易理解输出的结果。

刚才也说了,构造函数和普通函数调用完全相同,那么是不是说可以把构造函数进行普通调用呢?当然可以,对我们的例子再次进行修改:

function Person(){this.name= "zhangsan";this.age=10;
};
//var p=Person();
Person();
console.log(window.name,window.age) ;
//'zhangsan 10'

不使用new操作符调用,不会经过上述四个步骤的隐式处理,所以这个时候不会有新对象的创建和this指向的改变,那么此时的this就指向了全局对象,在浏览器中即window对象,所以可以使用window.name来访问,得到正确的输出。

OOP三大特性:封装、继承、多态;这里只讲述在Javascript中如何进行继承(限es5)。

继承只是是代码重用的一种手段,那么在js中如何实现继承呢?

基于原型的继承

首先,函数也是对象,因为所有函数都是Function对象的实例,function 函数名,这种方式定义函数只是一种快捷方式,理论上和var 函数名= new Function(p1,body)等价,从这一点上看函数名其实就是指向函数的指针,但是两者不同的是,通过new Function来创建函数,会被js解释器解释两次,一次是声明时,第二次是对body部分的解析。

其次,任何一个函数创建后,都有一个原型对象,如:

声明一个Person函数,那么我们可以使用Person.prototype来打印一下其原型对象,可以看到其原型对象是一个Object类型,里面包括一个属性(不算__proto__,这个术语内部变量,是实例到原型的指针)constructor,指向了函数Person;另外原型对象本身就是另一个对象的实例(这是是Object函数的实例,=new Object()),任何一个对象的实例,都包含一个内部变量__proto__(chrome浏览器)(Object.create(null)创建的对象除外),指向创建这个对象实例的类型的原型,此处__prop__指向的是Object.prototype.而且可以看到Object.prototype上包含的几个方法(toString、valueOf等)。

上面说了任何一个对象的实例,都包含一个指向其构造函数原型的内部变量,那么我们创建一个Person 的实例:

可以看到是同一个,所以我们可以总结构造函数、实例、和构造函数原型之间的关系如下:

虽然画的是很难看,但是我觉得应该也表达出我要表达的意思了啊,?。

综上,构造函数的原型是一个对象,默认情况下,该对象是Object对象的实例;由于访问时,查找规则如下:先查找当前对象的实例属性,如果找到则返回,否则,查找__proto__指向的原型对象上的同名属性,找到则返回,否则继续原型对象的__proto__指向的对象上的同名属性,一直到Object 实例的__proto__,也就是Object.prototype为止,上述由__proto__构建的这个链接就叫做原型链。

请看如下代码:

function Person(){this.name="zhangsan"; this.myFriends=["zhangsan","lisi"]
}Person.prototype.getName
=function(){return this.name;
}
function Student(){}Student.prototype=new Person();var s1=new Student();
var s2=new Student();s1.myFriends.push("wangwu");
console.log(s1.myFriends);
??
console.log(s2.myFriends);
??

上面的代码我基于原型模式实现了一个简单的继承,那么上面的输出是什么呢?可以进行测试,发现输出全部是["zhangsna","lisi","wangwu"];为什么会出现这样的结果,我们不难分析出,我们为Student构造函数的原型重新指定了新的对象,那么此时这个对象(Person实例)就成了Student构造函数的原型对象,则其实例属性就变成了Student构造函数的原型属性,所以我们在通过Student实例访问其原型中引用类型属性 的时候会产生共享,从而出现上述输出。

这种输出结构和我们预期不符,如何处理这种问题呢,根据我们的查找规则,我们如果在本对象上查找到相对应的属性,不会去查找原型对象,基于这一点,我们只要覆盖原型上的对象即可,而最简单的方式就是借用构造函数,修改我们的代码如下:

function Person(){this.name="zhangsan"; this.myFriends=["zhangsan","lisi"]
}Person.prototype.getName
=function(){return this.name;
}
function Student(){Person.call(this);//关键调用,注意这里是普通调用,非构造函数调用,本次调用,通过call改变this指向为Student的实例

}Student.prototype
=new Person();var s1=new Student();
var s2=new Student();s1.myFriends.push("wangwu");
console.log(s1.myFriends);

console.log(s2.myFriends);

使用一次普通的方法调用,传递this,然后动态的在this上创建name和myFriends属性,这样实例在输出时就不会查找原型上的属性了,解决了输出不符合预期的问题。

但是上面的代码仔细分析还有什么问题呢?

1)、Student.prototype.constructor 指向了Person

2)、调用了两次Person方法,一次是构造函数调用,一次是普通调用

3)、借用构造函数方式,是创建新的实例属性覆盖原型属性,这样会创建额外的属性。

再次修改我们的代码,处理上面的问题:

function inherit(child,parent){function F(){}F.prototype=parent.prototype;//只要原型上的内容,而不是实例,这样就避免了实例上的属性,只继承原型上的属性(方法)F.prototype.constructor=child;//这里是强制修改constructor属性,让它指向子类型child.prototype=F.prototype;
}
function Person(){this.name="zhangsan"; this.myFriends=["zhangsan","lisi"]
}Person.prototype.getName
=function(){return this.name;
}
function Student(){Person.call(this);//关键调用,注意这里是普通调用,非构造函数调用,本次调用,通过call改变this指向为Student的实例

}
//Student.prototype=new Person();
inherit(Student,Person)var s1=new Student();
var s2=new Student();s1.myFriends.push("wangwu");
console.log(s1.myFriends);
console.log(s2.myFriends);

当然上述代码还是有些不完美,首先Student.prototype.constructor,应该是不可枚举的,我们这里是却可以。还有很多细节可能考虑不全,细节方面推荐大家啃一下《Javascript高级程序设计》这本圣经,本篇就这么多吧。

 


转载于:https://www.cnblogs.com/Johnzhang/p/7294632.html


推荐阅读
  • 本文详细解析了JavaScript中相称性推断的知识点,包括严厉相称和宽松相称的区别,以及范例转换的规则。针对不同类型的范例值,如差别范例值、统一类的原始范例值和统一类的复合范例值,都给出了具体的比较方法。对于宽松相称的情况,也解释了原始范例值和对象之间的比较规则。通过本文的学习,读者可以更好地理解JavaScript中相称性推断的概念和应用。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了在wepy中运用小顺序页面受权的计划,包含了用户点击作废后的从新受权计划。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • Final关键字的含义及用法详解
    本文详细介绍了Java中final关键字的含义和用法。final关键字可以修饰非抽象类、非抽象类成员方法和变量。final类不能被继承,final类中的方法默认是final的。final方法不能被子类的方法覆盖,但可以被继承。final成员变量表示常量,只能被赋值一次,赋值后值不再改变。文章还讨论了final类和final方法的应用场景,以及使用final方法的两个原因:锁定方法防止修改和提高执行效率。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
author-avatar
bliss
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有