JavaScript知识点总结(二)

变量, 做用域, 垃圾收集(内存问题)

基本类型和引用类型

ES中的变量包含基本类型值引用类型值javascript

基本类型值指的是简单的数据段前端

引用类型值值那些可能有多个值构成的对象java

五种基本数据类型(Undefined, Null, Boolean, Number, String)的值即基本类型值是按访问的, 所以操做的是保存在变量中实际的值正则表达式

引用类型值是保存在内存中的对象,ES不容许直接访问内存中的位置, 即不能直接操做对象的内存空间. 在操做对象时, 其实是在操做对象的引用而不是实际的对象. express

当复制保存在对象中的某个变量时, 操做的是对象的引用. 但在为对象添加属性时, 操做的是实际的对象.数组

动态的属性

定义基本类型的值和引用类型的值方式是基本同样的, 就是建立一个变量, 而后为该变量赋值.浏览器

建立变量之后,基本类型的值和引用类型的值执行的操做有很大不一样.数据结构

//引用类型能够为其添加或删除属性和方法
var p = new Object();
p.name = "Jon";
console.log(p.name); //Jon
delete p.name;
console.log(p.name); //undefined
//基本类型不能添加属性, 由于即便添加了也不能访问
var n = "Jon"; //一个string字符串类型
n.age = 25;
console.log(n.age); //undefined

复制变量值

复制基本类型值的变量值与复制引用类型值的变量值也存在不一样.app

复制基本类型值时, 是直接建立新值, 占据不一样的内存(栈)空间, 复制以后两个变量能够参与任何操做而不互相影响函数

var n1 = 5;
var n2 = n1; //复制n1

复制引用类型值时, 一样也会把储存在变量对象中的值复制一份到新变量分配的空间中, 但这个新变量的值实际上是一个指针, 指向储存在堆中的一个对象. 因此二者引用的是同一个对象. 因此, 改变其中一个变量, 另外一个变量也会受到影响.

var o1 = new Object();
var o2 = o1;
o1.name = 'Jon';
console.log(o2.name); //o1和o2指向的是堆内存中的同一个对象, 因此一样会输出Jon

参数传递

ES中全部函数的参数都是按传递的,不存在引用传递的参数

函数外部的值复制给函数内部的参数, 就等于把值从一个变量复制到另外一个变量同样.

基本类型值的传递就像基本类型变量的复制同样, 向参数传递基本类型值的时候,被传递的值会被复制给一个局部变量(即命名参数, 用ES的概念来讲, 就是arguments中的一个元素)

引用类型值的传递就像引用类型变量的复制同样, 向参数传递引用类型值的时候, 会把这个值在内存中的地址复制给一个变量, 所以这个局部变量的变化会反映在函数的外部

function addTen(n){ //参数(这里是n)实际上是函数的局部变量
  n += 10;
  return n;
}
var c = 20;
var r = addTen(c); //调用时,c做为一个局部变量传递给n,函数体内又会自増10而后返回
console.log(c); //外部的c变量不会被影响,仍是20
console.log(r); //30
function setName(obj){
  obj.name = "Jon";
}
var p = new Object(); //建立了一个对象并保存在变量p中
setName(p); //随即被传递到setName()中,p复制给了obj
console.log(p.name); //因此obj的属性name也能被p访问到,因此这里输出Jon
//证实对象是按值传递的例子
function setName(obj){
  obj.name = 'Jon';
  obj = new Object(); //为obj从新定义了一个对象
  obj.name = 'Percy'; //而后为obj定义了另外一个name属性
}//若是p是引用传递的话, 那么p就会自动被修改成指向其name属性值为Percy的对象,但下面的例子输出的仍然是Jon. 说明即便函数内部修改了参数的值, 但原始的引用仍然保持不变.
var p = new Object();
setName(p);
console.log(p.name); //Jon

能够吧ES函数的参数想象成局部变量.

检测类型

typeof — 检测变量是哪一种基本数据类型.string, number, boolean, undefined, object(若是变量的值是一个对象或null, 则返回object)

console.log(typeof "Jon"); //string
console.log(typeof true); //boolean
console.log(typeof 1); //number
var a;
console.log(typeof a); //undefined
console.log(typeof null); //object
var o = new Object();
console.log(typeof o); //object

instanceof — 检测引用数据类型值时, 检测其引用数据类型值是什么类型的对象

result = variable instanceof constructor

若是变量是给定引用类型的实例, 那么instanceof操做符就会返回true

console.log(person instanceof Object); //变量person是Object吗?
console.log(colors instanceof Array); //变量colors是Array吗?
console.log(pattern instanceof RegExp); //变量pattern是RegExp吗?

全部引用类型的值都是Object的实例, 因此检测一个引用类型的值和Object构造函数时会始终返回true

使用instanceof操做符检测基本类型的值会始终返回false, 由于基本类型不是对象

