内容
---理解基本类型和引用类型的值
---理解执行环境
---理解垃圾收集

--JavaScript变量松散类型的本质
--决定了它只是在特定时间用于保存特定值的一个名字而已
--变量的值及其数据类型可以在脚本的生命周期内改变

基本类型和引用类型的值
--ECMAScript变量可能包含两种不同数据类型的值
---基本类型值和引用类型值
----基本类型值指简单的数据段(undefined、null、Boolean、Number、String)
----引用类型值值那些可能由多个值构成的对象(Object、Array)

--在将一个值赋给变量时,解析器必须确定这个值是那种类型
--基本类型按值访问,可以操作保存在变量中的实际的值
--引用类型的值是保存在内存中的对象
---JavaScript不允许直接访问内存中的位置,不能直接操作对象的内存空间
---在操作对象时,实际上是在操作对象的引用而不是实际的对象
---引用类型的值是按照引用访问的

--在很多语言中,字符串以对象的形式表示,也被认为是引用类型
--ECMAScript放弃了这一个传统

动态的属性
--定义基本类型值和引用类型值
---创建一个变量并为该变量赋值
--对于引用类型的值,可以添加,修改,删除其属性和方法

var person = new Object();
person.name = "luking";
console.log(person.name); //"luking"

--如果对象不被销毁或者这个属性不被删除,则这个属性一直存在

--不能给基本类型添加属性,尽管不会报错

复制变量值
--复制基本类型的值:把该值的副本复制到新的变量中去

var num1 = 5;
var num2 = num1;
num2; //5 与num1互相独立

--引用类型值:也是复制该值的备份,只不过这个值实际上是一个指针

---这个指针指向内存在堆中的一个对象
---因此这两个变量实际上引用的是同一个对象
---通过一个变量修改对象,另一个变量也会受影响

var obj1 = new Object();
var obj2 = obj1;
obj1.name = "luking";
console.log(obj2.name); //luking

--因为两个变量的值是一个指针,指向同一个对象

--所以操作的同一个对象,展示的也是同一个对象

传递参数
--ECMAScript中所有函数的参数都是按照值传递的
--即,把函数外部的值复制给函数内部的参数
--引用类型值也只是复制的其指针
--所以在函数内部也可以修改这个指针指向的对象
--但是当改变这个值时,不会对原来的引用类型变量产生影响

function f(o) {
o.name = "luking";
o = new Object();
o.name = "alen";
}
var obj = new Object();
f(obj);
obj.name; //"luking"

--可以把ECMAScript函数的参数想想成为局部变量

检查类型
--typeof 用于检查是那种基本类型
---typeof null; //object

--instanceof 用于判断是那种引用类型的对象

result = varible instanceof constructor;

--如果变量是给定引用类型(根据原型链识别)的实例返回true

person instanceof Object;     //变量person是不是Object类型
color instanceof Array; //变量color是不是Array类型
pattern instanceof RegExp; //变量pattern是不是RegExp类型

--所有引用类型的值都是Object的实例

--基本类型不是对象,返回false

执行环境及作用域
--执行环境定义了变量或者函数有权访问的其他数据,决定了它们个自的行为
--每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
--无法访问,但是解析器在处理数据时会在后台使用它

--全局执行环境是最外围的一个执行环境
--根据ECMAScript实现所在的宿主环境不同,表示的执行环境的对象也不一样
---在web浏览器中,全局执行环境被认为是window对象
---因此,所有全局变量和函数都是作为window对象的属性和方法创建的
--某执行环境中的所有代码执行完毕后,该环境被销毁,保存其中的所以变量和函数定义也随之销毁
---全局执行环境知道应用成勋退出--例如关闭网页或者浏览器时,才被销毁

--每个函数都有自己的执行环境
--当执行流进入函数时,函数的环境会被推入一个环境栈中
--当函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境
--ECMAScript程序中的执行流正是由这个方便的机制控制着

--当代码在一个环境中执行时,会创建变量对象的一个作用域链
---作用域链,保证对执行环节有权访问的所有变量和函数的有序访问
--作用域链的前端,始终都是当前执行的代码所在的环境的变量对象
--如果这个环境是函数,则将其活动对象作为变量对象
---活动对象最开始只包含一个变量,arguments对象(在全局环境中不存在)
--作用域链中的下一个变量对象来自包含(外部)环境
--再下一个变量对象则来自下一个包含环境
--一直延续到全局执行环境
--全局执行环境的变量对象始终都是作用域链的最后一个对象

--标识符解析是沿着作用域链一级一级地搜索标识符的过程
--搜索过程始终从作用域链的前端开始,逐渐地向后回溯
--直到找到标识符为止,如果找不到标识符,通常会导致错误发生

var color = "blue";
function changeColor() {
if(color === "blue") {
color = "red";
} else {
color = "blue";
}
}
changeColor();
color; //"red"

--函数changeColor()的作用域链包含两个对象

--是它自己的变量对象(其中定义着arguments对象)和 全局执行环境
--可以在函数内部访问变量color,就是因为可以在这个作用域链中找到它

