跟着红皮书学习js

139 阅读6分钟

4. 变量、作用域和内存问题

es中变量分为引用类型和基本类型

属性

我们可以给引用变量操作属性和方法,但是基本类型不可以

复制:

  • 基本类型: 直接复制值,操作不相互联系(保存在栈中)
  • 引用类型:复制了同一个值,但是这个值是个指针,指向了存储在堆中的一个对象。因此,在操作时,是对堆中的对象进行操作。操作会相互联系

传递参数:

所有的参数都是按值传递的。这是对的!!

  • 基本类型: 传递值
  • 引用类型: 也是传递值,但是值是指针。所以会同步发生变化

image.png 在这里有个问题: 因为是按照值传递的,在new之后,没有改变person对应的值,而是obj换了个值。如果是引用传递的话,那么person也会变,但是没有变。所以,是按值传递的.

检测类型

  • typeof
  • intanceof

执行环境和作用域

  • 全局作用域:最外层,window的作用域
  • 局部作用域:函数里面的作用域。 在局部作用域中可以使用全局的变量,反之不可以

延长作用域

  • try-catch中的catch
  • with语句

image.png

可以在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,

image.png

性能问题

垃圾收集器是周期运行的。如果内存数量较大,那么回收量也会很大。
例子: 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) 例子: image.png 改变原数组
  • 栈:先进后出(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方法的问题 image.png 解决方式:传一个参数。例子在下面

操作
位置

返回的是索引值,找不到返回的是-1

迭代