执行环境, 做用域

  • 执行环境定义了变量或函数是否有权访问的其余数据.

  • 全局执行环境是最外围的执行环境(Web浏览器中指的是window对象),所以因此全局变量函数都是做为window对象的属性和方法建立的.

  • 每一个函数都有本身的执行环境, 当执行流进入一个函数时, 函数的环境就会被推入一个环境栈中, 函数执行以后, 栈将其环境弹出, 把控制权返回给以前的执行环境.

  • 代码在一个环境中执行时, 会建立变量对象的一个做用域链, 它保证了对执行环境有权访问的全部变量和函数的有序访问.

  • 全局执行环境的变量对象始终都是做用域链中的最后一个对象

var color = "blue";
function changeColor(){
  if(color === "blue"){
      color = "red";
  }else{
      color = "blue";
  }
}
changeColor();
console.log("Now color is : " + color);
//Now color is : red
//函数changeColor()的做用域链包含两个对象,它本身的变量对象和全局环境的变量对象.
//内部环境能够经过做用域链访问全部的外部环境, 但外部环境相反不能访问内部环境的任何变量和函数
var color = "blue";
        
function changeColor(){
  var anotherColor = "red";

  function swapColors(){
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;
    //这里能够访问color, anotherColor, tempColor
  }
  swapColors();
  //这里只能够访问color, anotherColor
}
changeColor();
////这里只能够访问color
alert("Color is now " + color);

没有块级做用域

if(true){
  var color = "blue";
}
console.log(color); //ES中, 在外部依然能访问块级做用域内的变量和函数
for (var i=0; i < 10; i++){
        doSomething(i);
}
alert(i);      //能够访问块级做用域内的变量,输出10
//ES中查询标识符会从正在执行的局部环境查找, 若是当前局部环境查找不到, 就会沿着做用域链一级一级向上查找. 若是在全局环境都找不到须要查找的标识符, 说明该变量未声明
var color = "blue";
function getColor(){
  return color;
}
console.log(getColor()); //这里会先搜索getColor()内部有没有color变量, 若是没有就向上一级查找, 直到查找到位置, 这里在上一级已经找到, 因此会输出blue
var color = "blue";
function getColor(){
  var color = "red";
  return color;
}
console.log(getColor()); //red , 同级找到就不会再向上查找

垃圾收集

标记清除

ES中, 当变量进入环境(例如, 在函数中声明一个变量时), 就把这个变量标记为"进入环境", 这种进入环境的变量从逻辑上讲不能释放其内存, 由于有可能用到它们. 而当变量离开环境时, 就将其标记为"离开环境".

过程 :

  • 垃圾收集器运行的时候会给储存在内存中的全部变量都加上标记(可使用任意可以使用的标记方式)

  • 接着会去掉环境中的变量, 以及被环境中的变量引用的变量的标记(我的理解就是当前执行环境的变量以及被环境中变量引用的变量 的标记)

  • 在此以后再被加上标记的变量, 就是被视为准备删除的变量(由于它们以前用的时候已经被标记一次了, 再次(第二次)标记说明已经使用完毕), 环境中的变量已经没法访问这些变量了

  • 垃圾收集器完成内存清除工做, 销毁那些带标记的值并回收它们所占用的内存空间

引用计数

引用计数的含义是跟踪记录每一个值被引用的次数

  • 声明了一个变量并将一个引用类型值赋值给该变量时, 则这个值的引用次数就是1

  • 引用类型值又赋值给另外一个变量, 则引用次数加1

  • 相反, 若是包含对这个值的引用的变量(如a变量)又取得了另外一个引用类型值, 则前一个引用类型值的引用次数减1

  • 当这个引用类型值的引用次数变成0时, 则说明没办法再访问这个值了, 于是能够将其回收, 释放内存空间

该垃圾收集机制早期的循环引用问题
function problem(){
  var oA = new Object();
  var oB = new Object();
  
  //oA与oB经过各自的属性互相引用, 在标记清除的回收机制中, 它们的引用次数永远不多是0, 而且若是这个函数重复屡次调用, 会致使大量内存得不到回收
  //因此这种方式已经被摒弃, 而采用标记清除来实现其垃圾回收
  oA.someOtherObject = oB;
  oB.anotherObject = oA;
}
IE中的BOM与DOM使用引用计数来做为垃圾收集机制的问题
var element = document.getElementById("some_element");
var myObject = new Object();
//DOM元素(element)与一个原生JS对象(myObject)之间建立了循环引用
myObject.element = element; //myObject的element属性指向element对象
element.someObject = myObject; //变量element也有一个属性名叫someObject回指myObject
//基于上述问题, 即便将力争中的DOM从页面中移除, 它也永远不会被回收

//解决方案是在他们不使用时手动断开原生JS对象与DOM元素之间的连接
//把变量设置为null意味着断开变量与它以前引用的值之间的连接, 当下一次的垃圾回收执行时, 就会删除这些值并回收他它们占用的内存
myObject.element = null;
element.someObject = null;
//IE9以上已经把DOM和BOM转换成了真正的JavaScript对象, 因此避免了上述问题

