上一章节中,我们用vue实现了简单的todo应用,如果我们把vue.js引用去掉,首先是界面显示原始文本内容

然后console报错

index.html:22 Uncaught ReferenceError: Vue is not defined
    at index.html:22

原因是我们在代码中通过代码var app = new Vue({...})创建了Vue对象,Vue对象是定义在vue.js中,现在没了vue.js自然报错。

这章的内容就是解决Vue对象找不到问题,让代码不再报错,为什么要先解决js报错问题而不是文本显示问题?因为Vue对象提供了Vue所有的功能,其中就包括值绑定,解决了值绑定的问题,界面自然也会恢复正常,所以先解决Vue对象找不到问题。

匿名函数和闭包

先解释什么是匿名函数,顾名思义就是没有定义名称的函数,比如下面这几种


// 函数表达式
var fn = function add(a,b){
    return a+b;
}

var c  = fn(1,2);

// 上面例子的结合
(function(a,b){return a+b;})(1,2);

//事件响应函数

var button = document.getElementById('submitButton');
button.onclick = function(){
    //do something
}

还有其他情况,这里就不列举,java也有匿名类,跟js的匿名函数类似。

再来聊一下前端面试必问,无数前端学子竟折腰的闭包是什么东西,这东西确实难理解,我觉得很大一个原因是翻译的问题,比如匿名函数,看名称大概就知道什么意思,闭包两个字没有任何想象的空间,而且还可能引起误会,闭包实际上跟包并没有什么关系。我的不严谨但直观的理解是闭包就是类,你可以理解为闭包就是java里的类,在java类中,类可以定义变量和方法,方法可以引用类的变量,类比js,java类就是js的函数,java类变量就是js函数中定义的变量,java类的方法就是定义在js函数内部的函数。举个例子:


function CircleArea(r){
    var PI=3.14159;
    
    function calculate(){
        return PI*r*r;
    }
    
    return calculate;
}

var circle2=CircleArea(2);
var area=circle2();
console.log(area);

正常当函数执行完变量会进行回收,在函数CircleArea中返回了calculate这个函数,理论上PI已经回收因为函数已经执行完,但在函数外部执行var area=circle2();时,结果依然时正确的,说明PI并没有被回收,这个就是闭包的特性,函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure),这个才是官方对闭包的定义。

那么在我们这个应用中,Vue是以什么形式存在,对象还是函数?毫无疑问肯定是函数,只有在函数中才能实现复杂的逻辑,才能实现生命周期的控制。所以如果想让var app = new Vue({...})这行代码不报错,一个可行的方案如下:


var Vue = function (options){
    console.log(options);
}

创建一个js文件,名称就叫做vuex.js,复制上面的代码到js文件中,将上一章html中引用vue.js的改为引用vuex.js,重新运行,这时候虽然界面还是原样输出,但是console已经不报错了,而且也正确打印出数据。

这一章的目的也就达到了。能不能更优雅点?如果看过jquery源码和vue源码,大家都能在代码的开头看到这么一段代码

(function( global, factory ) {....})(this,function(options){...})

是否有点像上面匿名函数(function(a,b){return a+b;})(1,2);,没错,这个就是立即执行的匿名函数,整个框架的逻辑就写在function中,配置通过options传入,这样写有什么好处?

  • 更加优雅整洁
  • 拥有独立的运行环境,多个框架之间不会相互冲突

其中保证拥有独立运行环境才是重要原因,通过匿名函数实现了一个闭包,整个框架的逻辑在闭包中完成,通过function的return给调用者api。但如果通过匿名函数+闭包的方式,如何保证能new Vue({})呢,可以全局声明Vue变量,将上面的代码修改为以下代码:

(function(global, factory) {
    global.Vue = factory
})(this, function(options) {
    console.log(options);
});

其中global对象在调用时传入this,this表示window对象,factory就是实现整个逻辑的匿名函数,通过global.Vue = factory将匿名函数定义在window.Vue上,这样全局都可以使用new Vue实现函数的调用。

参考


DQuery
300 声望93 粉丝

幸福是奋斗出来的