--内部环境可以通过作用域链访问所有的外部环境
--反之则不行
--这些环境之间的联系是线性,有次序的
--每个环境都可以向上搜索作用域链,以查询变量和函数名
--但是不能向下搜索进入另一个执行环境
--函数参数也被当作变量来对待,因此其访问规则与执行环境中的其他变量相同

延长作用域链
--执行环境的类型只有两种--全局和局部(函数)
--可以通过其他方法来延长作用域链
--也就是在作用域链的前端临时增加一个变量对象
--该变量对象会在代码执行后移除

--try-catch语句的catch块
--with语句

--两个语句都会在作用域链的前端添加一个变量对象
--with 会指定的对象添加到作用域链中
--catch会创建一个新的变量对象,其中包含被抛出的错误对象的声明

function buildUrl() {
var qs = "?debug=true" with(location) {
var url = href + qs;
} return url;
}

--location对象被添加到了作用域链的前端

--不是新的执行环境,因为没有块级作用域(执行环境)

没有块级作用域(执行环境)
--在其他类C语言中,由花括号封闭的代码都有自己的作用域
---用ECMAScript的话来说,就是他们自己的执行环境
---所以支持根据条件来定义变量

if(rtue) {
var color = "red"
}
color; //"red"

--在for循环的初始化变量表达式中定义的变量

---也会存在于循环外部的执行环境中

声明变量
--使用var声明的变量会自动被添加到最接近的环境中
--在函数内部,就是函数的局部环境
--在with语句中,就是最接近的函数环境
--如果初始化变量时,没有使用var声明
---该变量会自动被添加到全局环境

--不建议,不声明直接初始化变量
--在严格模式下会导致错误

查询标识符
--当在某个环境中为了读取或者写入而引用了一个标识符时,
---必须通过搜索来确定标识符实际代表什么
--搜索过程从作用域链的前端开始,向上逐级查询于给定名字匹配的标识符
--如果在局部环境中找到了,就停止搜索,变量就绪

--如果在局部环境中没有找到,则沿着作用域链向上搜索
--搜索过程一直追溯到全局环境的变量对象
--如果还找不到则以为着该变量尚未声明(会自动声明一个全局变量)

垃圾收集
--JavaScript有自动的垃圾收集机制
--执行环境会负责管理代码执行过程中使用的内存
--原理:找出那些不再继续使用的变脸,释放其占用的内存
--垃圾收集器会按照固定的时间间隔(或执行中预定的收集时间)周期性地执行这一操作

--函数中的局部变量的正常生命周期
--局部变量直在函数执行的过程中存在
--在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,
--以便存储他们的值,然后在函数中使用它们,直到函数执行结束
--函数结束,局部变量没有存在的必要了,释放掉他们的内存以供将来使用
--并不是所以情况下都这么容易判断变量是否有存在的必要
--垃圾收集器必须跟踪,标记变量,
--用于标识无用变量的策略,因浏览器的不同而不同

--大致分两种
--标记清除 和 引用计数

标记清除
--最常用的垃圾收集方式
--垃圾收集器在运行时,
--会给存储在内存中的变量都加上标记
--然后,去掉环境中的变量以及被环境中的变量引用的变量 的标记
--这之后再被加上标记的变量视为准备删除的变量
--因为环境中的变量已经无法访问到这些变量(没有引用)
--垃圾收集器会销毁这些带标记的值,并回收内存空间,完成内存清除工作

引用计数
--不太常见
--跟踪记录每一个值的引用次数
--当声明了一个变量并将一个引用类型值赋给这个变量时,这个值的引用次数+1
--又被赋给另一个变量再+1
--如果这个变量的值又赋给新的值,则原来的值的引用-1
--当引用变成0时,就可以销毁,回收内存
--当下一次垃圾收集器运行时,被销毁,释放

--存在很严重的问题
--循环引用的变量无法被销毁

function f() {
var a = new Object(); //
var b = new Object(); //
a.x = b; //
b.y = a; //
}
f(); //a:1 b:1

--函数执行完,局部变量也不会被释放

性能问题
--垃圾收集器是周期性运行的,时间间隔很重要
--被触发运行的条件也很重要
--有的浏览器可手动触发,但是不建议

管理内存
--浏览器可用内存数量通常要比分配给桌面应用程序要少
--出于安全角度,防止运行JavaScript的页面耗尽系统内存而导致系统崩溃
--内存限制问题不仅影响给变量分配的内存,同时还会影响调用栈以及一个线程中能够同时执行的语句数量
--确保占用最少的内存可以让页面获得更好的性能

--最佳方式,只保存必要的数据
--一旦数据不再用,最好设置为null释放(解除引用)

var a = new Object();

//do something...

a = null;    //手动解除引用

--解除一个值的引用为了让值脱离执行环境,以便垃圾收集器下次执行时将其回收

小结

