js

变量与作用域

js

Posted by Liyaozr on March 20, 2017

1、基本类型与引用类型

在JS中,变量类型包括基本类型和引用类型。

  • 基本类型:undefined、null、boolean、string、number
  • 引用类型:object

1)两者区别

(1)属性及方法的区别

我们可以给引用类型添加属性并访问它,或者改变它甚至删除它,却不能给基本类型的值添加属性,尽管这样做不会导致错误。

// 基本类型
var person="moon";
person.age=12;
alert(person.age); //undefined

//引用类型
var person=new Object();
person.name="moon";
alert(person.name); //moon
person.name="leo";
alert(person.name); //leo
delete(person.name);
alert(person.name); //undefined

方法:

// 基本类型
var person="moon";
person.say=alert("我是"+person); //我是moon

//引用类型
var person=new Object();
person.name='moon';
person.say=alert("我是"+person.name); //我是moon
(2)复制变量上的区别
//基本类型
var num1=5;
var num2=num1;
var num2=2;
alert(num1); //5
alert(num2); //2

//引用类型
var person1=new Object();
var person2=person1;
person1.name='moon';
alert(person1.name); //moon
person2.name='leo';
alert(person1.name); //leo
alert(person2.name); //leo

说明:

  • 原始类型的复制:当我们复制num1时,复制的是它的值,复制后num1和num2是两个独立的变量,因此num1的值得改变不会影响num2.
  • 引用类型的复制:复制person1并保存为person2,此时,person1和person2都指向堆内存中的Object,那么当person1的值发生改变时,person2也会发生改变。

思考: 如何彻底的复制一个对象(复制实际的值,而不是一个地址或者指针)?

(3)参数传递上的区别

在JS高级程序设计中写的是所有函数的参数都是按值传递的。 先理解一下概念:

①按值传递call by value—将外部变量传递给函数时,函数实参获取到的是变量的副本,而不是变量本身,因此在函数内部对实参进行修改,不会影响外部变量的值。

②按引用传递call by reference— 将外部变量传递给函数时,函数实参实际是外部变量的引用,因此在函数内部对实参的任何修改,都会反映到外部变量上。

③按共享传递call by share–本质上也是按引用传递,具体看例子

按值传递:当变量作为参数传递的时候,传递的是它的值,而不是变量本身,因为在修改参数的值以后,对变量本身并没有影响。 按引用传递:变量作为参数时,传递的是一个引用指针,变量和参数的指针同时指向堆内存中的值,因此当参数的值发生改变后,变量的值也会发生改变。

//基本类型
function add1(num) {
	num += 10;
	return num;
}
var count = 20;
var result = add1(count);
console.log(count); //20
console.log(result); //30
//引用类型
//情景1:
function add2(obj) {
	obj.a=2;
	obj.b=3;
	return obj;
}
var obj1={a: 1, b: 2};
var obj2=add2(obj1);
console.log(obj1); //{a: 2, b: 3}
console.log(obj2); //{a: 2, b: 3}
//情景2:
function add3(obj) {
	obj={a:2,b:3};
	return obj;
}
var obj1={a: 1, b: 2};
var obj2=add3(obj1);
console.log(obj1); //{a: 1, b: 2}
console.log(obj2); //{a: 2, b: 3}

我们可以把一个对象拆分为三个部分:

1.变量的地址

2.栈内存中存储的值或者对象的地址

3.堆内存中存储的对象的值

上面两个情景我们可以这样去理解:

obj1作为参数,传给了add2函数,(把obj1当成变量看,一部分是变量的地址,一部分是对象),此时obj对象的指针,也指向堆内存中obj1对象的值,因此直接改变obj对象的属性的值,obj1也会收到影响。 而在第二个例子中,我们同样也是把obj1变量地址和对象地址复制给了参数obj,obj{a :2, b: 3}这是一条赋值语句,相当于赋了一个新的地址给了形参obj,指向堆内存中的新对象。此时obj和obj1的指针不再指向堆内存中同一个对象值了,因此对实参obj1并没有影响。

2)如何检测

识别基本类型,使用typeof就可以了(null除外,会返回object)

typeof--返回的都是小写
var s="moon";
var n=1;
var u;
var nu=null;
var t=true;
var foo=function () {
	alert(1);
};
console.log(typeof s); //string
console.log(typeof n); //number
console.log(typeof u); //undefined
console.log(typeof nu); //object
console.log(typeof t); //boolean
console.log(typeof foo); //function

typeof无法识别引用类型(函数除外),此时我们需要使用instanceof,它返回的是一个布尔值。

instanceof
var person={name:"moon",age:12};
var colors=['red','yellow','blue'];
var foo=function () {
	alert(1);
};
console.log(person instanceof Object); //true
console.log(colors instanceof Array); //true
console.log(foo instanceof Function); //true

2、执行环境及作用域

(1)执行环境

执行环境,又成为执行上下文(execution context) 执行环境又全局执行环境和函数执行环境,在web浏览器中,全局执行环境就是window对象。当调用一个JS函数时,该函数就会进入与该函数相对应的执行环境,如果又调用了另外一个函数,则又会创建一个新的执行环境。

当前活动的多个执行环境在逻辑上形成一个栈结构。该逻辑栈的最顶层的执行环境成为当前运行的执行环境。

执行环境的状态组件 组件|作用目的 –|– 词法环境|指定一个词法环境对象,用于解析该执行环境内的代码创建的标识符引用 变量环境|指定一个词法环境对象,其环境数据用于保存由该执行环境内的代码通过变量表达式和函数表达式创建的绑定 This绑定|指定该执行环境内的ECMA脚本代码中this关键字所关联的值

词法环境

词法环境是一个用于定义特定变量和函数标识符在ECMAScript代码的词法嵌套结构上关联关系的规范类型

简单来说,词法环境是描述环境的一个对象。 词法环境包含两部分:

  • 环境记录
    • 形参
    • 函数声明
    • 变量
    • 其他
  • 对外部词法环境的引用(outer)
//下面函数中包含三个词法环境
var x=10;
function foo(y){
   var z=30;
   function bar(q){
      return x+y+z;
   }
   return bar;
}
var bar=foo(20);
bar(40);

(2)词法环境的具体分析

1)环境记录初始化–声明提前

指当全局代码或函数代码执行前,它要先扫描函数里面的内容,将函数里面的形参、函数声明、变量先定义到环境记录里面。

1.bar环境

  • 环境记录
    • 形参–q
  • 对外部词法环境的引用(outer)–foo环境

2.foo环境

  • 环境记录
    • 形参–y
    • 函数声明–bar函数
    • 变量–z
  • 对外部词法环境的引用(outer)–全局环境

3.全局环境

  • 环境记录
    • 函数声明–foo函数
    • 变量–x
  • 对外部词法环境的引用(outer)–无

2)代码执行

step1: 执行x=10

全局环境变化

  • 环境记录
    • 函数声明–foo函数
    • 变量–x
  • 对外部词法环境的引用(outer)–无

step2: 执行var bar=foo(20);

foo环境

  • 环境记录
    • 形参–20
    • 函数声明–bar函数
    • 变量–30
  • 对外部词法环境的引用(outer)–全局环境

step3: 执行bar函数

bar环境

  • 环境记录
    • 形参–40
  • 对外部词法环境的引用(outer)–foo环境

step4: 结果-x + y + z + q = 10 + 20 + 30 + 40 = 100