性能问题及内存管理

性能问题

早期的浏览器按内存分配量运行的, 达到一个临界值就会触发垃圾回收机制, 这个问题在于, 若是一个脚本中包含大量的变量, 那么会在其生命周期也保持有那么多变量, 致使长时间处于垃圾回收机制的临界值, 从而使垃圾回收机制频繁运行, 形成严重的性能问题.

新版本的浏览器已经将其垃圾回收机制的工做方式改变, 会动态的调整触发的临界值.

内存管理

优化内存占用的方式, 就是为执行中的代码只保存必要的数据. 一旦数据再也不有用, 就经过将其值设置为null来释放引用 — 即解除引用

function createPerson(){
  var localPerson = new Object();
  localPerson.name = name;
  return localPerson;
}
var globalPerson = createPerson("Jon");

//手动解除globalPerson的引用
globalPerson = null;

引用类型

引用类型的值(对象)引用类型的一个实例.

ES中, 引用类型是一种数据结构, 用于将数据和功能组织在一块儿.就像传统的同样.

对象是某个特定引用类型的实例

新对象使用new操做符后跟一个构造函数来建立的.

构造函数自己就是一个函数, 只不过该函数是出于建立新对象的目的而定义的.

var person = new Object(); //建立Object引用类型的一个新实例, 并把实例保存在person变量中, 并为新对象定义了默认的属性和方法

ES中提供了不少原生引用类型,用于平常的开发任务.

Object类型

//建立Object实例
var person = new Object();
person.name = "Jon";
person.age = 25;
person.sayName = function(){
  console.log("My name is " + name);
}

//使用 对象字面量 建立
var person = {
  name : "Jon",
  age : 25,
  sayName : function(){
  console.log("My name is " + name);
  }
};

//使用 对象字面量 时, 属性名也能使用字符串
var person2 = {
  "name" : "Mark",
  "age" : 24,
  //...
}
var person = {
  name : "Jon",
  age : 25,
  sayName : function(){
  console.log("My name is " + name);
  }
};

//访问对象属性: 使用点表示法或者方括号表示法
console.log(person.name); //经常使用, Jon
console.log(person[name]); //不经常使用, 但若是属性名包含特殊字符或者空格等, 可使用方括号表示法来访问对象属性

Array类型

ES的Array每一项均可以保存任何类型的数据.

ES的Array大小是能够动态调整的, 便可以随着数据的添加自动增加以容纳新增数据.

数组的建立方式

var arr1 = new Array(); //建立数据的基本方式
var arr2 = new Array(10); //预先知道要保存的项目数量能够直接建立特定长度的数组, 这里建立了length为10的数组
var arr3 = new Array("Jon","Mark","Martin"); //建立包含特定值的数组
var arr4 = Array(5); //建立数组也能够省略new操做符
var arr5 = ["blue","yellow","green"]; //使用数组字面量 表示法来建立数组, 使用这种方法并不会调用Array构造函数

数组的读取和设置

var arr1 = ["blue","yellow","green"];

//读取
console.log(arr1[0]); //数组元素索引从0开始, 访问每一个元素就是 数组名[索引号], 好比第1个就是arr1[0] , 因此这里会输出blue

//设置
arr1[1] = "red"; //把数组arr1的第二个元素值设置为red;
console.log(arr[1]); //red
arr1[arr1.length] = "black"; //在数组的末尾添加一个元素
console.log(arr1); //["blue", "red", "green", "black"]

//访问数组长度
console.log(arr1.length); //3
//数组长度属性length属性不是只读的..能够经过设置其长度来改变数组的长度
arr1.length = 5;
console.log(arr1.length); //5
console.log(arr1[4]); //undefined

//丧心病狂地增长数组长度
arr1[99] = "purple"; //除了前面有效的值和这个新增有效的值, 其余的都是undefined
console.log(arr1.length); //100

检测对象是否为数组 — instanceof 或者 ES5里面新增的Array.isArray()

var arr = [];
if(Array.isArray(arr)){
  //do sth...
}

转换方法

var arr = [1,2,3];
console.log(arr.toString()); //1,2,3(返回的是字符串形式拼接而成的用逗号分隔的字符串)
console.log(arr.valueOf()); //1,2,3(返回的是原来的数组)
console.log(arr); //1,2,3(与toString()同样)

//**使用join()方法可使用不一样的分隔符构建指定的数组**
console.log(arr.join("-")); //1-2-3

//若是数组中的值是null或undefined, 那么该值在join(),toLocaleString(),toString(),valueOf()中返回的结果会以空字符串表示

数组的插入和删除

栈方法

LIFO(Last-In-First-Out), 后进先出

push(), 接收任意参数并把它们逐个添加到数组末尾, 返回修改后数组的长度

pop(), 从数组末尾移除最后一项, 减小数组的length值, 而后返回移除的项

