JavaScript 面向对象开发知识基础总结

最近看了两本书,书中有些内容对自己还是很新的,有些内容是之前自己理解不够深的,所以拿出来总结一下,这两本书的名字如下:

  • JavaScript 面向对象精要
  • JavaScript 启示录

如果对于 JavaScript 面向对象编程理解不够深的话,第一本书还是强烈推荐的。第二本书比较适合初中级的开发者阅读。对各种知识点都有代码示例。内容中规中矩。

1.JavaScript 中的变量类型和类型检测

C#和Java等编程语言用栈存储原始类型,用堆存储引用类型,JavaScript则完全不同:它使用一个变量对象追踪变量的生存期。原始值被直接保存在变量对象内,而引用值则作为一个指针保存在变量对象内,该指针指向实际对象在内存中的存储位置。

1.1原始类型

在 JavaScript 中有5中原始类型,分别如下:

类型表达式 类型描述
boolean 布尔,值为 false或者 true
number 数字,值为任何整型或者浮点数值
string 字符串,值由单引号或者双引号括出的单个字符或者连续字符(JavaScript不区分字符类型)
null 空类型,该原始类型仅有一个值:null
undefined 未定义,该原始类型仅有一个值:undefined(undefined会被赋给一个还没有初始化的变量)

所有原始类型的值都有字面形式,字面形式是不被保存在变量中的值。

//string

var name='zhiqiang';
var selection='a'; //number var count=235;
var cost=1.51; //boolean var found=true; //null var object=null; //undefined var flag=undefined;
var ref; console.log(ref); //undefined

原始类型的变量直接保存原始值(而不是一个指向对象的指针)。当将原始值赋值给一个变量时,该值将被复制到变量中。也就是说,如果你使一个变量等于另一个时,每个变量有它自己的一份数据拷贝。

示例代码如下:

var color1='red';
var color2=color1;

内存中的保存形式,如下图:

1.2引用类型

引用类型是在JavaScript中找到最能接近类的东西。引用值是引用类型的实例,也是对象的同义词。属性包含键(始终是字符串)和值。如果一个属性的值是函数,它就被称为方法。JavaScript中函数其实是引用值,除了函数可以运行以外,一个包含数组的属性和一个包含函数的属性没有区别。

创建引用类型的两种方式看下面的一段代码:

//第一种使用new操作符
var obj1 = new Object(); //
var obj2 = obj1; //第二种
var obj3 = {}

以上两种创建对象的方式并没有本质的区别,是等价的。

那么当我们创建了一个对象,且发生了赋值的时候,在内存中发生了什么呢?

看下图:

1.当发生了new操作的时候,先在内存中开辟一块空间,存放创建的对象,并且使obj1指向这块开辟的空间;

2.引用类型发生赋值的时候,仅仅是引用地址指向了内存中的同一块区域;

JavaScript语言有"垃圾回收"功能,所以在使用引用类型的时候无需担心内存分配。但是为了防止"内存泄露"还是应该在不实用对象的时候将该对象的引用赋值为null。让"垃圾回收"器在特定的时间对那一块内存进行回收。

1.3內建类型的实例化

JavaScript中的內建类型如下:

类型 类型描述
Array 数组类型,以数字为索引的一组值的有序列表
Date 日期和时间类型
Error 运行期错误类型
Function 函数类型
Object 通用对象类型
RegExp 正则表达式类型

內建引用类型有字面形式。字面形式允许你在不需要使用new操作符和构造函数显式创建对象的情况下生成引用值。(包括字符串,数字,布尔,空类型和未定义);

1.4函数的字面形式

创建函数的三种方式:

//第一种函数声明
function abc(){
console.log(1);
} //使用构造函数的形式
var value = new Function('','console.log(1)'); //函数表达式
var a = function(){
console.log(1);
};

使用构造函数的方式创建函数,不易读,且调试不方便,不建议使用这种方式创建函数。

1.5正则表达式的字面形式

在JavaScript中使用正则表达式有两种方式:

var a1 = /\d+/g;//使用字面形式
var a2 = new RegExp('\\d+','g');//使用构造函数的形式

在JavaScript中建议使用字面形式的正则表达式,因为不需要担心字符串中的转义字符。比如上面示例代码中字面形式使用\d而构造函数使用的是\\d

1.6类型检测

1.6.1原始类型的检测

使用typeof运算符可以完成对原始类型的检测,看下面的一段代码:

