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接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
随机推荐
- 通过LRU实现通用高效的超时连接探测
编写网络通讯都要面对一个问题,就是要把很久不存活的死连接清除,如果不这样做那死连接最终会占用大量内存影响服务运作!在实现过程中一般都会使用ping,pong原理,通过ping,pong来更新连接的时效 ...
- Docker系列06—基于容器制作镜像并上传到Docker Registry
本文收录在容器技术学习系列文章总目录 1.制作镜像 1.1 镜像的生成途径 基于容器制作 dockerfile,docker build 本篇主要详细讲解基于容器制作镜像:基于dockerfile 制 ...
- C#单例模式的几种实现方式
一.多线程不安全方式实现 public sealed class SingleInstance { private static SingleInstance instance; private S ...
- MyCat做MySQL负载均衡(享学课堂,咕泡学院听课笔记)
不要用战术上的勤奋,掩盖战略上的懒惰. 一.数据库集群演示 演示的数据库的表分了三种, 1.配置表,存储一些配置文件,其他业务表需要关联读取,每个数据库都存储配置表的全部内容,即操作Mycat,所有集 ...
- TCP/IP,Web世界的基本规则
TCP/IP协议 TCP/IP 是因特网的通信协议.通信协议是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信.浏览器与服务器就是通过这个协议连接在互联网上的,还有电子邮 ...
- ROW_NUMBER() OVER()函数用法;(分组,排序),partition by
转载:https://www.cnblogs.com/alsf/p/6344197.html 1.row_number() over()排序功能: (1) row_number() over()分组排 ...
- Hibernate框架笔记01_环境搭建_API_CRUD
目录 1. Hibernate框架的概述 1.1 什么是框架 1.2 经典三层架构 1.3 Hibernate框架 2 Hibernate入门 2.1 下载Hibernate的开发包 2.2 创建项目 ...
- Java学习笔记之——IO
一. IO IO读写 流分类: 按照方向:输入流(读),输出流(写) 按照数据单位:字节流(传输时以字节为单位),字符流(传输时以字符为单位) 按照功能:节点流,过滤流 四个抽象类: InputStr ...
- C++系列总结——mutable关键字
介绍 mutable的中文意思是易变的,是C++的一个关键字.它的作用就是允许修改被const修饰的对象的成员变量. 常用场景 什么情况下我们会使用到mutable? 一般我们会用const修饰get ...
- 20190328-CSS样式一:字体样式font-、文本样式text-、背景图样式background-
目录 CSS参考手册:http://css.doyoe.com/ 1.字体简写:font:font-style || font-variant || font-weight || font-size ...