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接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
随机推荐
- 初探Java设计模式1:创建型模式(工厂,单例等)
Java 设计模式 一直想写一篇介绍设计模式的文章,让读者可以很快看完,而且一看就懂,看懂就会用,同时不会将各个模式搞混.自认为本文还是写得不错的,花了不少心思来写这文章和做图,力求让读者真的能看着简 ...
- Python爬虫实例:爬取豆瓣Top250
入门第一个爬虫一般都是爬这个,实在是太简单.用了 requests 和 bs4 库. 1.检查网页元素,提取所需要的信息并保存.这个用 bs4 就可以,前面的文章中已经有详细的用法阐述. 2.找到下一 ...
- ELK-logstash案例实战之读取日志输出到elasticsearch
简介:从日志文件中读取日志,输出到elasticsearch集群中 $ cd /home/es/logstash-/config $ vim test3_es.conf $ cd /home/es/l ...
- cache2go - cachetable源码分析
今天我们来看cachetable.go这个源码文件,除了前面介绍过的主要数据结构CacheTable外还有如下2个类型: 下面先看剩下2个类型是怎么定义的: CacheItemPair非常简单,注释一 ...
- NFS的安装
NFS的安装鸟哥地址:http://vbird.dic.ksu.edu.tw/linux_server/0330nfs_2.php 13.2 NFS Server 端的设定 既然要使用 NFS 的话, ...
- [三]java8 函数式编程Stream 概念深入理解 Stream 运行原理 Stream设计思路
Stream的概念定义 官方文档是永远的圣经~ 表格内容来自https://docs.oracle.com/javase/8/docs/api/ Package java.util.s ...
- Tomcat多实例部署
前言 以前总是采用很Low的方式太同一台服务器上部署多个Web应用,步骤是这样的:Copy Tomcat目录-->更改conf/server.xml三个端口号----->部署war包--- ...
- 记录:C++类内存分布(虚继承与虚函数)
工具:VS2013 先说一下VS环境下查看类内存分布的方法: 先选择左侧的C/C++->命令行,然后在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存 ...
- Python并发编程之消息队列补充及如何创建线程池(六)
大家好,并发编程 进入第六篇. 在第四章,讲消息通信时,我们学到了Queue消息队列的一些基本使用.昨天我在准备如何创建线程池这一章节的时候,发现对Queue消息队列的讲解有一些遗漏的知识点,而这些知 ...
- Collection集合。
Collection集合. java.util.Collection 接口. 没有索引 是所有单列集合的最顶层的接口,里面定义了所有单列集合共性的方法. 任意的单列集合都可以使用Collecion接口 ...