上面的代码中有一段比较特殊就是

typeof null   //object

这里其实是不准确的,如果我们要判断一个值是否为空类型的最佳的方式是直接和null进行比较

console.log(value === null);

=====之间的最主要的区别就是前者在进行比较的时候会进行类型转化,而后者不会;

console.log(5==5);//true
console.log('5'==5);//false
console.log('5'===5);//fasle

1.6.2鉴别引用类型

JavaScript中对于引用类型的检测较为复杂。对于函数类型的引用使用typeof返回的是Function,而对于非函数的引用类型返回的则是object。所以在JavaScript中鉴别引用类型的类型引入了instanceof

instanceof操作符以一个对象和一个构造函数作为参数;

function a (){}
var b = {};
var c =[]; typeof a // function
typeof b //object
typeof c //object a instanceof Function //true
b instanceof Object //true
c instanceof Array //true

1.6.3鉴别数组

有前一小结可以知道鉴别数组类型可以使用instanceof。但是在ECMAScript5中,Array对象提供了更好的方式来鉴别一个变量是不是数组类型。

var a = [];
var b =3;
Array.isArray(a); //true
Array.isArray(b); //false

注意:IE8及更早的IE不支持该方法

1.6.4原始封装类型

JavaScript中的原始封装类型共有3种。这些特殊引用类型的存在使得原始类型用起来和对象一样方便。当读取字符串,数字,布尔类型时,原始封装类型被自动创建。

var a ='qwer';
var firstChar = a.chatAt(0);
console.log(firstChar);// q

在JavaScript引擎中发生了如下的过程:

var a ='qwer';
var temp = new String(a);
var firstChar = temp.chatAt(0);
temp =null;
console.log(firstChar);// q

由于要把字符串当成对象使用,JavaScript引擎创建了一个字符串实体让charAt可以工作,字符串对象(temp)的存在仅仅用于该语句(temp.chatAt(0)),随后便被销毁(temp =null)。

我们可以简单测试一下

var a ='qwer';
a.temp ='122';
console.log(a.temp); //undefined

上面代码的过程如下:

var a ='qwer';
var temp = new String(a);
temp.temp ='122';
temp=null; var temp = new String(a);
console.log(a.temp); //undefined
temp=null;

由上面的代码我们可以看到我们实际上是在一个立刻就会被销毁的对象上而不是字符串上添加了一个新属性。当试图访问这个属性时,另一个不同的临时对象被创建,而新属性并不存在。虽然原始封装类型会被自动创建,但是在这些值上进行instanceof检查对应类型的返回值却都是false;

var a ='1234';
var num = 10; a instanceof String //false
num instanceof Number //false

这是因为临时对象仅在值被读取的时候创建,随即被销毁。instanceof操作符并没有读取到任何东西,也没有临时对象的创建,因此它告诉我们这些值并不属于原始封装类型;

但是我们可以手动创建原始封装类型,但是此时使用typeof没办法检测对象的实际类型,只能够使用instanceof来检测变量类型;

2.JavaScript 中的函数

在JavaScript中函数就是对象。函数不同于其他对象的决定性特点是,函数存在一个被称为[[Call]]的内部属性。内部属性无法通过代码访问而是定义了代码执行时的行为。ECMAScript为JavaScript的对象定义了多种内部属性,这些内部属性都用双重中括号来标注。

[[Call]]属性是函数独有的,表明该对象可以被执行。由于仅函数拥有该属性,ECMAScript定义了typeof操作符对任何具有[[Call]]属性的对象返回**function**>

2.1定义函数的两种方式

2.1.1函数声明

函数声明是以function关键字开头,这也是区别函数声明和函数表达式的一个重要的方法。函数声明会在编译期对整个作用域内的变量名字进行查询,函数声明的变量被提升至上下文的顶部,也就是说可以先使用函数后声明它们。

abc();
function abc(){
console.log(2);
}

2.1.2函数表达式

函数表达式是function关键字后边不需要加上函数的名字。这种函数被称为匿名函数。因为函数对象本身没有名字,所以函数表达式通常会被一个变量或者属性引用。

abcd()
var abcd=function(){
console.log(1)
}; var aaa={
abc:function(){ }
}

函数表达式只能通过变量引用,无法提升匿名函数的作用域。在使用函数表达式之前必须先创建它们,否则代码会报错。看示例代码的运行结果:

2.2JavaScript函数的参数