var colors = Array();
var count = colors.push("red","green"); //推入两项
console.log(count); //2

count = colors.push("black"); //推入另外一项
console.log(count); //3

var item = colors.pop(); //取得最后一项
console.log(item); //black
console.log(colors.length); //2
//能够跟其余数组方法一块儿使用
 var colors = ["red","blue"];
 colors.push("brown"); //添加一项
 colors[3] = "purple"; //添加一项
 console.log(colors.length); //4
 
 var item = colors.pop(); //取得一项
 console.log(item); //purple

队列方法

FIFO(First-In-First-Out), 先进先出

数组最左侧的会被移除, 最右侧的会被添加

shift(), 移除数组中的第一个项(左边)并返回该项, 同时将数组长度减1

unshift(),在数组前端(左边)添加任意个项并返回数组长度

//结合使用shift()和push()方法, 能够像使用队列同样使用数组
var colors = [];
var count = colors.push("red","green"); //推入两项
console.log(count); //2

count = colors.push("black"); //推入另外一项
console.log(count); //3

var item = colors.shift(); //取得第一项(左边)
console.log(item); //red
console.log(colors.length); //2
//结合使用unshift()和pop()方法, 能够反向模拟队列, 即在数组的前端(左边)添加项, 在末尾(右边)移除项
var colors = [];
var count = colors.unshift("red","green"); //推入两项
count = colors.unshift("black"); //推入另外一项
console.log(count); //3

var item = colors.pop(); //取得最后一项
console.log(item); //black 
console.log(colors.length); //2

重排序方法

reverse(),反向排序

sort(),把数组的每一项转换成字符串再进行排序

//reverse()
var values = [1, 2, 3, 4, 5]; 
values.reverse(); 
console.log(values); //5, 4, 3, 2, 1
//sort()方法由于会转换为字符串, 因此排序时会出现问题, 不少时候不会按照正常的规则排列 -- 即最小的排最前面, 最大的排最后面
//因此使用sort()时应该接收一个比较函数, 比较函数定义两个参数, 若是第一个参数应该位于第二个以前就返回一个负数, 若是两个参数相等就返回0, 若是第一个参数应该位于第二个以后就返回一个整数.
function compare(value1, value2){
  if(value1 < value2){
      return -1;
  }else if(value1 > value2){
      return 1;
  }else{
    return 0;
  }
} 
//使用上面的比较函数
var values = [3, 2, 6, 8, 1];
values.sort(compare);
console.log(values); //1, 2, 3, 6, 8

操做方法

concat(), 拼接接收的参数, 返回一个拼接后的数组.

slice(), 数组截取方法, 接受一个或两个索引参数, 一个时, 会返回该索引到数组结尾的项(包括该索引的项), 两个时, 会返回第一个到第二个索引参数之间的项(不包括第二个索引的项).

splice(), 像数组的中部插入项, 有3种方式

  • 删除 : 两个参数, 要删除的起始索引位置, 以及要删除的项

  • 插入 : 三个参数, 起始索引位置, 要删除的项数, 要插入的项. 若是第二个参数设置为0, 则不删除直接插入

  • 替换 : 三个参数, 起始因此位置, 要删除的项数, 要插入的想, 跟上面插入同样, 只不过第二个参数不为0, 删除后插入第三个参数的数据

//concat
var colors1 = ["purple", "pink", "blue"]; 
var colors2 = ["green", "red", "yellow"];
var allcolors = colors1.concat(colors2);
console.log(allcolors); //["purple", "pink", "blue", "green", "red", "yellow"]

var anothercolors = colors1.concat("white",["orange","lightpink"]);
console.log(anothercolors); //["purple", "pink", "blue", "white", "orange", "lightpink"]
//slice
var colors = ["red", "blue", "green", "pink", "orange", "purple"];
var colors2 = colors.slice(1); //"blue", "green", "pink", "orange", "purple"
var colors3 = colors.slice(2,5); //"green", "pink", "orange"

/*若是参数是负数, 则使用数组长度加上该负数来决定相应的位置; 
若是这时第二个参数的位置小于第一个参数的位置, 则会返回一个空数组
若是第一个参数和数组长度相加以后等于负数, 则从索引0开始计算
*/
var colors3 = colors.slice(-3 , -1); //等同于slice(3,5), 结果是"pink", "orange"
var colors4 = colors.slice(-7 , -1); //等同于(0,5), 结果是"red", "blue", "green", "pink", "orange"
var colors5 = colors.slice(-7 , -8); //第二个索引参数大于第一个索引参数(起始位置), 因此这里返回空数组 "[]"
//splice
var colors = ["green","red","blue"]; 
var removed = colors.splice(0, 1); //删除索引为0开始的1项
console.log(colors); //red, blue
console.log(removed); //green

removed = colors.splice(1, 0, "purple", "pink"); //从索引为1的地方删除0项, 添加后面两项
console.log(colors); //red, purple, pink, blue
console.log(removed); //空数组 []

