@Secretmm
2017-11-11T07:41:16.000000Z
字数 3569
阅读 793
js专题
js中的变量可以保存两种不同类型的值:基本类型值、引用类型值;
在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值;
String Number Boolean Null Undefined 是按值访问的,可以操作保存在变量中实际的值
对于基本类型值,在复制变量的时,会在新的变量上创建一个新值,这个新值是原值的一个副本,它们相互独立。
基本类型值之间的比较是单纯的值的比较
eg:
var a = 20;
var b = a;
b = 30;
console.log(a); //20
图解:
eg:
var m = {
a: 10, b: 20
}
var n = m;
n.a = 15;
console.log(n.a);//15
图解:
引用类型值的比较并非值的比较,即使两个对象包含相同的属性和值,它们也是不相等的;各个索引元素完全相等的两个数组也不相等。
引用类型值的比较是引用的比较,当且仅当它们引用同一个对象时,它们才相等。
eg:
var a = {
name: 'jack',
};
var b = {
name: 'jack',
}
var c = a;
console.log(a === b); //false
console.log(a === c); //true
当前代码的执行环境,它会形成一个作用域。
js中的运行环境大概包括三种情况:
- 全局环境: js代码运行起来会首先进入该环境
- 函数环境: 当函数被调用执行时,会进入当前函数中执行代码
- eval
在一个js程序中,必定会产生多个执行上下文,js引擎会以栈的方式来处理它们,这个栈被称为函数调用栈。【栈:先进后出】
栈底永远都是全局上下文,栈顶是当前正在执行的上下文
当代码在执行过程中,遇到以上三种情况,都会生成一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈
eg1:
var color = 'blue';
function changeColor() {
var anotherColor = 'red';
function swapColors() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColors();
}
changeColor();
图解:
执行上下文总结:
- 单线程
- 同步执行,只有栈顶的上下文处于执行中,其他的上下文需等待
- 全局的上下文只有唯一的一个,它在浏览器关闭时出栈
- 函数执行上下文的个数没有限制
- 每次某个函数被调用,就会有新的执行上下文被创建,即使是自身的函数也是如此
eg2:
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); // 999
过程:
1. 全局上下文入栈
2. var result = f1()
相当于f1()
被调用【一个执行过程】,所以f1()入栈;【这里result = f2】
3. return f2
后,f1()出栈,
4. 执行到result()
时,result入栈;【相当于f2()】
5. alert(n)
后,result出栈
图解
练习:
var name = "window";
var p = {
name: 'Perter',
getName: function() {
var self = this;
return function() {
return self.name;
}
}
}
var getName = p.getName();
var _name = getName();
console.log(_name);
恩。过程
1. 全局上下文入栈;
2. 我擦,老子感觉还不会
原文链接
当调用以一个函数时,一个新的执行上下文就会被创建。而一个执行上下文的生命周期可以分为两个阶段
创建阶段
在这个阶段中,执行上下文会分别创建变量对象、建立作用域链、确定this指向;
代码执行阶段
创建完成后进入代码执行阶段,此时会完成变量赋值、函数引用、其它代码执行;
图解:
function声明会比var声明优先级更高
通过变量对象的创建过程可以很好的理解变量提升;【先创建后执行】
eg1:
console.log(foo);
function foo() {
return 'girl';
}
结果:
ƒ foo() {
return 'girl';
}
解析:
- 首先是创建变量对象:
foo: <foo reference>
// 表示foo的地址引用- 其次开始执行代码
console.log(foo)
eg2:
console.log(foo);//undefined
var foo = 'mark'
解析:
- 首先是创建变量对象:
var foo = undefined
- 其次开始执行代码
console.log(foo);//undefined
foo = 'mark'
eg3
var foo = 20;
function foo() {
console.log('function foo')
}
console.log(foo); // 20
解析:
- 创建变量对象
1. foo: <foo reference>
//foo内存地址的引用【function声明优先级大于var】
2. 因为foo的属性已经存在,所以var foo 的变量声明被跳过;
- 执行代码
1. foo = 20
2. console.log(foo)
未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。
变量对象和活动对象有什么区别?
他们其实都是同一个对象,只是处于执行上下文的不同生命周期。不过只有处于函数调用栈栈顶的执行上下文中的变量对象,才会变成活动对象。
变量和函数可访问的范围
作用域分全局作用域、函数作用域、块级作用域(es6)
当前环境与上层环境的一系列变量对象组成
保证当前执行环境对符合访问权限的变量和函数的有序访问
eg:
var a = 20;
function test() {
var b = a + 10;
function innerTest() {
var c = 10;
return b + c;
}
return innerTest();
}
test();
在上面的例子中,全局,函数test,函数innerTest的执行上下文先后创建。我们设定他们的变量对象分别为VO(global),VO(test), VO(innerTest)。而innerTest的作用域链,则同时包含了这三个变量对象,所以innerTest的执行上下文可如下表示:
innerTestEC = {
VO: {...}, // 变量对象
scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链
}
作用域链图解:
作用域链是由一系列变量对象组成,我们可以在这个单向通道中,查询变量对象中的标识符,这样就可以访问到上一层作用域中的变量了。