《JavaScript高级程序设计》读书笔记(四)变量、作用域和内存问题的更多相关文章

  1. 《JavaScript高级程序设计》笔记:变量、作用域和内存问题(四)

    基本类型和引用类型的值 ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值指的是简单的数据段,而引用类型的值指那些可能有多个值构成的对象. 动态的属性 var p ...

  2. 精读《javascript高级程序设计》笔记二——变量、作用域、内存以及引用类型

    变量.作用域和内存问题 执行环境共有两种类型——全局和局部 作用域链会加长,有两种情况:try-catch语句的catch块,with语句. javascript没有块级作用域,即在if,for循环中 ...

  3. JavaScript高级程序设计 读书笔记

    第一章 JavaScript 简介 第二章 Html中使用JavaScript 第三章 基本概念 第四章 变量,作用域,内存 第五章 引用类型 第六章 面向对象 第七章 函数表达式 第八章 BOM 第 ...

  4. javascript高级程序设计读书笔记

    第2章  在html中使用javascript 一般都会把js引用文件放在</body>前面,而不是放在<head>里, 目的是最后读取js文件以提高网页载入速度. 引用js文 ...

  5. javascript高级程序设计 读书笔记1

    第二章  在HTML中使用JS 加载JS有三种:行内,head头部和外部链接JS   最好使用外部链接<script src="example.js" ></sc ...

  6. JavaScript高级程序设计-读书笔记(1)

    第1章 JavaScript简介 JavaScript是一种专为与网页交互而设计的脚本语言,由下列三个不同的部分组成: l        ECMAScript:提供核心语言功能: l        文 ...

  7. javascript高级程序设计读书笔记-事件(一)

    读书笔记,写的很乱   事件处理程序   事件处理程序分为三种: 1.html事件2. DOM0级,3,DOM2级别  没有DOM1 同样的事件 DOM0会顶掉html事件   因为他们都是属性  而 ...

  8. 《JavaScript 高级程序设计》读书笔记四 变量 作用域 内存

    一   变量(基本类型和引用类型) a.基本类型保存值,保存在栈内存,引用类型保存指针,保存在堆内存: b.所有函数的参数都是按值进行传递的,不管参数是何种类型: c.检测类型 typeof     ...

  9. Javascript高级程序设计读书笔记(第六章)

    第6章  面向对象的程序设计 6.2 创建对象 创建某个类的实例,必须使用new操作符调用构造函数会经历以下四个步骤: 创建一个新对象: 将构造函数的作用域赋给新对象: 执行构造函数中的代码: 返回新 ...

  10. JavaScript高级程序设计-读书笔记(7)

    第22章 高级技巧 1.高级函数 (1)安全的类型检测 在任何值上调用Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串. ...

随机推荐

  1. Photoshop——APP设计规范

    随着Android和iOS语言的兴起,能够在手机上运行的APP软件已经成为了目前移动应用技术的焦点,APP的UI设计随之也越来越受到重视. 用户的需求不断增加,技术也在不断的更新,UI设计也越来越被重 ...

  2. Linux安装MATLAB2016a

    一.准备工具 matlab2016a的镜像文件和破解文件(链接: https://pan.baidu.com/s/1cxtlOM 密码: cj2u) Linux系统,我用的是deepin15.4,和一 ...

  3. Virtual Judge HDU 1241 Oil Deposits

    八方向   深搜 #include <iostream> #include<cstdio> #include<cstdlib> #include<algori ...

  4. [lua]紫猫lua教程-命令宝典-L1-01-10. 自定义函数

    L1[function]01. 定义与调用函数 函数的定义 和概念 没什么可说的 lua的函数声明和调用是有先后顺序的  先声明后调用 函数就是变量的一种 所以可以自由的把函数在变量间相互赋值 不过注 ...

  5. HDU多校第三场 Hdu6606 Distribution of books 线段树优化DP

    Hdu6606 Distribution of books 题意 把一段连续的数字分成k段,不能有空段且段和段之间不能有间隔,但是可以舍去一部分后缀数字,求\(min(max((\sum ai ))\ ...

  6. mybatis报错:A query was run and no Result Maps were found for the Mapped Statement、、Property [login_ip] not found on type [com.thinkgem.jeesite.common.permission.entity.PremissUser]问题解决

    今天在做ssm项目的时候出现了: 先是出现 了错误: mybatis报错:A query was run and no Result Maps were found for the Mapped St ...

  7. 条件锁condition与Queue()

    在学习之前你应该先了解锁和队列基础 import queue import time import random import threading import asyncio import logg ...

  8. css3之渐变背景色(linear-gradient)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. 第十六篇 nginx主配置文件参数解释

    # 指定拥有运行nginx权限的用户 #user nobody; # 指定开启的进程数,建议设置为CPU核心数 worker_processes ; # 指定全局错误日志级别,包括:debug/inf ...

  10. frm、myd、myi、opt、par文件

    .frm 表结构文件 .myd 表数据文件 .myi 表索引文件 .opr 储存数据库的默认字符集 .par 储存分区信息 mysql 5.6版本分区表有一个文件:表名.par, 该文件在5.7.6版 ...