JavaScript函数参数与很多语言函数参数不一样。你可以给函数传递任意数量的参数却不造成错误。那是因为函数实际上被保存在一个被称为arguments的类似数组的对象中。arguments可以自由增长来包含任意个数的值,这些值可以通过数字索引来引用。argumentslength属性会告诉你目前有多少个值(函数接受了多少个参数)。

arguments是一个类数组对象,它本身并不具有JavaScript数组应该具有的全部的属性和方法。

这里我们思考一个问题,我们怎么将一个类数组转化为真正的数组?

  1. 最基本的我们应该想到的是创建一个原始的空数组,使用for循环将类数组中的每一项添加到新的数组中;
  2. 如果使用Zepto或者jQuery的话,会有一个toArray()的方法可以使用;
  3. ES6有Array.from(arrayLike[, mapFn[, thisArg]])可以将类数组转化为数组对象;
  4. 最后一种也是最高级的一种方法就是使用原型的方式;

借用原型的方式把一个类数组转化为真正的数组的示例代码:

function abc(){
console.log(arguments);
var arrTemp = [].slice.apply(arguments); //相当于Array.prototype.slice == [].slice
console.log(arrTemp);
console.log(Array.isArray(arrTemp));
} abc(1,2,3);

输出结果:

2.3函数的重载

依稀的记得在学习的从C# 的时候,这些强类型语言对重载的定义:函数名相同,参数不同,或者是参数类型不同都可以叫做函数的重载。

但是在JavaScript这样的语言中因为 arguments的存在,JavaScript的函数根本就不存在所谓的签名,所以重载在JavaScript中实际是不存在的。

但是我们可以根据arguments传入函数体的参数个数来模拟函数重载:

function abc(){
if (arguments.length ===1){
//A
}
if(arguments.length ===2){
//B
}
} abc(11);
abc(11,22);

这里主要是满足某些特殊场合的需求吧。

2.4函数使用最重要的3个点

  1. this;
  2. apply()和call();
  3. bind();

关于thiscallapplybind这几个概念在之前博客文章已经介绍过很多遍了。在这里还是做一下简单的介绍。

2.4.1 this的使用

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象;

this指向的对象是在代码的运行期决定的。既上面说的,谁调用了它,就指向谁。一个很简单的总结就是,在函数中使用this,当前this指向的是当前window对象。在对象的方法中使用this,this指向的是当前对象(这个也是最容易出错的地方)。

2.4.2 call和apply的使用

关于这两个概念,之前的博客文章也介绍多很多次。这里也简单总结介绍一下。callapply主要是在执行某个对象的方法的时候来改变当前this的指向。主要用在对象继承的时候。

2.4.3 bind的使用

bind也是改变对象this指向的一个方法。这个方法是ECMAScript5中新添加的一个方法。但是bindcall,apply的主要区别就是bind的第一个参数是要传给新函数的this的值。其它所有参数代表需要被永久设置在新函数中的命名参数。可以在之后继续设置任何非永久参数。

来看一段示例代码:

function abc (lab){
console.log(lab + this.name);
} var person1 = {
name:'xiaogang'
} var person2={
name:'zhiqiang21'
} var sayNamePer1 =abc.bind(person1);
sayNamePer1('person1'); var sayNamePer2 =abc.bind(person2,'person2');
sayNamePer2(); person2.sayName = sayNamePer1;
person2.sayName('person2');

上面的代码中:

sayNamePer1在绑定的时候没有传入参数,所以仍需要后续执行sayNamePer1来传入lab参数;sayNamePer2不仅绑定了thisperson2,还绑定了输入的第一个参数是person2。意味着可以可以直接执行sayNamePer2()。最后一个是将sayNamePer1设置位person2sayName方法。由于其this的值已经绑定,所以虽然sayNamePer1person2的方法,但是输出的仍然是person1.name的值。

其实总结一句话call,apply和bind的主要区别就是:

callapply是绑定既执行。bind是有返回值的,先绑定后执行。

