4. 变量、作用域和内存问题
es中变量分为引用类型和基本类型
属性
我们可以给引用变量操作属性和方法,但是基本类型不可以
复制:
- 基本类型: 直接复制值,操作不相互联系(保存在栈中)
- 引用类型:复制了同一个值,但是这个值是个指针,指向了存储在堆中的一个对象。因此,在操作时,是对堆中的对象进行操作。操作会相互联系
传递参数:
所有的参数都是按值传递的。这是对的!!
- 基本类型: 传递值
- 引用类型: 也是传递值,但是值是指针。所以会同步发生变化
在这里有个问题: 因为是按照值传递的,在new之后,没有改变person对应的值,而是obj换了个值。如果是引用传递的话,那么person也会变,但是没有变。所以,是按值传递的.
检测类型
- typeof
- intanceof
执行环境和作用域
- 全局作用域:最外层,window的作用域
- 局部作用域:函数里面的作用域。 在局部作用域中可以使用全局的变量,反之不可以
延长作用域
- try-catch中的catch
- with语句
可以在bulidUrl()作用域中使用with里年定义的变量
块级作用域
在js中没有块级作用域(通俗来讲就是{}包含起来的)(对于var而言)
引发的问题
在局部作用域中定义的变量可能会被外部引用 if、for语句中定义的变量会存在于当前的执行环境
for(var i=0; i<10 ;i++){}
alert(i) // 10
原因:for中的i执行10次之后,没有被销毁,而是到了外部
解决办法
- 声明变量 在函数中定义变量时使用var(个人觉得不针对if、for语句)
- 查询标识符 还是在局部作用域中再次定义一个变量 原因: 作用域在查询一个标识符时,是不断往上找的,局部没有,找上一个层级,直到全局作用域。当在局部找到时,就不用访问全局了
垃圾收集
找出不再使用的变量,释放其占用的内存
方法
- 标记清除(最常用) 给不使用的值加上标记
变量进入执行环境时,标记为“进入环境”。离开环境时,标记为“离开环境”。
原理:给所有存储在内存中变量,去掉在执行环境中的变量以及被他们引用的变量的标记。剩下的就是需要删除的了 - 引用计数(不常用,有个严重的问题)
逻辑:变量被赋值或者复制时给变量引用次数+1,若引用值被换掉则-1。当计数为0时,就可以删掉了。
问题:像下面这种,他的计数永远不会为0,
性能问题
垃圾收集器是周期运行的。如果内存数量较大,那么回收量也会很大。
例子:
IE6中是根据内存分配量运行的。当达到了任何一个临界值时,就会运行垃圾收集器。然而,这样就不得不频繁地运行垃圾收集器。
某些函数可以触发垃圾收集器
window.CollectGarbage() //IE
window.opera.collect() //Opera7
管理内存
处于安全方面的考虑,分配给web的内存数量通常比桌面少。防止运行js的网页耗尽全部系统内存而崩溃。
- 方法:(解除引用) 在不使用变量可以使设置为null(针对全局变量,局部变量会自动释放)
5. 引用类型
定义
描述的是一类对象所具有的属性和方法。通过new来创建
5.1 Object类型
创建方式
- new
let person = new Object()
perosn.name = 'Zoe'
person.age = 23
- 对象字面量(记不住名字)
let person = {
name:'Zoe',
age: 23
}
访问属性方法
- .方法
let person = new Object()
perosn.name // Zoe
- 中括号方法(可以使用变量等)
let person = new Object()
perosn['name'] // Zoe
5.2 Array类型
定义方法
let arr = new Array()
let arr = new Array(20) // 定义长度为20的数组
let arr = new Array('a', 'b', 'c') // [a,b,c]
//去掉new时同上
let arr = Array()
let arr = Array(20) // 定义长度为20的数组
let arr = Array('a', 'b', 'c') // [a,b,c]
let arr = [1, 2]
let arr = [1, 2, ] // 长度也为2,不推荐
读取值
let arr = [1, 2, 3]
arr[0] // 1
arr[4] // undefined 当长度超过数组长度时,则添加undefined
length属性(不是可读的)
let arr = [1, 2, 3]
arr.length // 3
arrr.length = 4
arr[4] // undefined 此时长度为4,为undefined
检测数组
- array instanceof Array
- Array.isArray(array)(书上说是因为instanceof只在单一的全局环境中,如果引入了别的框架,会与其他全局中的数组具有不同的构造函数。没有理解? 可以有个人帮我解答一下吗?)
相关方法
注:所有对象都包含三种方法(toString()、valueOf()、toLocaleString()) 区别:
- toString:返回以逗号分开的字符串
- valueof: 返回本身
- toLocalString:返回以逗号分开的字符串。(与toString区别:数组本身返回的是按照toString)
例子:
改变原数组:
- 栈:先进后出(pop、push)
- 队列:先进先出(push,shift)
- 排序: reverse、sort
- splice 不改变原数组
- slice、concat
- indexOf、lastIndexOf
- every、some、filter
- reducce
- toString()、valueOf()、toLocaleString()
var arr = [1, 2, 3];
console.log(arr.toString()); // 1,2,3
console.log(arr.valueOf()); // [1,2,3]
console.log(arr.toLocaleString()); // 1,2,3
// 还有join()方法
console.log(arr.join('[')); // 1[2[3
/**
push:给数组插值, 返回的是新数组的长度
pop:移除数组的最后一项,返回的是新数组的长度
shift:移除数组的第一项,返回的是第一项
unshift:在最前端添加任意项,返回的是新数组的长度
reverse:数组反向排序,返回的是新数组
sort:自动按照升序排列(但是,会有问题,因为利用了toString方法,比较的是字符串。会出现下面的问题)返回的是新数组
concat:(链接) 传入n个数组,原数组添加参数中每个项, 返回的是新数组,**不改变原数组**
slice:(剪切) 数组的第一项位置为0 **不改变原数组**
一个参数: arr.slice(a) 返回的是从a位置开始到最后的数组。
两个参数: arr.slice(a, b) 返回的是从a位置开始到b-1位置。
splice **改变原数组** 返回的是删除项,没有则返回[]
两个参数: arr.splice(a, b) 删除,从a位置删b项
三个参数: arr.splice(a, b, c) 返回的是从a位置开始到b-1位置。组
b为0时,表示插入,从a位置插入c
b不为0时,表示替换,从a位置,删除b项,插入c
indexof(a, ?b): 正序从b位置查找a,(第二个参数可以不写)
lastIndexOf():反序查找
some、every、filter、forEach、map:两个参数,一个是函数,一个是作用域对象(影响this的值,可不写)
函数有三个参数(数组项的值、索引、数组本身)
some、every:返货itrue/false
filter: 返回的是数组
map:返回的是数组
forEach:不返回,类似for
reduce、reduceRight:迭代数组中的所有项,返回一个值
两个参数: 函数,和返回值的初始值
reduce: 从第一项开始遍历
reduceRight: 从最后一项开始遍历
**/
sort方法的问题
解决方式:传一个参数。例子在下面
操作
位置
返回的是索引值,找不到返回的是-1