removed = colors.splice(1, 1, "green","black"); //从索引为1的地方删除1项(索引1的项), 添加后面两项
console.log(colors); //red, green, black, pink, blue
console.log(removed); //被删除的项 purple

位置方法

indexOf(),两个参数, 要查找的项, 以及(可选的)查找的起点位置的索引

lastIndexOf(),同上, 但该方法会在数组的末尾向前查找

两个方法都会返回要查找的想在数组中的位置, 没有找到的话返回-1

var numbers = [1,2,3,4,5,4,3,2,1];

alert(numbers.indexOf(4));//3
alert(numbers.lastIndexOf(4));    //是从左边开始数起的索引值, 但寻找是从右边向左找, 因此是5

alert(numbers.indexOf(4, 4));     //5
alert(numbers.lastIndexOf(4, 4)); //3       

var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [person];

alert(people.indexOf(person));     //-1
alert(morePeople.indexOf(person)); //0

迭代方法

ES5定义了5个迭代方法, 每一个方法都接收两个参数, 一是要在每一项上运行的函数, 二(可选)是运行在该函数的做用域对象 — 影响this的值.

传入这5个迭代方法做为参数的函数(即第一个参数)会接收三个参数, 数组项的值, 该项在数组中的位置 以及 数组对象自己.

  • every(), 对数组的每一项运行给定函数, 若是该函数对每一项都返回true, 则返回true

  • filter(), 对数组的每一项运行给定函数, 返回该函数会返回true的项组成的数组

  • forEach(), 对数组的每一项运行给定函数, 没有返回值

  • map(), 对数组的每一项运行给定函数, 返回每次函数调用的结果组成的数组

  • some(), 对数组的每一项运行给定函数, 若是该函数对任一项返回true, 则返回true

//every()和some()都是用于查询数组中的项是否知足某个条件
var numbers = [1,2,3,4,5,4,3,2,1];

var everyResult = numbers.every(function(item, index, array){
    return (item > 2);
});
alert(everyResult);       //every()在每一项都知足条件才会返回true,因此这里返回false
        
var someResult = numbers.some(function(item, index, array){
    return (item > 2);
});
alert(someResult);       //some()只有有其中一项或多项知足条件就会返回true, 因此这里返回true
//filter()返回条件中知足条件的项
var numbers = [1,2,3,4,5,4,3,2,1];

var filterResult = numbers.filter(function(item, index, array){
      return (item > 2);
});
alert(filterResult); // 3,4,5,4,3
//map()返回原始数组与给定函数运算而产生的结果
var numbers = [1,2,3,4,5,4,3,2,1];

var mapResult = numbers.map(function(item, index, array){
    return item * 2;
});
alert(mapResult);   //[2,4,6,8,10,8,6,4,2]
//forEach()没有返回值, 会使用原始数组和给定函数进行运算, 本质上和for循环迭代数组同样
var numbers = [1,2,3,4,5,4,3,2,1];

numbers.forEach(function(item, index, array){
    //do sth..
});

归并方法

ES5新增reduce()reduceRight()两个归并方法, 均会迭代数组的全部项, 而后构建一个最终返回的值. 前者会在数组的第一项开始迭代, 后者相反.

两个方法都接收两个参数, 一是在每一项上调用的函数, 二是(可选)做为归并基础的初始值

其中在每一项上调用的函数接收四个参数, 前一个值, 当前值, 项的索引, 数组对象

//reduce()
var values = [1, 2, 3, 4, 5];
var sum = values.reduce(function(prev, cur, index, array){
  //第一次执行回调函数时, prev是1, cur是2
  //第二次, prev是3(1加2的结果), cur是3(数组第三项)
  return prev + cur;
});
console.log(sum); //15
//reduceRight()
var values = [1, 2, 3, 4, 5];
var sum = values.reduce(function(prev, cur, index, array){
  //左右类似, 不过做用方向相反
  //第一次执行回调函数时, prev是5, cur是4
  return prev + cur;
});
console.log(sum); //15

Date类型

var now = new Date(); //建立日期对象, 得到当前时间

其余参见 :

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date#.E6.91.98.E8.A6.81

RegExp类型

//建立正则表达式
var expression = / pattern / flags ;

模式(pattern)部分能够是任何简单或复杂的表达式, 包括字符类, 限定符, 分组, 向前查找, 反向引用

标志(flags)能够有一或多个, 用以代表正则表达式的行为 :

  • g , 全局(global)模式, 即模式会被应用于全部字符串, 而非在发现第一个匹配项时当即中止

  • i , 不区分大小写(case-insensitive)模式, 即在肯定匹配项时忽略模式与字符串的大小写

  • m , 多行(multiline)模式, 即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项

/*
 *匹配字符串中因此"at"的实例
 */
var pattern1 = /at/g;

 /*
  *匹配第一个"bat"或"cat", 不区分大小写
  */
