##转载

一、函数定义的三种方式

方式1、函数声明

function foo(){}

方式2、函数表达式

var foo = function(){};

方式3、构造函数

var foo=new Function("alert('xiaoxiao')");

注意:申明提前的问题

变量和函数都有申明提前的概念,但是赋值还是在实际地方复制的。所以使用方式2的时候特别要注意了。

用方式1声明函数并使用:

foo(); // 正常运行,因为foo在代码运行前已经被创建
function foo() {}

用方式2声明函数并使用:

foo; // 'undefined'
foo(); // 出错:TypeError
var foo = function() {};//因为变量的赋值操作使用时在这里才完成

备注:表达式函数声明,需要在他后面才可调用此函数。

二、this的工作原理

this指针永远是这个引用所属的对象。

参考资料: http://blog.chinaunix.net/uid-15223977-id-2774361.html

三、闭包

javascript规定,当前作用域总是能够访问外部作用域的变量。当我们需要在外部访问私有变量的时候就不可能了。只有通过闭包来实现这个功能。

function Counter(start) {
    var count = start;
    return {
        increment: function() {
            count++;
        },

        get: function() {
            return count;
        }
    }
}

var foo = Counter(4);
foo.increment();
foo.get(); // 5

foo.get()能够输出5,是应为返回的两个闭包increment和get保持了维持着对外部作用域Counter的引用,所以总能够访问到这个作用域内定义的变量count

循环中的闭包问题

在实际开发中最容易出问题的是在循环中使用闭包

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
}

这里我们期望输出的是0到9的数字,但是实际输出的是10次10.

当console.log被调用的时候,匿名函数保持了对外部变量i的引用,此时for循环已经结束,i的值已经是10了。

解决办法:每次循环中创建变量i的拷贝,setTimeout外部的匿名函数会立即执行,并且把i作为它的参数,此时函数内e变量就拥有了i的一个拷贝。

for(var i = 0; i < 10; i++) {
    (function(e){
        setTimeout(function() {
            console.log(e);  
        }, 1000);
    })(i);
}

四、构造函数

JavaScript 中的构造函数和其它语言中的构造函数是不同的。 通过 new 关键字方式调用的函数都被认为是构造函数。

在构造函数内部 - 也就是被调用的函数内 - this 指向新创建的对象 Object。 这个新创建的对象的 prototype 被指向到构造函数的 prototype。

五、作用域和命名空间

作用域

javascript的作用域并不是其他语言中的花括号代码段,它并不支持块级作用域;而是函数作用域。

ES6提出的新的操作(let关键字)可以支持块级作用域。

var a = 5;
if(true){
    var a = 10;
}
console.log(a);//10

使用let关键字

let a = 5;
if(true){
    let a = 10;
}
console.log(a); //5

命名空间

只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 匿名包装器 轻松解决。

(function() {
    // 函数创建一个命名空间

    window.foo = function() {
        // 对外公开的函数,创建了闭包
    };

})(); // 立即执行此匿名函数