JavaScript—面向对象开发详解和垃圾回收
面向对象的概述
ECMAScript 有两种开发模式:1.函数式(过程化),2.面向对象(OOP)。
面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,传统ECMAScript中 没有类的概念(ES6开始有),因此它的对象也与基于类的语言中的对象有所不同。
js是基于对象,不是面向对象的。不具备描述事物的能力。
第一种创建对象的方式
创建一个对象,然后给这个对象新建属性和方法
如果一个函数作为一个对象的属性保存,那么我们称这个函数时这个对象的方法。调用这个函数就说调用对象的方法(method)
var box = new Object(); //创建一个 Object 对象
box.name = 'Lee'; //box对象中创建一个 name 属性并赋值
box.age = 100; //box对象中创建一个 age 属性并赋值
//box对象中创建一个 run()方法并返回值
box.run = function () {
return this.name + this.age + '运行中...';
};
console.log(box.run()); //调用box对象中的run方法
上面创建了一个对象,并且创建属性和方法,在 run()方法里的 this,就是代表 box 对象本身。
这种是 JavaScript 创建对象最基本的方法,但有个缺点,想创建一个类似的对象,就会产生大量的代码。
var box = new Object();
box.name = 'Lee';
box.age = 100;
box.run = function () {
return this.name + this.age + '运行中...';
};
console.log(box.run());
var box2 = box; //得到 box 的引用
box2.name = 'Jack';//直接改变了 name 属性
console.log(box2.run());
console.log(box.run()); //用 box.run()发现 name 也改变了
var box3 = new Object();
box3.name = 'Jack';
box3.age = 200;
box3.run = function () {
return this.name + this.age + '运行中...';
};
console.log(box3.run()); //这样才避免和 box 混淆,从而保持独立
为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题
function createObject(name, age) { //集中实例化的函数
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function () {
return this.name + this.age + '运行中...';
};
return obj;
}
var box1 = createObject('Lee', 100); //第一个实例
var box2 = createObject('Jack', 200); //第二个实例
console.log(box1.run()); // Lee100运行中...
console.log(box2.run()); //保持独立:Jack200运行中...
工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,因为根本无法搞清楚他们到底是哪个对象的实例。
function createObject(name, age) { //集中实例化的函数
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function () {
return this.name + this.age + '运行中...';
};
return obj;
}
var box1 = createObject('Lee', 100); //第一个实例
var box2 = createObject('Jack', 200); //第二个实例
console.log(box1.run());
console.log(box2.run()); //保持独立
console.log(typeof box1); //Object
//console.log(box1 instanceof box);//不能判断
console.log(box1 instanceof Object); //true 都是Object类型 无法区分谁到底是谁的
第二种创建对象的方式
ECMAScript 中可以采用构造函数(构造方法)可用来创建特定的对象。类型于 Object 对象(用函数来模拟面对对象的中的描述)。
//用js来描述人
function Person(){ //相当于构造器。
console.log("person run");
}
//通过描述进行对象的建立。 new.
var p = new Person(); // 这里创建Person对象的时候就会执行构造器里面的代码:person run
//动态给p对象添加属性。直接使用p.属性名即可。
p.name = "zhangsan";
p.age = 29;
//如果定义的p对象的属性赋值为一个函数,即是给p对象添加一个方法。
p.show = function(){
console.log("show :"+this.name+":"+this.age);
}
p.show(); //show :zhangsan:29
function Box(name,age){
this.name = name;
this.age = age;
this.run = function(){
return this.name+this.age+"运行中.....";
}
}
var box1 = new Box('Lee', 100); //new Box()即可
var box2 = new Box('Jack', 200);
console.log(box1.run());
console.log(box1 instanceof Box); //很清晰的识别他从属于 Box
使用构造函数的方法,即解决了重复实例化的问题,又解决了对象识别的问题,但问题是,这里并没有 new Object(),为什么可以实例化 Box(),这个是哪里来的呢
构造函数的方法,和使用工厂模式的方法他们不同之处
构造函数方法没有显示的创建对象(new Object());但它后台会自动var object = new Object();
直接将属性和方法赋值给 this 对象;this就相当于object
没有 renturn 语句,构造函数不需要返回对象的引用,它是后台自动返回的
构造函数的方法有一些规范
函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和普通函数);
通过构造函数创建对象,必须使用 new 运算符。
通过构造函数创建对象执行的过程
既然通过构造函数可以创建对象,那么这个对象是哪里来的,new Object()在什么地方执行了?执行的过程如下:
当使用了构造函数,并且 new 构造函数(),那么就后台执行了 new Object();
将构造函数的作用域给新对象,(即 new Object()创建出的对象),而函数体内的 this 就代表 new Object()出来的对象。
执行构造函数内的代码;
返回新对象(后台直接返回)。
关于 this 的使用
this 其实就是代表当前作用域对象的引用。如果在全局范围 this 就代表 window 对象,如果在构造函数体内,就代表当前的构造函数所声明的对象。
var box = 2; alert(this.box); //全局,代表 window
构造函数和普通函数的唯一区别,就是他们调用的方式不同。只不过,构造函数也是函数,必须用 new 运算符来调用,否则就是普通函数(函数名首字母无序大写)。
function Box(name, age) { //构造函数模式
this.name = name;
this.age = age;
this.run = function () {
return this.name + this.age + '运行中...';
};
}
var box = new Box('Lee', 100); //构造模式调用
console.log(box.run());
Box('Lee', 20); //普通模式调用,无效
可以使用对象冒充来调用
function Box(name, age) { //构造函数模式
this.name = name;
this.age = age;
this.run = function () {
return this.name + this.age + '运行中...';
};
}
var o = new Object();
Box.call(o, 'Jack', 200) //对象冒充调用,o这个对象冒充Box对象来运行
console.log(o.run());
探讨构造函数内部的方法(或函数)的问题,首先看下两个实例化后的属性或方法是否相等。
function Box(name, age) { //构造函数模式
this.name = name;
this.age = age;
this.run = function () {
return this.name + this.age + '运行中...';
};
}
var box1 = new Box('Lee', 100); //传递一致
var box2 = new Box('Lee', 100); //同上
console.log(box1.name == box2.name); //true,属性的值相等
console.log(box1.run == box2.run); //false,方法其实也是一种引用地址
console.log(box1.run() == box2.run()); //true,方法的值相等,因为传参一致
可以把构造函数里的方法(或函数)用 new Function()方法来代替,得到一样的效果,更加证明,他们最终判断的是引用地址,唯一性。
function Box(name, age) { //new Function()唯一性
this.name = name;
this.age = age;
this.run = new Function("return this.name + this.age + '运行中...'");
}
var box1 = new Box('Lee', 100); //传递一致
var box2 = new Box('Lee', 100); //同上
console.log(box1.name == box2.name); //true,属性的值相等
console.log(box1.run == box2.run); //false,方法其实也是一种引用地址
console.log(box1.run() == box2.run()); //true,方法的值相等,因为传参一致
我们可以通过构造函数外面绑定同一个函数的方法来保证引用地址的一致性,但这种做法没什么必要,只是加深学习了解:
function Box(name, age) {
this.name = name;
this.age = age;
this.run = run;
}
function run() { //通过外面调用,保证引用地址一致
return this.name + this.age + '运行中...';
}
var box1 = new Box('Lee', 100); //传递一致
var box2 = new Box('Lee', 100); //同上
console.log(box1.name == box2.name); //true,属性的值相等
console.log(box1.run == box2.run); //true,用的是同一个函数,所以地址相同
console.log(box1.run() == box2.run()); //true,方法的值相等,因为传参一致
虽然使用了全局的函数 run()来解决了保证引用地址一致的问题,但这种方式又带来了一个新的问题,全局中的 this 在对象调用的时候是 Box 本身,而当作普通函数调用的时候,this 又代表 window。
对象中的成员
实现私有成员
在java中,有成员的访问修饰符的概念:private,默认,prototype,public,但是在js中没有,只能模拟出私有成员,即通过作用域不同来体现,一般不这样用阅读性差
function Person(name,age){
this.name = name;
this.age = age;
//这是一个局部变量,如果将var去掉就变成全局变量了
//在js中,通过this把变量和方法与类关联上的,如果自己用var定义了一个变量或者直接定义了一个方法,会认为这个方法或变量与当前的类无关
//可以被看做是局部的变量和方法
var sex = "女";
setSex = function(){
sex = "男";
}
//通过this修饰方法
//通过成员方法调用局部方法修改局部变量
this.changeSex = function(){
setSex();
}
//通过成员方法返回局部变量的值
this.getSex = function(){
return sex;
}
}
//创建一个对象
var p = new Person("科比",34);
console.log(p.name+"****"+p.age); // 科比****34
console.log(p.sex);//结果是undefined,不能直接访问,
console.log(p.getSex());//女
p.changeSex();
console.log(p.getSex()); // 男
给类添加静态的属性和方法
function Person(name,age){
this.name = name;
this.age = age;
//这是一个局部变量,如果将var去掉就变成全局变量了
//在js中,通过this把变量和方法与类关联上的,如果自己用var定义了一个变量或者直接定义了一个方法,会认为这个方法或变量与当前的类无关
//可以被看做是局部的变量和方法
var sex = "女";
setSex = function(){
sex = "男";
}
//通过this修饰方法
//通过成员方法调用局部方法修改局部变量
this.changeSex = function(){
setSex();
}
//通过成员方法返回局部变量的值
this.getSex = function(){
return sex;
}
}
//添加静态属性
Person.sex = '男';
//添加静态方法
Person.run = function(){
console.log('run');
}
//调用静态属性和静态方法
console.log(Person.sex);
console.log(Person.run());
内存问题(垃圾回收GC)
JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。其他语言比如 C 和 C++,必须手工跟踪内存使用情况,适时的释放,否则会造成很多问题。
而 JavaScript 则不需要这样,它会自行管理内存分配及无用内存的回收。我们需要做的只是要将不再使用的对象设置null即可
JavaScript 最常用的垃圾收集方式是标记清除。垃圾收集器会在运行的时候给存储在内存中的变量加上标记。然后,它会去掉环境中正在使用变量的标记,而没有被去掉标记的变量将被视为准备删除的变量。
最后,垃圾收集器完成内存清理工作,销毁那些带标记的值并回收他们所占用的内存空间。
垃圾收集器是周期性运行的,这样会导致整个程序的性能问题。比如 IE7 以前的版本,它的垃圾收集器是根据内存分配量运行的,比如 256 个变量就开始运行垃圾收集器,这样,就不得不频繁地运行,从而降低的性能。
一般来说,确保占用最少的内存可以让页面获得更好的性能。那么优化内存的最佳方案,就是一旦数据不再有用,那么将其设置为 null 来释放引用,这个做法叫做解除引用。这一做法适用于大多数全局变量和全局对象。
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
var o = {
name : 'Lee'
};
o = null; //解除对象引用,等待垃圾收集器回收
JavaScript—面向对象开发详解和垃圾回收的更多相关文章
- Java Garbage Collection基础详解------Java 垃圾回收机制技术详解
最近还是在找工作,在面试某移动互联网公司之前认为自己对Java的GC机制已经相当了解,其他面试官问的时候也不存在问题,直到那天该公司一个做搜索的面试官问了我GC的问题,具体就是:老年代使用的是哪中垃圾 ...
- 详解CMS垃圾回收机制
原创不易,未经允许,不得转载~~~ 什么是CMS? Concurrent Mark Sweep. 看名字就知道,CMS是一款并发.使用标记-清除算法的gc. CMS是针对老年代进行回收的GC. CMS ...
- 详解Python垃圾回收机制
http://www.qytang.com/cn/list/28/417.htmhttp://www.qytang.com/cn/list/28/416.htmhttp://ww 引用计数 Pytho ...
- webpack环境搭建开发环境,JavaScript面向对象的详解,UML类图的使用
PS:因为所有的设计模式都是基于面向对象来完成的,所以在讲解设计模式之前先来过一下面向对象都有哪些知识点 搭建开发环境 初始化npm环境 下载安装nodejs安装即可,nodejs自带npm管理包,然 ...
- JavaScript 面向对象继承详解
题记 由于js不像java那样是完全面向对象的语言,js是基于对象的,它没有类的概念.所以,要想实现继承,一般都是基于原型链的方式: 一.继承初探 大多数JavaScript的实现用 __proto_ ...
- 关于javascript面向对象的详解!
认识面向对象 1.面向对象中的概念 一切事物皆对象 对象具有封装和继承特性 信息隐藏 2.基本面向对象 3.函数构造器构造对象 深入了解面向对象 第一种书写格式 第二种书写格式
- [转]javascript console 函数详解 js开发调试的利器
javascript console 函数详解 js开发调试的利器 分步阅读 Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 console 对象,使用该 ...
- JavaScript 面向对象开发知识基础总结
JavaScript 面向对象开发知识基础总结 最近看了两本书,书中有些内容对自己还是很新的,有些内容是之前自己理解不够深的,所以拿出来总结一下,这两本书的名字如下: JavaScript 面向对象精 ...
- 基于H5的微信支付开发详解
这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
随机推荐
- Python爬虫入门教程 10-100 图虫网多线程爬取
图虫网-写在前面 经历了一顿噼里啪啦的操作之后,终于我把博客写到了第10篇,后面,慢慢的会涉及到更多的爬虫模块,有人问scrapy 啥时候开始用,这个我预计要在30篇以后了吧,后面的套路依旧慢节奏的, ...
- 前端笔记之JavaScript(九)定时器&JSON&同步异步/回调函数&函数节流&call/apply
一.快捷位置和尺寸属性 DOM已经提供给我们计算后的样式,但是还是觉得不方便,因为计算后的样式属性值都是字符串类型. 不能直接参与运算. 所以DOM又提供了一些API:得到的就是number类型的数据 ...
- Jenkins+Maven+Gitlab+Tomcat 自动化构建打包、部署
一.环境需求 本帖针对的是Linux环境,Windows或其他系统也可借鉴.具体只讲述Jenkins配置以及整个流程的实现. 1.JDK(或JRE)及Java环境变量配置,我用的是JDK1.8.0_1 ...
- 痞子衡嵌入式:第一本Git命令教程(5)- 提交(commit/format-patch/am)
今天是Git系列课程第五课,上一课我们做了Git本地提交前的准备工作,今天痞子衡要讲的是Git本地提交操作. 当我们在仓库工作区下完成了文件增删改操作之后,并且使用git add将文件改动记录在暂存区 ...
- Eclipse查看JDK源码(非常详细)
Eclipse查看源码的方式其实很简单,打开项目,然后按着ctrl,然后把鼠标光标移动到你想查看的方法或者对象上,这时会出现一条下划线,然后点击鼠标左键就可以进入那个方法或者对象了.但是有的情况下会出 ...
- Jaccard相似度在竞品分析中的应用
上个月对一个小项目的效果进行改进,时间紧,只有不到一周的时间,所以思考了一下就用了最简单的方法来做,效果针对上一版提升了5%左右,跟大家分享一下(项目场景用的类似的场景) 项目场景:分析一个产品的竞品 ...
- SmoOne——开源免费的企业移动OA应用,基于.Net
一.SmoOne是什么一个开源的移动OA应用 二.语言C# 三.开发环境Visual Studio 四.开发平台Smobiler Designer 五.功能该应用开源代码中包含注册.登录.用户信息等基 ...
- [android] 创建模拟器遇到的常见错误
1.错误提示: invalid command line sdk安装目录有中文添加ANDROID_SDK_HOME环境变量,指向sdk安装目录2.模拟器无法安装应用模拟器开启其实是开启了的程序占用这个 ...
- Php导出百万数据的优化
导出数据量很大的情况下,生成excel的内存需求非常庞大,服务器吃不消,这个时候考虑生成csv来解决问题,cvs读写性能比excel高.测试表student 数据(大家可以脚本插入300多万测数据.这 ...
- Java开发笔记(四十)日期与字符串的互相转换
前面介绍了如何通过Date工具获取各个时间数值,但是用户更喜欢形如“2018-11-24 23:04:18”这种结构清晰.简洁明了的字符串,而非啰里八唆依次汇报每个时间单位及其数值的描述.既然日期时间 ...