var pattern2 = /[bc]at/i;

 /*
  *匹配全部以"at"结尾的三个字符的组合, 不区分大小写
  */
var pattern3 = /.at/gi;

正则表达式中的元字符包括 : ( [ { \ ^ $ | ) ? * + .]}

若是须要在正则表达式中使用这些元字符, 则必须进行转义 :

/*
 *匹配第一个"bat"或"cat", 不区分大小写
 */
 
var pattern1 = /[bc]at/i;

/*
 *匹配第一个" [bc]at", 不区分大小写
 */
 
var pattern2 = /\[bc\]at/i;

/*
 *匹配全部以"at"结尾的三个字符的组合, 不区分大小写
 */
  
var pattern3 = /.at/gi;

/*
 *匹配全部以".at"结尾的三个字符的组合, 不区分大小写
 */
 
var pattern4 = /\.at/gi;
//不使用上面的字面量形式来定义正则表达式, 而使用RegExp构造函数,
//接收两个参数, 一是要匹配的字符串模式, 二(可选)是标志字符串
/*
 *匹配第一个"bat"或"cat", 不区分大小写
 */
 
var pattern1 = /[bc]at/i;
var pattern2 = new RegExp("[bc]at", "i");

使用构造函数模式时注意某些字符的双重转义问题 :

var pattern1 = /\[bc\]at/;
var pattern1c = new RegExp("\\[bc\\]at"); //上面的构造函数形式, 注意符号的双重转义

var pattern2 = /\.at/;
var pattern2c = new RegExp("\\.at");

var pattern3 = /name\/age/;
var pattern3c = new RegExp("name\\/age");

var pattern4 = /\d.\d{1,2}/;
var pattern4c = new RegExp("\\d.\\d{1,2}");

var pattern5 = /\w\\hello\\123/;
var pattern5c = new RegExp("\\w\\\\hello\\\\123");

RegExp实例方法

exec(), RegExp对象的主要方法, 专门为捕获组而设计的, 接收一个参数, 即要应用模式的字符串, 而后返回包含第一个匹配项信息的数组, 没有匹配项的状况下返回null.

返回的数组是Array的实例, 但包含两个额外的属性,indexinput, index表示匹配项在字符串中的位置, input表示应用正则表达式的字符串.

….TODO

Function类型

函数.每一个函数都是Function类型的实例.

函数是对象, 函数名是一个指向函数对象的指针,不会与某个函数绑定

//函数定义
function sum(n1,n2){
  return n1 + n2;
}

//另外一种方式
var sum = function(n1, n2){
  return n1 + n2;
}

//函数名只是指针, 因此一个函数能有多个名字
var anotherSum = sum;
console.log(auntherSum(1+2)); //3
sum = null; //把sum设置为空
console.log(auntherSum(1+2)); //并不影响, 而且依然能输出3
ES中没有重载
function addSomeNumber(n){
  return n + 100;
}

//后声明的才有效
function addSomeNumber(n){
  return n + 200;
}

var r = addSomeNumber(100); //300

//其实就至关于下面的代码
var addSomeNumber = function(n){
  return n + 100;
}

//覆盖了前面的同名函数
addSomeNumber = function(n){
  return n + 200;
}

var r = addSomeNumber(100); //300
//函数声明与函数表达式的区别, 函数声明支持函数声明提高, 即解析器会率先解读函数声明, 而后才执行代码
alert(sum(10, 10)); //有效, 输出20
function sum(n1, n2){
  return n1 + n2;
}

//但函数表达式不支持函数声明提高
alert(sum(10, 10)); //错误
var sum = function(n1, n2){
  return n1 + n2;
}
函数名自己就是变量, 因此也能做为值来使用
function callSomeFunction(someFunction, someArgument){
  //第一个参数是函数, 第二个参数是传递给该函数的一个值
  return someFunction(someArgument);
}

function add10(n){
  return n + 10;
}

//注意add10没有加括号, 是由于只访问函数的指针而不执行函数, 就要去掉括号
var r1 = callSomeFunction(add10, 10);
console.log(r1); //20

function getGreeting(name){
  return "Hi, " + name;
}

var r2 = callSomeFunction(getGreeting, "Jon");
console.log(r2); //Hi, Jon
//从一个函数中返回另外一个函数
function createComparisonFunction(propertyName){
  return function(o1, o2){
    var v1 = o1[propertyName];
    var v2 = o2[propertyName];
    
    if(v1 < v2){
        return -1;
    }else if(v1 > v2){
        return 1;
    }else{
       return 0;
    }
  }
}

var data = [
  {name : "Jon", age : 25},
  {name : "Mark", age : 24}
];

data.sort(createComparisonFunction("name"));
console.log(data[0].name); //Jon

data.sort(createComparisonFunction("age"));
console.log(data[0].name); //Mark
函数的内部属性

argumentscallee属性, 是一个指针, 指向拥有这个arguments对象的函数

