1、可执行代码的分类
可执行代码(executable code):执行可执行代码的时候,会创建对应的执行上下文。
- 全局上下文
- 函数上下文
- eval(几乎不用)
2、执行上下文栈
执行上下文栈(FILO)
例1:
var scope = 'global scope';
function checkscope() {
var scope = 'local scope';
function f (){
return scope;
}
return f();
}
checkscope();
执行上下文栈变化
例2:
var scope = 'global scope';
function checkscope() {
var scope = 'local scope';
function f (){
return scope;
}
return f;
}
checkscope()();
执行上下文栈变化
3、执行上下文3个重要的特点
执行上下文(execution context)
① 变量对象
变量对象:variable object,简称VO。
变量对象是指 上下文中定义的变量和函数的声明。
全局上下文
this指向全局对象,浏览器环境下this指向window。
在JS中,全局VO指代的就是全局对象。
函数上下文
函数中的变量对象也叫活动对象。
执行代码
1、进入执行上下文,分析
2、代码执行
function foo(a){
var b = 2;
fucntion c(){};
var d = function(){};
b = 3;
// console.log(e) 报错 没有声明
e = 'e';
}
foo(1);
分析
② 作用域链(scope chain)
a、什么是作用域
定义变量的区域
b、作用域的分类
静态作用域:例如JS、C,、C++、 Python、Java
动态作用域:例如bash、Pascal、Emacs Lisp
scope.bash文件
value=1
function foo(){
echo $value;
}
function bar(){
local value=2;
foo;
}
bar
执行:bash scope.bash
结果:2
c、作用域的类型
- 全局作用域
- 函数作用域
- 模块作用域
c、作用域链
遇到一个变量,先在当前上下文中找VO、再找父级上下文的VO,直到找到全局变量this(window)。这个沿着上下文找到的VO链,叫作用域链。
function foo() {
function bar (){}
}
// foo的作用域链
foo.[[scope]] = [
globalContext.VO,
]
// bar的作用域链
bar.[[scope]] = [
fooContext.VO,
globalContext.VO,
]
③ this
全局中,this是全局对象,在浏览器中全局对象是widnow。
this指向什么
this指向调用它的内容。
全局代码中:this只想全局对象,浏览器环境中全局对象是window;
方法中:this指的是调用该方法的对象;
函数中:
- 函数作为对象的方法被调用:this指向调用该方法的对象
- 通过call、apply或bind方法被调用:this可以指向任意对象
- 没有明确指定:指向全局对象
闭包
自由变量:能在函数中使用的,但是不属于函数参数和局部变量的变量。
闭包:函数 + 函数里能访问非自身的变量。
function checkscope(){
var scope = 'local';
function f(){
return scope;
}
return f;
}
checkscope()();
// f的作用域链
[AO, checkscope.AO, globalContext.VO];
// 虽然checkscope不在执行上下文栈中了,但是f可以通过作用域链访问到scope,这种访问存在内存泄漏的风险
4、传递参数
基本类型按值传递,引用类型按拷贝的地址传递。
5、自定义call、apply、bind、new
Function.prototype.call2 = function (context, ...args) {
if ((typeof context === 'undefined') || (context === 'null')) {
context = window;
}
let fnSymbol = Symbol();
context[fnSymbol] = this;
let fn = context[fnSymbol](...args);
delete context[fnSymbol];
return fn;
}
Function.prototype.apply2 = function (context, arr) {
if ((typeof context === 'undefined') || (context === 'null')) {
context = window;
}
let fnSymbol = Symbol();
context[fnSymbol] = this;
let fn = arr ? context[fnSymbol](...arr) : context[fnSymbol]();
delete context[fnSymbol];
return fn;
}
Function.prototype.bind2 = function (context, ...arguments) {
// this就是当前方法
var _this = this;
var _arguments = Array.prototype.slice.call(arguments);
var fBound = function (...args) {
// this instanceof fBound 构造函数this指向自身
return _this.apply(this instanceof fBound ? this : context, args.concat(_arguments));
};
return fBound;
}
自定义new:objectFactory
const person1 = new Person(1, 2);
const person2 = objectFactory(Person, 1, 2);
function objectFactory(Constructor, ...args) {
// 新建对象
var obj = {};
// 新对象的__proto__ 指向构造函数的prototype
obj.__proto__ = Constructor.prototype;
// 构造函数执行结果
var ret = Constructor.apply(obj, args);
// 结果是对象就返回接口,否则返回创建的新对象
return typeof ret === 'object' ? ret : obj;
}