本文将this、prototype、Scope Chain、Execution Context放在一起梳理一哈
先看这样一段代码
var b = 'window_b'
var c = 'window_c'
Function.prototype.f = 'f'
Object.prototype.o = 'o'
function foo(a) {
var b = 'foo_b'
//var c = 'foo_scope'
function bar() {
}
console.log(b) //foo_b
console.log(this.b) //window_b
console.log(c) //window_c
console.log(foo.f) //f
console.log(foo.o) //o
}
foo('a')
foo.constructor === Function //true
Function.prototype.constructor === Function //true
foo instanceof Function //true
foo instanceof Object //true
思考:
- foo函数为什么可以用
this
关键字 - foo函数为什么可以访问函数外的变量
- 函数的
constructor
属性
首先this
只存在于Execution Context
(执行上下文)中,而每当调用一个函数时,一个新的执行上下文就会被创建并压栈到call stack
(函数调用栈)栈顶(栈底为全局执行上下文)。函数执行完从栈顶出栈
执行上下文有两个阶段:
- 函数刚被调用时:创建变量对象VO(
Variable Object
)包括:variables:undefined
、functions:<函数名:函数引用>
、arguments:<函数参数:undefined>
;
确定this
指向:取决于函数被调用的方式(普通自调用、对象方法调用、new...);
建立Scope chain
作用域链从:栈顶指向栈底,指向每个执行上下文中的AO(Activation Object
)。这也可以解释闭包
,因为函数体内调用函数的话,栈顶为“被调用函数”压住了“原函数”(栈是后进先出的)于是可以访问“原函数”的AO - 函数代码执行时:变量对象VO(属性无法访问)激活为=>活动对象AO(属性可以访问)
根据知识点解释代码:
首先函数被普通自调用foo('a'):
foo_Context = {
VO: {
arguments:{
a:undefined,
},
b:undefined,
bar: bar reference
},
this:undefined,
[[scope]]:Global_Context.AO
}
接下来函数代码要执行了
foo_Context = {
AO: {
arguments:{
a:'a',
},
b:'foo_b',
bar: bar reference
},
this:window,//因为没有"use strict"
[[scope]]:Global_Context.AO
}
console.log(b) 其实就是在foo_Context.AO.b中取值
console.log(this.b) 很明显 window.b === 'window_b' (Global_Context.AO.b)
console.log(c) 因为foo_Context.AO里没有就要顺着作用域链找 foo_Context.[[scope]].c === 'window_c'
原型链与作用域链很像 对象的隐藏属性[[prototype]]
(浏览器提供了__proto__
属性)就是原型链每一段链子
访问一个对象自身没有的属性时,会顺着内部属性[[prototype]]
这条链子找。直到Object.prototype.__proto__===null
而[[prototype]]指向构造器的prototype属性的值[].__proto__ === Array.prototype;{}.__proto__ === Object.prototype;...
函数的构造器都是FunctionArray.__proto__===Function.prototype;Object.__proto__ === Function.prototype
console.log(foo.f) //f
foo.__proto__ = Function.prototype
foo.__proto__.f = Function.prototype.f === 'f'
console.log(foo.o) //o
同理:
foo.__proto__.__proto__ = Function.prototype.__proto_ = Object.prototype
//原型对象也是对象所以构造器为Object
//而Function.prototype是个例外它既是对象又是函数
typeof Function.prototype //function
Function.__proto__ === Function.prototype //Function由自身创建
foo.constructor === Function //true
foo.constructor === Function.prototype.constructor === Function
当我们修改了默认的原型对象如:
foo.prototype = {some:some...}
foo.prototype.constructor === Object //true
因为{}是function Object创建的
{}.__proto__.constructor ===Object.prototype.constructor === Object
所以要重新绑定foo.prototype.constructor = foo
关于instanceof:
A instanceof B 其实就是找A的原型链中是否有B.prototype
A.__proto__ instanceof B
A.__proto__.__proto__ instanceof B
foo instanceof Function //true
foo.__proto__ === Function.prototype
foo instanceof Object //true
foo.__proto__.__proto__ === Object.prototype
A一定要是一个对象
console.log(new Number(1) instanceof Number) //true
console.log(new Number(1) instanceof Object) //true
console.log(new Number(1) instanceof Function) //false
基本包装类型1的原型链:
(1).__proto__ === Number.prototype
(1).__proto__.__proto__ === Number.prototype.__proto__ === Object.prototype
(1).__proto__.__proto__.__proto__ === Number.prototype.__proto__.__proto__ === Object.prototype.__proto__ === null