js中下面的结果?我不明白 英文的是函数提前了,var又提前了,为什么var没有取代foo()函数

问题如上感谢回答,具体运用上又有什么区别和优劣?
在Javascript中,函数及变量的声明都将被提升到函数的最顶部,也就是说我们可以先使用后声明,但函数表达式和变量表达式只是将函数或者变量的声明提升到函数顶部,函数表达式和变量的初始化将不被提升var js=function(){} 这种叫做函数表达式 必须先定义后使用function js(){}这种是函数声明 可以先使用后定义 它会对函数的声明进行一个提升举个例子fun();//hello,world哥
function fun(){
console.log("hello,world哥");
}方法先使用后声明这显然是没有问题的、那么我们接着看函数表达式var fun=function(){
console.log("本事啦,我的弟");
fun();//本事啦,我的弟显然没有任何问题,我们把声明和使用颠倒位置fun();
var fun=function(){
console.log("本事啦,我的弟");//Uncaught TypeError: fun is not a function
}我们发现报错了,但如果我们直接使用fun则会报一个”Uncaught ReferenceError: fun is not defined”的错误(和不声明fun报错是不一样的),其实fun也是一个变量,只不过他是function(){console.log(“本事啦,我的弟”);}的一个引用,fun的声明被提升了,但是初始化没有被提升。再给大家来一个比较有趣的关于提升的例子var a=1;
(function(){
console.log(a);
console.log(a);
})();大家认真的思考一下、打印结果是什么?正确答案:undefined 2 大家应该想的是1,2 其实这个结果就是变量提升的原因
已被提问者采纳
你还没有登录,请先登录或注册慕课网帐号
前者是定义,后者是声明。对于定义,代码得运行到那一行才能解析这个函数,在这之前调用将报错。对于声明,在预编译期间编译器会将声明放到代码树顶端,在任何位置都可以调用。
你还没有登录,请先登录或注册慕课网帐号
function js(){} 这个我认为是一个方法体var js=function(){} 声明一个参数,并且用后边的那个方法赋值
你还没有登录,请先登录或注册慕课网帐号
function js (){}这种会声明提前,后者不会声明提前
你还没有登录,请先登录或注册慕课网帐号
坚持与信念,星星与石头
楼下说得不错,用function js()吧,经常需要调用的可以直接通过js()就可以调用了
你还没有登录,请先登录或注册慕课网帐号
这位童鞋很懒,什么也没有留下!
function js(){}这种方式的声明函数,当声明完成之后,函数js()就会进行前置,在哪里都会调用到。var js=function(){}这种变量方式赋值的函数,变量js会前置,但是方法并没有进行赋值,所以如果在赋值语句之前调用该方法会出现undefined,因为该方法还未执行。
你还没有登录,请先登录或注册慕课网帐号
前面一个是声明一个变量,后面一个是声明一个函数。
你还没有登录,请先登录或注册慕课网帐号
一枚很奇葩的技术怪人
在JavaScript中,前面是声明了个变量js,这个变量是个方法,这个变量在js中是可以改变的,第二中是声明了个方法,但是这个js这个无法改变。
你还没有登录,请先登录或注册慕课网帐号
56947人关注
23485人关注
52476人关注
Copyright (C)
All Rights Reserved | 京ICP备 号-22013年8月 Web 开发大版内专家分月排行榜第一2010年2月 Web 开发大版内专家分月排行榜第一
2013年7月 Web 开发大版内专家分月排行榜第二2013年6月 Web 开发大版内专家分月排行榜第二
2013年8月 Web 开发大版内专家分月排行榜第一2010年2月 Web 开发大版内专家分月排行榜第一
2013年7月 Web 开发大版内专家分月排行榜第二2013年6月 Web 开发大版内专家分月排行榜第二
本帖子已过去太久远了,不再提供回复功能。因一段JavaScript代码引发的闲扯
前两天,一朋友给我发了一段Script代码:
function f1(){
    var n=999;
    nAdd=function(){
    };
    function f2(){
      alert(n);
    return f2;
  var result1=f1();
var result2=f1();
  result1(); // 999
result2();//999
  nAdd();
 result1(); // 是999而不是1000,这是为何呢?
result2();//1000
问题的原型在这里:javascript关于闭包的面试题
这里主要利用两个知识点:声明提升 和 闭包。
在中,没有用var关键字声明的变量均是隐式的全局变量。首先将全局变量声明提前,代码是这样子:
var nAdd =
function f1(){
    var n=999;
    nAdd=function(){
    };
    function f2(){
      alert(n);
    return f2;
  var result1=f1();
var result2=f1();
  result1(); // 999
result2();//999
  nAdd();
 result1(); // 是999而不是1000,这是为何呢?
result2();//1000
然后根据个人理解来解释一下原因:
var result1=f1();
var result2=f1();
这里两次调用f1(),因返回的f2起到了闭包的作用,因而result1和result2都各自保存对n的引用,但是nAdd被赋值了两次,后一次赋值覆盖了前一次的值。为了便于解释,引入两个临时变量,当第一次调用f1时,代码是这样子的:
function f1(){
    var n=999;
    //n在result1中的引用为temp1
    var temp1 =
    nAdd=function(){
temp1 += 1;
    };
    function f2(){
      alert(temp1);
    return f2;
第二次调用f1时,代码应该是这样子的:
function f1(){
    var n=999;
//n在result2中的引用为temp2
    var temp2 =
    nAdd=function(){
temp2 += 1;
    };
    function f2(){
      alert(temp2);
    return f2;
所以当调用nAdd()时,只影响result2中n的值,对result1中的n没有影响。在后面再对result1和result2追加一次调用,结果应该是这样子的:
var result1=f1();
var result2=f1();
  result1(); // 999
result2();//999
  nAdd();
 result1(); // 999
result2();//1000
result1(); // 999
result2();//1001
问题就解释到这里。如果你有其它理解,欢迎留下评论。
查找了关于声明提升和闭包的文章,除了扯一下这两点,也一并扯扯JavaScript中的回调函数、作用域和IIFEs(Immediately-Invoked Function Expressions)。
声明提升(Hoisting)
对于变量声明或函数表达式。可以看作由两部分组成:声明和赋值。JavaScript隐式地提升声明部分到封闭函数的顶部,而将赋值留在原地。需要注意的一点是:变量和函数声明存在此特征,但是函数表达式是没有这个特征的。看几段代码就知道这是怎么一回事了。
var f=function(){
function f(){
猜猜上面的结果是什么?看看下面的等效代码,来校验一下你猜的答案:
function f(){
f = function(){
两次弹出的结果都是1,你猜对了吗?
再举两个简单的例子来分别说明一下变量声明提升和函数声明提升。
(function() {
var foo = 1;
alert(foo +
var bar = 2;
var baz = 3;
能猜出结果吗?看看下面的等效代码,来校验一下你猜的答案:
(function() {
alert(foo +
所以结果是1、undefined、undefined。
函数声明提前的好处是可以提前调用要定义的函数:
function foo() {
alert(Hello!);
//其等效的代码
function foo() {
alert(Hello!);
正如之前所说的,函数表达式是没有声明提前的,所以按上述方式调用会出错:
//undefined is not a function
var foo = function() {
alert(Hello!);
闭包是JavaScript最优雅、最具表现力的特性之一。创建闭包的两种常见方式:
1、将内部函数作为值从外部函数返回
function B()
function A()
alert(闭包函数处理本地变量temp = +temp);
//或者直接返回
//return functio()
alert(闭包函数处理本地变量temp = +temp);
var a = B();
2、利用变量的作用范围形成闭包函数
function B()
F=function ()
alert(利用变量范围形成闭包函数处理本地变量temp = +temp);
要熟练掌握闭包,就得知道关于它的三个基本事实。
事实一:JavaScript允许你引用在当前函数以外定义的变量。
ES5中可以用let关键字定义块级作用域,但在ES5之前,JavaScript没有块级作用域的概念,只有函数作用域和全局作用域(try&catch块是能形成块级作用域,异常绑定的变量只作用于catch块)。在函数外部一般无法访问函数内部定义的局部变量,但是闭包可以。(见上述代码)
事实二:即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量。
function show(temp){
function make(filling){
return temp +
var ham = show(ham);
ham(cheese);
//ham and cheese
var turkey = show(turkey);
turkey(swiss);
//turkey and swiss
尽管由相同的make函数定义,但是ham和turkey是两个完全不同的函数,都保持着各自的作用域。
事实三:闭包可以更新外部变量的值。
这一点在本文开始处已经有体现了。因为闭包存储的是外部变量的引用而不是值,所以对于任何具有访问这些外部变量的闭包,都可以更新。
function box(){
set:function(newVal){val = newV},
get:function(){}
type:function(){}
var b = box();
//undefined
b.set(98.6);
b.type(); //number
首先看一个嵌套函数的作用域问题:
function f(){
function test(x){
var res = [];
function f(){
res.push(f());
res.push(f());
test(true);
test(false); //?
如果你认为结果是[&local&,&global&]和[&global&],那么你就错了。在JavaScript中是没有块级作用域的(至少在ES 6之前,ES6将通过let关键字支持块级作用域),所以返回的结果应该[&local&,&local&]和[&local&]。但是要模拟一下块级作用域呢?接着看代码:
function f(){
function test(x){
var res = [],g=f;
g = function(){
res.push(g());
res.push(g());
test(true);
//[local,local]
test(false); //[global]
利用var声明和函数表达式就可以模拟块级作用域效果了,但为什么第一组返回[&local&,&local&]而不是[&local&,&global&]呢?看下面的一段简单代码:
var name = Pomy
function getName(){
var name =
console.log(name);
getName();
分析一下:首先在全局范围内声明了一个变量name,并赋值为P然后,声明了一个函数getName,在其内部定义一个和全局变量同名的变量name,并赋值为dwqs;最后调用函数getName,在控制台输出dwqs。
在变量作用域中,JavaScript解释器会先在当前的执行域中寻找变量name,若找到变量name,则使用变量name,停止搜寻;反之,则向上一级作用域继续搜寻变量name,一直搜寻到顶级作用域,若没有找到变量,则抛出错误。
下面的代码就能很好的解释上面的废话了:
var locales = {
europe: function() {
// The Europe continent's local scope
var myFriend = M
var france = function() {
// The France country's local scope
var paris = function() {
// The Paris city's local scope
console.log(myFriend);
locales.europe();
在函数paris中并没有定义myFriend变量,则在其父作用域france中搜寻,也没有定义myFriend变量,继续向上搜寻,在祖先作用域europe中找到变量,停止搜寻,输出变量的值。
这种查找方式被称为词法(静态)作用域。程序的静态结构决定了变量的作用域,而变量作用域是由其内部的代码定义的,并且嵌套函数能够接近外部作用域声明的变量。无论是以函数调用还是其它方式调用,其词法(静态)作用域取决于函数在哪里被声明。
对于JavaScript,在多层嵌套的作用中,同名变量是能被识别的,在这种情况下,局部变量优先于全局变量。如果你声明了同名的全局变量和局部变量,当在函数中使用时,局部变量会被优先使用,这种行为被称为遮蔽(shadowing)。简单地说,就是局部变量覆盖了全局变量。
var test = I'
function testScope() {
var test = I'
console.log (test);
testScope();
// output: I'm local
console.log(test);
// output: I'm global
最后,看一道javascript-puzzlers的测试题:
vcHt0ru3vaOsvLTKucv8w8eyu9TazazSu7j21/fTw9PyoaM8L3A+DQo8aDMgaWQ9"回调函数">回调函数
在JavaScript中,函数是一等对象,这样造成的结果是函数可以作为参数传递给另一个函数或作为一个函数的返回值。
将函数作为参数或者返回值的函数称为高阶函数,被作为参数传递的函数称为回调函数。
回调函数常用的一个方式是当调用window对象的setTimeout或setInterval函数时&它们接收一个回调函数作为参数:
function showMessage(message){
setTimeout(function(){
alert(message);
showMessage('Function called 3 seconds ago');
Try out the example in JS Bin
另一个示例是,当为页面上的元素添加事件监听时,需要在事件触发时提供一个回调函数:
// HTMLClick me;
// JavaScript
function showMessage(){
alert('Woohoo!');
var el = document.getElementById(btn);
el.addEventListener(click, showMessage);
Try out the example in JS Bin
然而理解高阶函数和回调函数最快的方式是自己去创建,因此,现在就创建一个:
function fullName(firstName, lastName, callback){
console.log(My name is
+ firstName +
+ lastName);
callback(lastName);
var greeting = function(ln){
console.log('Welcome Mr. ' + ln);
fullName(Jackie, Chan, greeting);
Try out the example in JS Bin
回调本质上是一种设计模式,并且jQuery(包括其他框架)的设计原则遵循了这个模式。回调函数一般在同步情境下是最后执行的,而在异步情境下有可能不执行,因为事件没有被触发或者条件不满足。
回调函数的使用场合:
资源加载:动态加载js文件后执行回调,加载iframe后执行回调,ajax操作回调,图片加载完成执行回调等等。 DOM事件及Node.js事件基于回调机制(Node.js回调可能会出现多层回调嵌套的问题)。 setTimeout的延迟时间为0,这个hack经常被用到,setTimeout调用的函数其实就是一个callback的体现 链式调用:链式调用的时候,在赋值器(setter)方法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是this指针,如果要实现链式方法,可以用回调函数来实现 setTimeout、setInterval的函数调用得到其返回值。由于两个函数都是异步的,即:他们的调用时序和程序的主流程是相对独立的,所以没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,否则也就失去了setTimeout及setInterval的意义了,所以用return已经没有意义,只能使用callback。callback的意义在于将timer执行的结果通知给代理函数进行及时处理。
IIFEs(Immediately-Invoked Function Expressions)
一个立即执行函数是在创建之后立即执行的函数表达式。
有两种语法稍微有点不同的方式来创建IIFEs:
// variant 1
(function () {
alert('Woohoo!');
// variant 2
(function () {
alert('Woohoo!');
还有很多种方式来创建IEFEs,收集的方式如下:
( function() {}() );
( function() {} )();
[ function() {}() ];
~ function() {}();
! function() {}();
+ function() {}();
- function() {}();
delete function() {}();
typeof function() {}();
void function() {}();
new function() {}();
new function() {};
var f = function() {}();
1, function() {}();
1 ^ function() {}();
1 & function() {}();
ps:除了能提高一下逼格,基本都没什么用。
关于IEFEs,还需要知道的三件事。
1、如果你给函数分配了变量,就不需要将整个函数括放在括号里,因为它已经是一个表达式
var sayWoohoo = function () {
alert('Woohoo!');
2、IIFE末尾的分号是必须的,否则代码可能会不正常运行
3、可以给IIFE传递参数(毕竟也是一个函数),可以参考下面的示例:
(function (name, profession) {
console.log(My name is
+ name + . I'm an
+ profession + .);
})(Jackie Chan, actor);
// output: My name is Jackie Chan. I'm an actor.
Try out the example in JS Bin
将全局对象作为参数传递给IIFE是很普遍的模式,因而它能调用内部函数而不依赖window对象,这样能在环境中保持代码的独立性。不管运行平台是什么,下面的代码将创建一个变量global来指向全局对象:
(function (global) {
// access the global object via 'global'
This code will work both in the browser (where the global object is window), or in a Node.js environment (where we refer to the global object with the special variable global).
One of the great benefits of an IIFE is that, when using it, you don&t have to worry about polluting the global space with temporary variables. All the variables you define inside an IIFE will be local. Let&s check this out:
1(function(){ var today = new Date(); var currentTime = today.toLocaleTimeString(); console.log(currentTime); // output: the current local time (e.g. 7:08:52 PM) })(); console.log(currentTime); // output: undefined
Try out the example in JS Bin
是上面的示例中,第一个console.log()运行正常,而第二个console.log()运行失败,由于IIFE,today 和 currentTime成了局部变量。
我们都知道,闭包是保存外部变量的引用,而不是值的副本,因此,它返回外部变量的最新值。那么,你认为下面的输出会是什么?
function printFruits(fruits){
for (var i = 0; i & fruits. i++) {
setTimeout( function(){
console.log( fruits[i] );
}, i * 1000 );
printFruits([Lemon, Orange, Mango, Banana]);
Try out the example in JS Bin
并不是按照我们的期望依次输出数组的四个元素,而是输出四次undefined。因为i的最新值是4,而fruits[4]是undefined。
为了修复这个问题,可以把setTimeout()放在IIFE中,并提供一个变量来保存i的副本:
function printFruits(fruits){
for (var i = 0; i & fruits. i++) {
(function(){
var current =
// define new variable that will hold the current value of i
setTimeout( function(){
console.log( fruits[current] );
// this time the value of current will be different for each iteration
}, current * 1000 );
printFruits([Lemon, Orange, Mango, Banana]);
Try out the example in JS Bin
另外一种方式是将i作为参数传递给IIFE:
function printFruits(fruits){
for (var i = 0; i & fruits. i++) {
(function(current){
setTimeout( function(){
console.log( fruits[current] );
}, current * 1000 );
printFruits([Lemon, Orange, Mango, Banana]);}

我要回帖

更多关于 var foo 2 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信