JavaScript 面向对象开发知识基础总结的更多相关文章

  1. javaScript 面向对象开发实例

    javaScript 面向对象开发实例 这个是结合require的模块化开发,首先创建构造函数: //test.js 1 function Test(lists) { var config={ nam ...

  2. JavaScript进阶【一】JavaScript模块化开发的基础知识

    //模块化的最初写法 //1.最初写法 //下面的m1和m2就组成了一个模块 //缺点:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系. f ...

  3. JavaScript—面向对象开发详解和垃圾回收

    面向对象的概述 ECMAScript 有两种开发模式:1.函数式(过程化),2.面向对象(OOP). 面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是, ...

  4. javascript面向对象编程笔记

    对象:一切事物皆是对象.对象是一个整体,对外提供一些操作.比如说一个收音机是一个对象,我们不需要知道它的内部结构是什么,只需要会使用外部的按钮就可以使用收音机. 面向对象:面向对象语言的标志是他们都有 ...

  5. ArcGIS API for JavaScript开发初探——基础知识

    1.前言 在ArcGIS Web API开发体系中一共有四大类,分别为: ArcGIS API for Flex ArcGIS API for JavaScript ArcGIS API for RE ...

  6. JavaScript进阶【三】JavaScript面向对象的基础知识复习

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. 前端笔记之JavaScript面向对象(三)初识ES6&underscore.js&EChart.js&设计模式&贪吃蛇开发

    一.ES6语法 ES6中对数组新增了几个函数:map().filter().reduce() ES5新增的forEach(). 都是一些语法糖. 1.1 forEach()遍历数组 forEach() ...

  8. day29—JavaScript中DOM的基础知识应用

    转行学开发,代码100天——2018-04-14 JavaScript中DOM操作基础知识即对DOM元素进行增删改操作.主要表现与HTML元素的操作,以及对CSS样式的操作.其主要应用知识如下图: 通 ...

  9. 浅谈:javascript的面向对象编程之基础知识的介绍

    在进入javascript的面对对象之前,我们先来介绍一下javascript的几个概念. 1.javascript的面向对象的基本概念 function aa(){ } /* * 这里的aa,在我们 ...

随机推荐

  1. R语言使用过程中出现的问题--attach()函数的使用

    使用attach(file)时,一定要配合使用detach(file),否则再此运行程序时极易出现问题,The following objects are masked ... 此外工作空间中不能有与 ...

  2. iOS 开发库相关(持续更新)

    01-给任意view添加毛玻璃效果 https://github.com/JagCesar/iOS-blur   02-浮动式的textfield输入框(可用于登录界面) https://github ...

  3. Allure--自动化测试报告生成

    之前尝试使用过testNG自带的测试报告.优化过reportNG的测试报告,对这两个报告都不能满意.后经查找资料,发现有个神器: Allure(已经有allure2了,笔者使用的就是allure2), ...

  4. 前端开发工程师 - 04.页面架构 - CSS Reset & 布局解决方案 & 响应式 & 页面优化 &规范与模块化

    04.页面架构 第1章--CSS Reset 第2章--布局解决方案 居中布局 课堂交流区 水平列表的底部对齐 如图所示,一个水平排列的列表,每项高度都未知,但要求底部对齐,有哪些方法可以解决呢? & ...

  5. LeetCode 102 ——二叉树的层次遍历

    1. 题目 2. 解答 定义一个存放树中数据的向量 data,一个存放树的每一层数据的向量 level_data 和一个存放每一层节点的队列 node_queue. 如果根节点非空,根节点进队,然后循 ...

  6. Leetcode - 557. Reverse Words in a String III (C++) stringstream

    1. 题目:https://leetcode.com/problems/reverse-words-in-a-string-iii/discuss/ 反转字符串中的所有单词. 2. 思路: 这题主要是 ...

  7. SpringCloud IDEA 教学 (二) Eureka Service

    写在开头 本篇继续介绍基于Eureka的SpringCloud微服务搭建,回顾一下搭建过程, 第一步:建立一个服务注册中心: 第二步:建立微服务并注入到注册中心: 第三步:建立client端来访问微服 ...

  8. 3.azkaban3.0测试

    测试目标 azkaban多executor下flow的分配方式 azkaban可以同时执行的flow\job个数 azkaban单个job最小使用的内存 相关配置 executor最大线程数: exe ...

  9. 默认初始化&拷贝初始化&直接初始化&值初始化&列表初始化

    一.各种初始化的形式 /* 定义变量形式一:不指定初始值 */ int a; // 默认初始化 /* 定义变量形式二:指定初始值 */ int b = 1; // 拷贝初始化 int b(1); // ...

  10. 第二次作业 编程题 PAT 1001A+B Format

    Github的object-oriented仓库:1001.A+BFormat(20) 1.解题的思路过程 在之前学习C语言时曾经碰到过类似的将数字转换成字符输出的情况,这道题目要求输出的数字每三个间 ...