javascript
JavaScript知识点总结(二)
变量, 作用域, 垃圾收集(内存问题)
基本类型和引用类型
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); //objectinstanceof — 检测引用数据类型值时, 检测其引用数据类型值是什么类型的对象
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对象的属性和方法创建的.
每个函数都有自己的执行环境, 当执行流进入一个函数时, 函数的环境就会被推入一个环境栈中, 函数执行之后, 栈将其环境弹出, 把控制权返回给之前的执行环境.
代码在一个环境中执行时, 会创建变量对象的一个作用域链, 它保证了对执行环境有权访问的所有变量和函数的有序访问.
全局执行环境的变量对象始终都是作用域链中的最后一个对象
没有块级作用域
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); //2count = colors.push("black"); //推入另一项 console.log(count); //3var 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); //4var 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); //2count = colors.push("black"); //推入另一项 console.log(count); //3var 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); //3var 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, 删除后插入第三个参数的数据
位置方法
indexOf(),两个参数, 要查找的项, 以及(可选的)查找的起点位置的索引
lastIndexOf(),同上, 但该方法会在数组的末尾向前查找
两个方法都会返回要查找的想在数组中的位置, 没有找到的话返回-1
var numbers = [1,2,3,4,5,4,3,2,1];alert(numbers.indexOf(4));//3 alert(numbers.lastIndexOf(4)); //是从左边开始数起的索引值, 但寻找是从右边向左找, 所以是5alert(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
归并方法
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是4return prev + cur; }); console.log(sum); //15Date类型
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)模式, 即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项
正则表达式中的元字符包括 : ( [ { \ ^ $ | ) ? * + .]}
如果需要在正则表达式中使用这些元字符, 则必须进行转义 :
/**匹配第一个"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的实例, 但包含两个额外的属性,index和input, 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)); //并不影响, 并且依然能输出3ES中没有重载
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); //20function 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); //Jondata.sort(createComparisonFunction("age")); console.log(data[0].name); //Mark函数的内部属性
arguments的callee属性, 是一个指针, 指向拥有这个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)); //0this
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(); //blueES5新增的对象属性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); //0prototype, 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(); //redsayColor.call(this); //red sayColor.call(window); //red sayColor.call(o); //blueES5新增了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
总结
以上是生活随笔为你收集整理的JavaScript知识点总结(二)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: SQL Server 监控统计阻塞脚本信
- 下一篇: Spring 文件上传功能