最近花了近半个月的时间, 初步读了一遍 underscore.js 源码, 通过读源码, 对 javascript 系统的学习了一遍.
之前也有通过看书或者是工作中用到 javascript 对其进行学习, 但是效果都不是很好.
一般看书, 都是这边看过去, 理解了, 过两天就忘了; 工作中的实战, 只是为了解决当时的问题而学习,
对 javascript 没有一个很好的认识,学习及 研究.
之前也有过读 之前也有过读 underscore 源码的计划, 但都是读了几行,
一时半会看不懂, 或是其他事给搁置了, 这次痛下决心, 即使第一遍读不懂,
也要把它看完, 就采用了, 把它敲下来的方法, 还别说一行行的往下敲, 还真让我坚持了下来.
读完之后, 也相当于又把 javascript 基础给了解了一遍, 收益颇多,
现在总结记录一下, 好记性不如烂笔头嘛, 说的太好了.
一. 数据类型
数据类型分为基本类型和引用类型
1. 基本类型: Undefined
, Null
, String
, Number
, Boolean
, Symbol
- 基本类型, 是简单的数据段, 值本身无法被改变;
- 可以通过
typeof
来确定是哪种基本类型, 返回值有undefined
,object
,string
,number
,boolean
,function
- 存储在变量获得内存中, 即有限存储空间的栈内存中;
- 数值的复制, 是按值传递, 复制者和被复制者的值相同, 复制结束后再没何联系;
- 传递参数, 是按值传递, 使用时也是按值使用;
2. 引用类型: Object
, Array
, Function
, Date
, Regexp
- 引用类型有自己的属性和方法; 引用类型是一种数据结构, 被称为类; 引用类型的对象是引用类型的实例
- 可以使用
Object.prototype.toString.call(context)
的方法来获得是哪种类型的引用类型
[object Object]
,[object Array]
,[object Function]
,[object String]
,
[object Error]
,[object RegExp]
,[object Date]
,[object Arguments]
,[object Boolean]
- 存储在堆内存中, 变量只是指向内存的指针
- 复制引用类型, 是按引用传递, 复制者和被复制者是指向同一内存的指针,
一个修改内容, 另一个相应的值也会跟着发生变化 - 传递参数, 是按值传递, 但是按引用来访问
二. 基本类型(原始类型)
1. Undefined
类型
Undefined
类型只有一个值undefined
;- 以下情况值为
undefined
: 一个变量为初始化; 一个函数未传入实参的形参;
一个函数未返回值, 默认返回值为undefined
2. Null
类型
Null
类型只有一个值null
;null
值表示指向一个空对象的指针, 使用typeof null
返回的值为object
;一般声明对象时, 若无具体初始值, 给该对象初始化为
null
;null == undefined => true; null === undefined => false;
3. String
类型
String
可以由 “ 或 ‘ 表示;- 其他类型可以通过
toString()
和String()
的方法转为字符串;
4. Number
类型
有 Number()
, parseInt()
, parseFloat()
函数, 可将其他类型转为数字类型;parseInt(param, n)
(n: 转换为多少进制), 转为整数类型, parseFloat(param)
转为浮点型;
Number(true) => 1;
Number(false) => 0;
Number(null) => 0;
Number('string') => NaN;
Number(undefined) => NaN;
5. Boolean
类型
- 有两个值
false
和true
; 有
Boolean()
函数, 可将其他类型转为布尔类型;Boolean(undefined) => false; Boolean(NaN) => false; Boolean(0) => false; Boolean('') => false; Boolean(null) => false;
6. Symbol
类型
ECMA 6 新增类型
三. 引用类型
引用类型是一种数据结构, 将数据和功能组织在一起, 被称为类; 引用类型的值即对象, 就是类的实例;
使用 new 操作符加上一个构造函数来创建对象
1. Object
类型
创建对象:
var obj = { name: 'Jeo', age: 20 } var obj = {} 或 obj = new Object(); obj.name = 'Jeo'; obj.age = 20;
访问对象:
obj.name; obj['name'];
2. Array
类型
创建数组:
var arr = [1, 2, 3]; var arr = new Array(length);//length 为数组长度, 创建一个包含 length 项数组 var arr = Array(length);//同上 var name = Array('Joe');//创建包含一个项的数组
访问数组:
arr[index]; //index 是数组索引值;
获得数组长度:
var arr = [1, 2, 3, 4]; arr.length; =>4; arr['length']: =>4;
方法:
toString
,toLocaleString
,valueOf
=>转换方法, 将数组转换为字符串, 返回字符串, 以逗号隔开
push
,pop
,unshift
,shift
=>在原数组的基础上最后/最前添加项或删除最后/第一项,
slice
,splice
, =>操作方法,slice(m, n)
:获得数组第 m 项到第 n - 1的项. 返回新的数组, 原数组值不变;splice(m, n[, string])
:删除, 插入, 替换. 返回删除项, 原数组发生相应变化
sort
,reverse
=>重新排序, 返回经过排序的数组
indexOf
,lastIndexOf
=>位置方法,indexOf(m[, n])
,lastIndexOf(m[, n])
获得传入项在数组中的索引值[或第 n 个之后项的索引值], 存在返回索引值, 不存在返回-1
;
every
,filter
,forEach
,map
,some
=>迭代方法, 传入参数一般为函数, 每一项经过函数, 返回新的值类数组:
拥有length属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解)
不具有数组所具有的方法.DOM方法
document.getElementsByClassName()
的返回结果(实际上许多DOM方法的返回值都是类数组) 和 函数中的arguments
对象就是类数组对象var obj = { 1: 'string', 2: 'string', 3: 'string', length: 3 }
3. Date
类型
时间是以 1970 年 1 月 1 日凌晨为基数, 来保存日期
创建日期:
var now = new Date();//创建日期对象, 并获得当前时间, 毫秒值 var someday = new Date(Date.parse('May 25, 2004'));//创建日期对象, 并赋值 var someday = new Date(2004, 5, 25); //尽量少用, 有些方法, 对这种方式创建的时间对象, 不适用 var now = Date();//创建日期对象, 无论传参数与否,都只获得当前时间, 毫秒值 var now = Date(anything);
方法:
Date.parse
,Date.UTC
,valueOf
,getTime
把时间对象, 转为毫秒值;toDateString
,toTimeString
,toUTCString
toLocaleDateString
,toLocaleTimeString
以特定的时间显示年月日时间等;getFullYear
,getMouth
,getDate
,getDay
,getHours
,getMinutes
,
getSeconds
,getMilliseconds
=>获得年月日星期时分秒毫秒setFullYear
,setMouth
,setDate
,setHours
,setMinutes
,
setSeconds
,setMilliseconds
=>设置年月日时分秒毫秒;
4. Function
类型
任何函数都是
Function
类型的实例, 函数名则是指向函数的指针;定义函数
函数可由函数声明和函数表达式来定义函数; 函数声明的函数, 在执行任何代码之前可用;
函数表达式必须等到解析器执行到它所在的代码行, 才会被解释执行. 原因如下:javascript引擎在执行 javascript 时分为 预编译阶段 和 执行阶段 .
预编译阶段 :- 声明变量 var a = 2; 首先编译器会在当前作用域的集合中声明该变量, 然后为引擎生成运行时所需的代码, 这些代码被用来处理 a = 2 这个赋值操作,
引擎在执行阶段时, 先询问作用域是否有变量 a, 如果有则对 a 进行赋值操作, 如果没有继续向上一层级查找, 直到没有抛出异常错误. - 变量的赋值会执行两个动作, 编译器在当前作用域声明变量; 执行时引擎在作用域中查找该变量, 找到就对其进行赋值操作.
- 预编译阶段, 确认变量是在哪里以及如何声明的, 方便在执行过程中如何对他们进行查找.
- 在内存中开辟一块空间 , 存放用
var
关键字声明的变量和function
关键字声明函数 . - 预编译不会对变量进行赋值 , 但是会对函数初始化 ;
在函数声明之前可调用函数, 而在变量初始化之前变量的值为undefined
.
函数声明提升时,函数名相同的函数, 后面声明的函数会覆盖前面声明的函数例子. - 将变量声明和函数声明提升 , 变量声明优先于函数声明 .
也就是说, 如果函数声明的函数名称和变量名称一样时, 函数名称会覆盖变量名称; - 函数内部也存在变量提升, 函数声明提升, 将变量名, 提升至作用域链顶部, 原理同上;
- 函数声明提升, 也就是将整个函数提升, 在执行阶段, 不再解析函数声明, 如果有变量和函数重名时, 再解析到变量时, 该函数名被重新定义. 如下
foo(); => ok var foo = 10; function foo() { console.log('ok'); } console.log(foo) => 10
- 声明变量 var a = 2; 首先编译器会在当前作用域的集合中声明该变量, 然后为引擎生成运行时所需的代码, 这些代码被用来处理 a = 2 这个赋值操作,
函数没有重载, 也就是一个函数名定义多次, 最后一个声明的函数, 会覆盖之前声明的函数;
函数内部有两个特殊的对象
arguments
和this
arguments
是类数组对象, 具有数组的length
属性, 但不具有数组的其他方法,
可通过Array.prototype.slice.call(arguments)
将其转为数组;
this
对象是函数据以执行的环境对象, 全局环境下是指windows
对象, 局部环境下, 是指引用该函数的对象;函数有两个属性
length
和prototype
length
是指函数接收的命名参数的个数
prototype
是包含函数类型所有实例共享的属性和方法, 如indexOf()
,toString()
等方法, 都保存在prototype
对象中
- 函数的方法
apply()
和call()
他们的作用是设置函数体内this
对象的值和传入函数的参数
使用方法:func.apply(thisArg[, argsArray)
和func.call(thisArg[, arg1[, arg2[, ...]]])
5. Regexp
类型
6. 基本包装类型
为了便于操作基本类型, ECMAScript 提供了 3 种特殊的引用类型: Number
, String
, Boolean
;
这三种类型依旧是引用类型, 使用 typeof
的结果都是 object
. 直接调用包装类型的函数, 相当于是调用转型函数, 将其他类型数据, 转为调用的包装类型
每当读取一个基本类型时, 后台就会创建一个对应的基本包装类型的对象, 让我们可以调用基本包装类型的方法和属性 .
有如下步骤:
- 创建基本包装类型的实例 ;
- 在实例上调用指定方法 ;
- 销毁这个实例
如下操作:
var str = 'some text';
var str2 = str.substring(2);
后台建立如下代码:
var str = new String('some text');
var str2 = str.substring(2);
str = null;