//阶乘函数
function factorial(n){
  if(n <= 1){
    return 1;
  }else{
    //函数执行与函数名factorial牢牢耦合
      return n * factorial(n - 1);
  }
}

//使用callee消除耦合
function factorial(n){
  if(n <= 1){
    return 1;
  }else{
      return n * arguments.callee(n - 1);
  }
}

//trueFactorial得到了factorial的值, 其实是在另外一个位置保存了一个函数的指针
var trueFactorial = factorial;

//把factorial变成返回0的简单函数
factorial = function(){
  return 0;
};

//假如不使用arguments.callee, 那么下面的trueFactorial也会返回0, 使用了arguments.callee, 便可以解除耦合, 返回正常
console.log(trueFactorial(5)); //120
console.log(factorial(5)); //0

this

window.color = "red";
var o = {
  color : "blue"
};

function sayColor(){
  console.log(this.color);
}

//在全局做用域内调用, 此时this引用的对象是window, 因此输出red
sayColor(); //red

//把函数赋给对象o并调用sayColor(), 此时this引用的对象是对象o, 因此输出blue
o.sayColor = sayColor;
o.sayColor(); //blue

ES5新增的对象属性caller, 这个属性保存着调用当前函数的函数的引用.

全局做用域调用时它的值为null

function outer(){
  inner();
}

function inner(){
  console.log(inner.caller);
}

outer();
/*function outer(){
  inner();
  }*/
//更松散的耦合, 使用arguments.callee.caller
function outer(){
  inner();
}

function inner(){
  console.log(arguments.callee.caller);
}

outer();
/*function outer(){
  inner();
  }*/
//严格模式下, 访问arguments.callee会致使错误; 访问arguments.caller也会致使错误; 严格模式下还不能为函数的caller属性赋值, 不然会致使错误
函数属性和方法

每一个函数开始都包含两个属性, length,prototype

length, 表示函数但愿接收的命名参数的个数

//length
function sayName(name){
  console.log(name);
}

function sum(sum1, sum2){
  return num1 + num2;
}

function sayHi(){
  console.log("Hi");
}

console.log(sayName.length); //1
console.log(sum.length); //2
console.log(sayHi.length); //0

prototype, ES引用类型中保存全部实例方法的属性, 该属性不可枚举

apply(), call(), ES函数中两个原生的方法, 用途都是在特定的做用域中调用函数, 实际上等于设置函数体内this对象的值. 接收两个参数, 一是在其中运行函数的做用域, 二是参数数组(第二个参数能够是Array的实例, 也能是arguments对象)

//apply()
function sum(num1, num2){
  return num1 + num2;
}

function callSum1(num1, num2){
  return sum.apply(this, arguments); //传入arguments对象
}

function callSum2(num1, num2){
  return sum.apply(this, [num1, num2]); //传入数组
}

console.log(callSum1(10, 10)); //20
console.log(callSum2(10, 10)); //20
//call()与apply()做用相同, 只是接收参数的方式不一样, 使用call()时, 传递给函数的参数必须逐个列举出来
function sum(num1, num2){
  return num1 + num2;
}

function callSum(num1, num2){
  return sum.call(this, num1, num2);
}

console.log(callSum(10, 10)); //20
//apply()和call()重要的做用是扩充函数的做用域, 好处是对象不须要和方法有任何的耦合关系
window.color = "red";
var o = {
  color : "blue"
}

function sayColor(){
  cosnole.log(this.color);
}

sayColor(); //red

sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue

ES5新增了bind()方法, 会建立一个函数的实例, 其this值会被绑定到传给bind()函数的值

window.color = "red";
var o = {
  color : "blue"
}

function sayColor(){
  cosnole.log(this.color);
}

//用sayColor()方法绑定o对象, this的值指向o, 因此输出blue
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

基本包装类型

3种特殊的引用类型, Boolean, Number, String

这三种特殊的类型可使用new操做符建立实例, 但如非必要不推荐

//Boolean
var booleanObject = new Boolean(true); //不推荐使用
//Number
var numberObject = new Number(10);

//toFixed()按照指定的小数位返回数值的字符串表示
var num = 12;
console.log(num.toFixed(2)); //12.00

//toExponential()返回指数表示法(e表示法)表示的数值的字符串形式
var num2 = 11;
console.log(num2.toExponential(1)); //1.1e1

//toPrecision(), 返回合适的格式, 有多是固定大小(fixed)格式, 也多是e表示法, 接受一个参数, 即表示数值的全部数字的位数(不包括指数部分)

var num3 = 88;
console.log(num3.toPrecision(1)); //9e+1
console.log(num3.toPrecision(2)); //99
console.log(num3.toPrecision(3)); //99.0
//String
var stringObject = new String("Hi Jon");

//length属性, 表示字符串中包含的字符数量
console.log(stringObjet.length); //6

