原文链接:https://zhuanlan.zhihu.com/p/22109292
var x = 0;
var foo = {
x:1,
bar:{
x:2,
baz: function () {
console.log(this.x)
}
}
}
var a = foo.bar.baz
foo.bar.baz() // 2
a() //0
•
this 永远指向函数运行时所在的对象,而不是函数创建时所在的对象
• 任何在执行上下文时刻的作用域都由作用域链来实现
• 在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性
• 在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.
上面的文字大家可以好好琢磨一下,可以更好的理解函数作用域。
第二步:收集函数声明、变量声明和形参,保存在环境记录项内。这个收集的过程,就是一般所谓的声明提升现象的本质。如果发现了重复的标识符,则优先级为函数声明 、形参 、变量声明(优先级低的会被无视)。
第三步:开始执行代码,环境记录项内没有的标识符会根据作用域链查找标识符对应的值,环境记录项亦有可能因赋值语句而被修改。
第四步:函数执行完毕,EC栈被弹出、销毁。
好了,第二步说的很清楚了 声明提升(Hoisting)现象就是在收集函数、变量声明和形参的过程会根据函数声明、形参、变量声明的顺序优先级来收集。
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
console.log(a);
// 输出1 由于函数声明提升,b内的实际是这样:
var x = 0;
var foo = {
x:1,
bar:function () {
console.log(this.x);
var that = this;
return function () {
console.log(this.x)
console.log(that.x)
}
}
}
foo.bar() // 1
foo.bar()() // this: 0, that: 1
上面的例子中bar里面返回了一个匿名函数,这个匿名函数可以在外部被调用即:foo.bar()() 读取到了bar的执行上下文的变量对象 that,这个函数就形成了一个闭包。
var x = 0;
var bar:function () {
var n = 999;
return function () {
return n;
}
}
var outer = bar();
outer() // 999
用途:
var add;
var bar = function () {
var n = 999;
add = function () {
n += 1;
}
return function () {
return n;
}
}
var outer = bar();
outer() // 999
add();
outer(); // 1000
说明,n一直保存在内存当中,而没有在bar()执行完成之后被销毁;
var singleton = function( fn ){
var result;
return function(){
return result || ( result = fn .apply( this, arguments ) );
}
}
更简洁一点的:
var singleton = (function () {
var instance;
return function (object) {
if(!instance){
instance = new object();
}
return instance;
}
})();