ECMAScript 定义变量包含两种不同的数据类型的值
基本类型值 简单的数据段
-
Null, Undefined,String, Boolean, Number
-
按值访问
-
复制
-
基本类型的复制后,值是独立的
var n=5//独立 var m =n//独立 m=4//不影响n
-
引用类型的值,可能由多个值构成的对象
-
值是内存中的对象,按引用访问
-
Javascript不允许直接访问内存中的位置,也就是说不能直接操作内存空间,操作的是对象的引用,不是实际对象
-
可以添加,改变,删除属性,方法
var p = new Object(); p.name='haha'; console.log(p.name)//'haha'; -
复制
-
实际是一个指针,指向内存中堆的一个对象,两个变量实际上引用同一个对象
var oj1=new Object(); var oj2=oj1; oj1.name='d' console.log(oj2.name)//d
-
传递参数
ECMAScript中所有函数的参数都是按值传递的
就是函数的外部的值,复制给函数内部的参数
- 基本类型,如复制值,函数内外独立
- 引用类型,会把内存中的地址复制给函数内部的一个局部变量,局部变化可以反应到外部
function add(num){
num +=10;
return num;
}
var c = 20;
var r=add(c);
console.log(c)//20
function setName(obj){
obj.name='e';
}
var person = new Object();
setName(person);
console.log(person.name);//e
为何说参数是按照值传递的
function setName(){
obj = arguments[0]//复习函数章节,arguments对象
obj.name='N';//反应到a对象,添加了name属性,值为N
obj = new Object();
obj.name='G'
}
const a = new Object();
setName(a);
console.log(a.name)//N
实际函数内部修改了obj对象,但是原始的a对象的引用没有发生改变, obj变成了局部变量,函数执行完毕后obj被销毁。
执行环境及作用域
-
每个执行环境都有一个与之关联的变量对象
-
环境中所有的变量和函数都保存在这个对象中
-
Web浏览器,全局执行环境是window对象
-
环境中所有代码执行完毕后,环境会被销毁,其中的变量,函数也被销毁
-
环境栈
- 每个函数都有自己的执行环境, 全局或局部
- 执行一个函数会被推入一个环境栈中,执行后,环境弹出栈,控制权交给之前的执行环境(ECMAScript 程序中的执行流)
-
当代码在一个环境中执行,会创建变量对象的一个作用域链
-
作用域链的用途是保证所有变量和函数的有序访问
-
作用域链的前端始终是当前执行的代码所在环境的变量对象
-
如果环境是函数,将其活动对象作为变量对象,arguments作为第一个变量(全局环境不存在arguments)
-
全局执行环境的变量对象始终是作用域链最后的一个对象
const color = 'blue'; function changeColor() { const anotherColor = 'red'; function swapColors() { const tempColor = anotherColor;//red anotherColor = color;//blue color = tempColor;//red } swapColors(); } changeColor();
-
-
内部环境可以通过作用域访问所有外部环境
-
外部环境不能访问内部环境是变量和函数
-
环境可以向上搜索作用域链,查询变量,和函数, 不可以向下收索进入内部环境
-
延长作用域链,临时,添加一个变量对象到作用域链的前端
- try-catch 中的catch
- with语句
没有块级作用域
- var声明的变量,会添加到最接近的环境中,函数内部,最接近的环境就是函数的局部环境
if(true){
var c='blue'
}
console.log(c)//blue
for(var i=0;i<10;i++){
doSomething(i)
}
console.log(i);//10
闭包
闭包是指有权访问另一个函数作用域中的变量的函数
创建方式
function a() {
const a = 'ddd';
return function () {
console.log(this.a);//作用域链包含了外部作用域
}
}
var aF= a();
aF();//ddd
理解
- 当某个函数被调用时,会创建一个执行环境,及相应的作用域链
- 使用arguments和其它命名参数的值来初始化函数的活动对象
- 作用域链中,外部函数的活动对象处于第二位,外部函数的外部函数的活动对象处于第三位,直至作为作用域终点的全局执行环境
- 作用域链本质上是一个指向变量对象的指针列表
- 一般来讲,当函数执行玩后,局部活动对象会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)
1.创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链保存在内部的[[scope]]属性中
2.调用compare()函数时,会为函数创建一个执行环境,通过复制函数的[[scope]]属性中的对象构建执行环境的作用域链。
3.然后,有一个活动对象被创建推入执行环境作用域链的前端。
function compare(value1,value2){
if(value1<value2){
return -1;
}
}
var result = compare(5,10);
闭包有所不同
function a() {
const a = 'ddd';
return function () {
console.log(this.a);//作用域链包含了外部作用域
}
}
var aF= a();
aF();//ddd
- 当闭包的匿名函数的作用域链在外部函数a执行完后,a的活动对象没有被销毁,因为匿名函数还引用着这个活动对象
- a函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然留在内存中,被内部匿名函数引用着。
闭包与变量
- 闭包只能包含函数中任何变量的最后一个值(现在执行,不存在这个问题,有待考究)
function createFunctions() {
var result = new Array();
for (let i = 0; i < 10; i++) {
result[i] = function () {
return i;
};
}
return result;
}
const result = createFunctions();
console.log(result[7]());//7
function createFunctions1 () {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var funcArray = createFunctions1();
for (let func of funcArray) {
console.log(func())//这样输出都是10
}
function createFunctions1() {
var result = new Array();
for (let i = 0; i < 10; i++) {
result[i] = function (num) {
return function () {
return num;
}
}(i);
}
return result;
}
const result = createFunctions1();
console.log(result[7]());//7
关于this对象
- this对象是在运行时基于函数的执行环境绑定的
- 全局函数中,this等于window,当函数被作为某个对象的方法调用时,this等于那个对象
- 匿名函数的执行环境具有全局性,因此this对象通常指向window
var name = 'the window'
var object = {
name:"my object",
getName:function(){
return function(){
return this.name;
}
}
};
console.log(object.getName()())//"the window"
function TestA(){
console.log(this)
this.b='ddd';
function b(){
console.log(this)
}
b()
}
const t = new TestA();
VM225:2 TestA {}
VM225:5 Window {0: global, window: Window, self: Window, document: document, name: "", location: Location, …}
匿名函数的执行环境具有全局性,因此this对象通常指向window
理解
- 每个函数调用时都会自动取得两个特殊变量:this和arguments,内部函数在搜索这两个变量时,只会搜到其活动对象为止
- 不可能直接访问外部函数中的this和arguments
把外部作用域的中的this对象保存在一个闭包能够访问的变量里,就可以让闭包访问该对象
var name = 'a';
var object = {
name:'b',
getNameFunc:function(){
var that = this;
return function(){
return that.name;
}
}
}
不能维持的this的写法
var name = "The Window";
var object = {
name:'my',
getName:function(){
return this.name;
}
}
object.getName();//"my"
(object.getName)()//'my'
(object.getName=object.getName)()//"The Window"
JavaScript自动回收垃圾
垃圾收集方式
-
mark-and-sweep(标记清除)
- 进入环境进行标记
- 离开环境进行标记
- 存储在内存中的所有变量加上标记
- 先清除标记,之后再加标记就是准备删除,最后垃圾收集器完成内存清除工作
-
引用计数
- 引用一次,次数加1
- 当引用为0,进行内存回收