今天与一挚友加同事调试一段奇葩的javascript代码,在分析出结果后,让我萌生了写此篇文章的想法,如有不对之处望指正,也欢迎大家一起讨论。缩减后的js代码如下,你是否能准确说明他的输出值呢?
function DemoFunction(){
this.init = function(){
var func = (function(va){
this.va = va;
return function(){
va += this.va;
return va;
}
})(function(va1, va2){
var va3 = va1 + va2; //干扰代码
return va1;
}(1,2));
console.log(func(20));
this.func = func;
console.log(this.func(100));
}
}
var a = new DemoFunction();
a.init();
如果要解释这段代码,首先我们得有如下几个概念:
这是代码的重点,第一层代码可以缩减为如下:
function DemoFunction(){
this.init = function(){
//省略代码....
}
}
表示为DemoFunction的实例提供init方法(声明:此处有误导成份,方法应尽可能放在原型链接上,也就是prototype上。),对外公开的接口。
var func = (function(va){
this.va = va;
return function(){
va += this.va;
return va;
}
})(8/*省略代码...*/);
//省略代码....
代码虽短,但信息量巨大,但这样省略部分代码后,你是否可以清晰的看出他的层次结构。
this.va=va
这行代码,由于立即执行函数没有调用者,所以在进入可执行上下文时,this会被赋值为Global(浏览器中为window对象)。func = function(){
va += this.va;
return va;
}
只不过此时他的父级作用域是立即执行函数而已。
var func = (function(va){
this.va = va;
return function(){
va += this.va;
return va;
}
})(function(va1, va2){
var va3 = va1 + va2;
return va1;
}(1,2));
//省略代码....
va又为一个立即执行函数,这个立即执行函数接受了两个参数va1,va2,但只返回了va1。以此为据,那么可以确定va的值也就为1。接着就执行this.va=va
这句代码,由于当前this为window,所以参数va的值被赋值到了window的一个叫va的属性上。
var func = (function(va){
this.va = va;
return function(){
va += this.va;
return va;
}
})(function(va1, va2){
var va3 = va1 + va2;
return va1;
}(1,2));
console.log(func(20));
this.func = func;
console.log(this.func(100));
}
主要分析两个console.log,也是产出结果的时候到了。
va += this.va
这句代码中的this是指向当前对象的实例,但当前对象的实例上是没有va属性的。如果你调试跟踪会发现va是有值的,他的值为:1,这是为什么呢?这就是作用域链,va虽然在当前作用域没有,但在父级(也就是window的作用域下)是存一个叫va的属性的。通过此段示例代码的分析,我们可以体会到要深入理解Javascript代码,必须要明白且深度掌握他的:闭包、this、原型链(作用域链)、立即调用函数表达式、函数等概念和机理。此类概念每时每该都充斥任务一个库或者框架的代码中,有了他们做为基石,理解和看懂别人的Js代码就so easy了。