资讯专栏INFORMATION COLUMN

JavaScript知识点总结(二)

wapeyang / 3366人阅读

摘要:变量作用域垃圾收集内存问题基本类型和引用类型中的变量包含基本类型值和引用类型值基本类型值指的是简单的数据段引用类型值值那些可能有多个值构成的对象五种基本数据类型的值即基本类型值是按值访问的因此操作的是保存在变量中实际的值引用类型值是保存在内

变量, 作用域, 垃圾收集(内存问题) 基本类型和引用类型

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

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

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

五种基本数据类型(Undefined, Null, Boolean, Number, String)的值即基本类型值是按访问的, 因此操作的是保存在变量中实际的值

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

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

动态的属性

定义基本类型的值和引用类型的值方式是基本一样的, 就是创建一个变量, 然后为该变量赋值.

创建变量以后,基本类型的值和引用类型的值执行的操作有很大不同.

//引用类型可以为其添加或删除属性和方法
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
复制变量值

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

复制基本类型值时, 是直接创建新值, 占据不同的内存(栈)空间, 复制之后两个变量可以参与任何操作而不互相影响

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 = /whello123/;
var pattern5c = new RegExp("whello123");
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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79290.html

相关文章

  • 前端资源系列(4)-前端学习资源分享&前端面试资源汇总

    摘要:特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 本以为自己收藏的站点多,可以很快搞定,没想到一入汇总深似海。还有很多不足&遗漏的地方,欢迎补充。有错误的地方,还请斧正... 托管: welcome to git,欢迎交流,感谢star 有好友反应和斧正,会及时更新,平时业务工作时也会不定期更...

    princekin 评论0 收藏0
  • Deep in JS - 收藏集 - 掘金

    摘要:今天同学去面试,做了两道面试题全部做错了,发过来给道典型的面试题前端掘金在界中,开发人员的需求量一直居高不下。 排序算法 -- JavaScript 标准参考教程(alpha) - 前端 - 掘金来自《JavaScript 标准参考教程(alpha)》,by 阮一峰 目录 冒泡排序 简介 算法实现 选择排序 简介 算法实现 ... 图例详解那道 setTimeout 与循环闭包的经典面...

    enali 评论0 收藏0
  • SegmentFault 技术周刊 Vol.40 - 2018,来学习一门新的编程语言吧!

    摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...

    caspar 评论0 收藏0
  • SegmentFault 技术周刊 Vol.40 - 2018,来学习一门新的编程语言吧!

    摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...

    nihao 评论0 收藏0
  • SegmentFault 技术周刊 Vol.40 - 2018,来学习一门新的编程语言吧!

    摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...

    Drummor 评论0 收藏0
  • 真正理解 JavaScript (中高级知识总结

    摘要:本文整理了我对的一些理解,试将零散的知识归总。此文非语法整理,内容偏中高级,如有纰漏或错误,请予以指正。原型对象原型对象通常由内置函数对象创建,它通常是一个普通对象,但也可能是函数对象。构造器的属性中只包含全局对象参考资料 分享一篇我在2015年底做的总结笔记。本文整理了我对 JavaScript 的一些理解,试将零散的知识归总。此文非语法整理,内容偏中高级,如有纰漏或错误,请予以指正...

    figofuture 评论0 收藏0

发表评论

0条评论

wapeyang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<