//charAt与charCodeAt(), 一个参数, 即须要查找的字符的索引, 返回查找到的字符, 后者获得的是 字符编码
var s1 = "Happy FrontEnd!";
console.log(s1.charAt("F")); //6
console.log(s1.charCodeAt("F")); //72
//ES5可使用方括号表示法, 接收一个索引值以返回获得的字符串
console.log(s1[3]); //p

//字符串操做方法
//concat(), 用于拼接字符串
var s2 = "Hi ";
var s3 = "Jon";
var r = s2.concat(s3);
console.log(r); //Hi Jon
//能够直接接受字符串使用
console.log("Hi", "Mark", "!"); //Hi Mark !

//slice(), substr(), substring(), 用于截取字符串并返回一个副本
//slice()和substring()接收两个参数, 开始索引值和结束索引值, 返回两个索引值之间的值, 在参数不是负数的时候做用相同
//substr(), 第一个参数接收开始的索引值, 第二个参数接收**返回的字符个数**
//若是这三个方法不接收第二个参数, 则返回开始字符串到结尾的字符串

var s4 = "Have a Good Day!";
console.log(s4.slice(3)); //e a Good Day!
console.log(s4.substring(3)); //e a Good Day!
console.log(s4.substr(3)); //e a Good Day!
console.log(s4.slice(3,8)); //e a G
console.log(s4.substring(3,8)); //e a G
console.log(s4.substr(3,8)); //e a Good

//传入负值的状况下, slice()会把 传入的负值 和 字符串的长度相加; substr()会把第一个负值的参数加上字符串的长度, 而把第二个负的参数转为0; substring()会把全部负值参数都转为0
var s5 = "My WOW player is Warlock";
console.log(s5.slice(-3)); //-3 + 24(字符串长度) = 21(从索引值为21的字符开始); 输出ock
console.log(s5.substr(-3)); //-3 + 24(字符串长度) = 21(从索引值为21的字符开始); //输出ock
console.log(s5.substring(-3)); //全部负数参数转换为0, 输出My WOW player is Warlock
console.log(s5.slice(3, -3)); //WOW player is Warl
console.log(s5.substr(3, -3)); //输出"", 由于第二个参数转为0(返回字符的个数)
console.log(s5.substring(3, -3)); //My


//字符串位置方法, indexOf(), lastIndexOf(), 从一个字符串中搜索给定的子字符串, 而后返回字符串的位置(没有则返回-1), 其中indexOf()会从头开始搜索, 而lastIndexOf()会在后面开始搜索

var s6 = "Happy WOW game!";
console.log(s6.indexOf("a")); //1 从前面开始搜索, 最早出现的索引位置是1
console.log(s6.lastIndexOf("a")); //11 从后面开始搜索, 最早出现的索引位置是11
//能够接收第二个可选参数, 表示从字符串中的哪个位置开始搜索
console.log(s6.lastIndexOf("a",4)); //11, 从索引值4的位置开始搜索, 索引值1的a已经被忽略, 因此找到第二个a在索引值11的位置
console.log(s6.lastIndexOf("a",6)); //1, 从索引值6开始向前搜索, 因此位置11的a已经被忽略, 因此找到第二个a, 在索引值1的位置
//indexOf()循环调用获得字符串中全部匹配的子字符串
var s7 = "My name is JonHo, World Of Warcraft is my favourite game";
var positions = []; //用于存取找到的所有子字符串的索引值数组
var pos = s7.indexOf('a'); //找到第一个a, 进入循环

while(pos > -1){ //当找到a子字符串时(找不到会返回-1,大于-1即表示找到)进入循环
  positions.push(pos); //把找到的索引值(pos内的内容)push到positions数组内
  pos = s7.indexOf('a', pos + 1); //每次循环都给上一次找到的a的索引值位置加1, 那样能确保每次新的搜索都在上一次找到的子字符串(a)的索引值后一个值开始
}

console.log(positions); //[4, 28, 32, 43, 53]
//ES5的trim()方法, 会删除字符串的前置及后置空格
var s8 = "    My name is Jon.       ";
var trimmedString = s8.trim();
console.log(trimmedString); //My name is Jon.
//字符串大小写转换, 经常使用的两个, toLowerCase()和toUpperCase(), 还有两个不经常使用的toLocaleLowerCase()和toLocaleUpperCase()
//不经常使用的两个方法一般会返回与前面两个方法相同的结果, 但在少数语言中会为Unicode大小写转换应用特殊的规则.
var s9 = "My WOW player is Warlock.";
var upperResult = s9.toUpperCase(); 
var lowerResult = s9.toLowerCase(); 
console.log(upperResult); //MY WOW PLAYER IS WARLOCK.
console.log(lowerResult); //my wow player is warlock.
//String的RegExp匹配方法, 略
单体内置对象(Global和Math)

Global是实际上是终极对象, 在平常使用中不存在这个对象, 注意其属性和某些特殊方法

Math对象的属性和方法都是数学上经常使用的运算方法, 不详述, 参见

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math

相关文章
相关标签/搜索