七个最有用的JavaScript技巧( 三 )


flux.addEventListener("click",%20function(e)%20{%20%20%20%20marty.timeTravel(someYearValue);});点击按钮将会在控制台输出类似下面的信息:
 
成功了!但为什么这样可以?思考我们是如何调用timeTravel方法的 。在我们按钮点击的第一个例子中,我们在事件处理程序中订阅方法自身的引用,所以它没有从父对象marty上调用 。在第二个例子中,通过this为按钮元素的匿名函数,并且当我们调用marty.timeTravel时,我们从其父对象marty上调用,所以this为marty 。
第四——构造函数中的this值当你用构造函数创建对象实例时,函数内部的this值就是新创建的对象 。例如:
var TimeTraveler = function(fName, lName) {this.firstName = fName;this.lastName = lName;// Constructor functions return the// newly created object for us unless// we specifically return something else};var marty = new TimeTraveler("Marty", "McFly");console.log(marty.firstName + " " + marty.lastName);// Marty McFlyCall,Apply和BindCall你可能开始疑惑,上面的例子中,没有语言级别的特性允许我们在运行时指定调用函数的this值吗?你是对的 。存在于函数原型上的call和apply方法允许我们调用函数并传递this值 。
call方法的第一个参数是this,后面是被调用函数的参数序列:
someFn.call(this, arg1, arg2, arg3);apply的第一个参数也是this,后面是其余参数组成的数组:
someFn.apply(this, [arg1, arg2, arg3]);我们的doc和marty实例他们自己能时间旅行,但einstein(爱因斯坦)需要他们的帮助才能完成时间旅行 。所以让我们给我们的doc实例添加一个方法,以至于doc能帮助einstein完成时间旅行 。
doc.timeTravelFor = function(instance, year) {this.timeTravel.call(instance, year);// 如果你使用apply使用下面的语法// this.timeTravel.apply(instance, [year]);};现在它可以传送Einstein 了:
var einstein = {firstName: "Einstein",lastName: "(the dog)"};doc.timeTravelFor(einstein, 1985);// Einstein (the dog) is time traveling to 1985我知道这个例子有些牵强,但它足以让你看到应用函数到其他对象的强大之处 。
这种方法还有我们没有发现的另一种用处 。让我们给我们的marty实例添加一个goHome方法,作为this.timeTravel(1985)的快捷方式 。
marty.goHome = function() {this.timeTravel(1985);}然而,我们知道如果我们订阅marty.goHome作为按钮的点击事件处理程序,this的值将是按钮——并且不幸的是按钮没有timeTravel方法 。我们能用上面的方法解决——用个一匿名函数作为事件处理程序,并在其内部调用上述方法——但我们有另一个选择——bind函数:
flux.addEventListener("click", marty.goHome.bind(marty));
bind函数实际上会返回一个新函数,新函数的this值根据你提供的参数设置 。如果你需要支持低版本浏览器(例如:ie9以下版本),你可能需要bind函数的shim(或者,如果你使用jQuery你可以用$.proxy代替,underscore和lodash都提供_.bind方法) 。

记住重要一点,如果你直接使用原型上的bind方法,它将创建一个实例方法,这将绕过原型方法的优点 。这不是错误,做到心里清楚就行了 。我写了关于这个问题得更多信息在这里 。
4.) 函数表达式vs函数声明函数声明不需要var关键字 。事实上,如Angus Croll所说:“把他们想象成变量声明的兄弟有助于理解” 。例如:
function timeTravel(year) {console.log(this.firstName + " " + this.lastName + " is time traveling to " + year);} 上面例子里的函数名字timeTravel不仅在它声明的在作用域可见,同时在函数本身内部也是可见的(这对递归函数调用非常有用) 。函数声明,本质上说其实就是命名函数 。换句话说,上面函数的名称属性是timeTravel 。函数表达式定义一个函数并指派给一个变量 。典型应用如下:
var someFn = function() {console.log("I like to express myself...");}; 也可以对函数表达式命名——然而,不像函数声明,命名函数表达式的名字仅在它自身函数体内可访问:var someFn = function iHazName() {console.log("I like to express myself...");if(needsMoreExpressing) {iHazName(); // 函数的名字在这里可以访问}};// 你可以在这里调用someFn(),但不能调用iHazName()someFn();
讨论函数表达式和函数声明不能不提“hoisting(提升)”——函数和变量声明被编译器移到作用域的顶部 。在这里我们无法详细解释hoisting,但你可以读Ben Cherry和Angus Croll两个人的伟大解释 。


推荐阅读