你真的懂函数吗?

函数声明方式

匿名函数

function后面直接跟括号,中间没有函数名的就是匿名函数。javascript

let fn = function() {
    console.log('我是fn')
}
let fn2 = fn
console.log(fn.name) //fn
console.log(fn2.name)//fn,fn和fn2指向的是同一个function。

具名函数

function后面有函数名字的,不是直接跟括号的的就是具名函数。
若是把一个具名函数赋值给一个变量,那么这个具名函数的做用域就不是window了。html

let fn = function fn1() {
  console.log('function')
}
console.log(fn.name) //fn1
console.log(fn1,name) // ReferenceError: fn1 is not defined

箭头函数

箭头函数是es6知识点,具备如下几个特色:java

  1. 若是只有一个参数,能够省略小括号。
  2. 若是有至少有两个参数,必须加小括号。
  3. 若是函数体只有一句话能够省略花括号,而且这一句做为返回值return。
  4. 若是函数体至少有两句必须加上花括号。
  5. 箭头函数里面是没有this的。
let fn = e => e+1
console.log(fn(1)) //2

let fn1 = (i,y) => i+y
console.log(fn1(2,3)) //5

let fn2 = (i,y) => {
  i+=1;
  y+=2;
  return i+y
}
console.log(fn2(5,6)) //13

词法做用域(静态做用域)

静态做用域又叫作词法做用域,采用词法做用域的变量叫词法变量。词法变量有一个在编译时静态肯定的做用域。词法变量的做用域能够是一个函数或一段代码,该变量在这段代码区域内可见(visibility);在这段区域之外该变量不可见(或没法访问)。词法做用域里,取变量的值时,会检查函数定义时的文本环境,捕捉函数定义时对该变量的绑定。

词法做用域:变量的做用域是在定义时决定而不是执行时决定,也就是说词法做用域取决于源码,经过静态分析就能肯定,所以词法做用域也叫作静态做用域。 with和eval除外,因此只能说JS的做用域机制很是接近词法做用域(Lexical scope)。es6

经过词法做用域树能判断变量指向关系,可是不能判定变量的值,变量的值还须要根据执行顺序进一步做出判断,看一下例子:ajax

由于JavaScript采用的是词法做用域,bian'liang的做用域基于函数建立的位置,跟调用时的位置无关。数组

var i = 1,
    j = 2,
    k = 3;

function a(o, p, x, q) {
    var x = 4;
    alert(i);

    function b(r, s) {
        var i = 11,
            y = 5;
        alert(i);

        function c(t) {
            var z = 6;
            alert(i);
        };
        
        var d = function() {
            alert(y);
        };
        c(60);
        d();
    };
    b(40, 50);
}
a(10, 20, 30); //1 11 11 5
/**
* 模拟创建一棵语法分析树,存储function内的变量和方法
*/
var SyntaxTree = {
        // 全局对象在语法分析树中的表示
    window: {
        variables:{
            i:{ value:1},
            j:{ value:2},
            k:{ value:3}
        },
        functions:{
            a: this.a
        }
    },

    a:{
        variables:{
            x:'undefined'
        },
        functions:{
            b: this.b
        },
        scope: this.window
    },

    b:{
        variables:{
                 i:'undefined'
            y:'undefined'
        },
        functions:{
            c: this.c,
            d: this.d
        },
        scope: this.a
    },

    c:{
        variables:{
            z:'undefined'
        },
        functions:{},
        scope: this.b
    },

    d:{
        variables:{},
        functions:{},
        scope: {
           scope: this.b
        }
    }
};
/**
* 活动对象:函数执行时建立的活动对象列表
*/
let ActiveObject = {
    window: {
        variables: {
            i: { value: 1 }
            j: { value: 2 }
            k: { value: 3 }
        },
        functions: {
                a: this.a
        }
    }

    a: {
        variables: {
            x: { vale: 4 },
            functions: {
                b: this.b
            },
            scope: this.window,
            params: {
                o: { value: 10 },
                p: { value: 20 },
                x: this.variables.x
                q: { vale: 'undefined' }
            },
            arguments: [this.params.o, this.params.p, this.params.x]
        }
    }

    b: {
        variables: {
            i: { vale: 11 },
            y: { vale: 5 },
        },
        functions: {
            c: this.c,
            d: this.d
        },
        params: {
            r: { value: 40 }
            s: { value: 50 }
        },
        arguments: [this.params.r, this.params.scope]
        scope: this.a
    }


    c: {
        variables: {
            z: { value: 6 },
            functions: {},
            params: {
                t: { value: 60 }
            },
            arguments: [this.params.t]
            scope: this.b
        }
    }

    d: {
        variables: {},
        functions: {},
        params: {},
        arguments: []
        this.scope: this.b
    }

}

call stack

进入call stack 时的一些规则:app

  1. 函数的全部形参(若是咱们是在函数执行上下文中)异步

    • 由名称和对应值组成的一个变量对象的属性被建立;没有传递对应参数的话,那么由名称和 undefined 值组成的一种变量对象的属性也将被建立。
  2. 全部函数声明(FunctionDeclaration, FD)ide

    • 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被建立;若是变量对象已经存在相同名称的属性,则彻底替换这个属性。
  3. 全部变量声明(var, VariableDeclaration)函数

    • 由名称和对应值(undefined)组成一个变量对象的属性被建立;若是变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。
/*
* example1:形参
*/
function test(a, b) {
    /*
    var a = 10
    var b = undefined
    根据规则1,在进入执行上下文时会自动对形参声明而且赋值。
    */ 
  console.log(a)
  var c = 10;
  function d() {}
  var e = function _e() {};
  (function x() {});
}
test(10); // 10
/*
* example2:函数声明
*/
function test(a, b) {
  console.log(a)
  function a() {}
  var e = function _e() {};
}
test(10); // ƒ a() {} .根据规则2,进入执行上下文会自动声明形参而且赋值,可是同名的函数声明会替换这个变量。

function test(a, b) {
  console.log(a)
  var a = 30;
  var a = function _e() {};
}
test(10); // 10 .根据规则2,进入执行上下文会自动声明形参而且赋值,可是同名的函数声明会替换这个变量。
/*
* example3:变量声明
*/
console.log(foo);//会打印出foo函数,根据规则3,同名的变量声明不会干扰函数声明和形参

 function foo(){
    console.log("foo");
}

var foo = 1;

this和arguments

函数调用

在es5中,函数有四种调用方式:

1. fn(p1,p2)
2. obj.fn(p1,p2)
3. fn.call(context,p1,p2)
4. fn.apply(context,p1,p2)

第三和第四种才是正常的js函数调用方式,其余两种就是语法糖。

fn(p1,p2)     等价于 fn.call(undefined,p1,p2) 等价于 fn.apply(context,[p1,p2])
obj.fn(p1,p2) 等价于 obj.fn.call(obj,p1,p2)   等价于 obj.fn.apply(obj,[p1,p2])
若是你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

this是什么??

this是call的第一个参数!!!!

var obj = {
  foo: function(){
    console.log(this)
  }
}

var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window
obj.foo() 至关于 obj.foo.call(obj) 也就至关于把函数名前面的做为call的第一个参数,也就是this,若是没有就是window。
bar() 至关于 bar.call(undefined)

在执行函数的时候,this是隐藏的一个参数,且必须是一个对象,若是不是,js是自动把它转为对象。

function fn() {
    console.log(this)
    console.log(arguments)
}
fn.call(1,2,3) // Number {1}  [2,3]

arguments

arguments是伪数组它相似于Array,但除了length属性和索引元素以外没有任何Array属性。
call和apply里面除了第一个参数以外的都是arguments,若是arguments的个数少建议使用call,使用apply也能够,若是不肯定就使用apply。
使用一下方法吧arguments转为真正的数组:

var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);

// ES2015
const args = Array.from(arguments);
const args = [...arguments]

bind

MDN 官方文档对 bind() 的定义:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

大概意思就是,bind会返回一个新的函数(并无的调用原来的函数),这个新函数会call原来的函数,call的参数由你决定。看例子:

this.x = 9;
        var module = {
          x: 81,
          getX: function() { return this.x; }
        };
        
        var retrieveX = module.getX;
        var boundGetX = retrieveX.bind(module);
        boundGetX(); // 81

retrieveX.bind(module)返回了一个新的函数boundGetX,而后调用这个新的函数的时候,把这个函数里面的this绑定到了module对象上,因此this.x就至关于module.x也就是等于81.

柯里化

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术。这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的,尽管它是Moses Schönfinkel和戈特洛布·弗雷格发明的。

说的明白一点就是,给函数传递一部分参数,让它返回一个函数去处理其余参数,举个例子,求三个数之和:

let addOne = function add(x) {
  return function(y) {
    return function(z) {
      return x+y+z
    }
  }
}

let one = addOne(3)
console.log(one)//ƒ (y) {return function (z) {return x + y + z}}
let two = one(4)
console.log(two)//ƒ (z) {return x + y + z}
let three = two(5)
console.log(three)//12

javascript函数柯里化--详细说明连接

高阶函数

在数学和计算机科学中,高阶函数是至少知足下列一个条件的函数:

  1. 接受一个或多个函数做为输入
  2. 输出一个函数

举一些高阶函数的例子:

/*
*接受一个或多个函数做为输入
*/
1. Array.prototype.filter()
2. Array.prototype.forEach()
3. Array.prototype.reduce()
4. Array.prototype.map()
5. Array.prototype.find()
6. Array.prototype.every()
/*
*输出一个函数
*/
1. fn.bind(args)

回调函数

函数A做为参数(函数引用)传递到另外一个函数B中,而且这个函数B执行函数A。咱们就说函数A叫作回调函数。若是没有名称(函数表达式),就叫作匿名回调函数。

名词形式:被当作参数的函数就是回调
动词形式:调用这个回调
注意回调跟异步没有任何关系

回调函数的使用场合

  1. 资源加载:动态加载js文件后执行回调,加载iframe后执行回调,ajax操做回调,图片加载完成执行回调,AJAX等等。
  2. DOM事件及Node.js事件基于回调机制(Node.js回调可能会出现多层回调嵌套的问题)。
  3. setTimeout的延迟时间为0,这个hack常常被用到,settimeout调用的函数其实就是一个callback的体现
  4. 链式调用:链式调用的时候,在赋值器(setter)方法中(或者自己没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来讲很差实现链式调用,由于你须要取值器返回你须要的数据而不是this指针,若是要实现链式方法,能够用回调函数来实现。
  5. setTimeout、setInterval的函数调用获得其返回值。因为两个函数都是异步的,即:他们的调用时序和程序的主流程是相对独立的,因此没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,不然也就失去了setTimeout及setInterval的意义了,因此用return已经没有意义,只能使用callback。callback的意义在于将timer执行的结果通知给代理函数进行及时处理。

回调函数的传递

传递的方式有两种,函数引用和函数表达式。

$.get('myhtmlpage.html', myCallBack);//这是对的
$.get('myhtmlpage.html', myCallBack('foo', 'bar'));//这是错的,那么要带参数呢?
$.get('myhtmlpage.html', function(){//带参数的使用函数表达式
myCallBack('foo', 'bar');
});

箭头函数与es5的函数主要区别

箭头函数的主要区别在this,箭头函数是没有this这个概念的,看例子:

setTimeout(function(a){
  console.log(this) //这个this指的是{name:'Jack'}
  setTimeout(function(a){
    console.log(this) //这个this指的是window,由于没有bind,调用setTimeout的是window
  },1000)
}.bind({name:'Jack'}),1000)
setTimeout(function(a){
  console.log(this) //这个this指的是{name:'Jack'}
  setTimeout(function(a){
    console.log(this) //这个this指的是{name: "Jack"},由于bind了外面的this也就是{name: "Jack"}
  },1000)
}.bind({name:'Jack'}),1000)
setTimeout(function(a){
  console.log(this) //这个this指的是{name:'Jack'}
  setTimeout(a=>console.log(this),1000)//这个this指的是{name:'Jack'},由于箭头函数没有this的概念,它指的this就是外面的this,也就是{name:'Jack'}
}.bind({name:'Jack'}),1000)

至此基本上说了js的全部函数内容,只是简单举个例子,更深刻的研究还须要看一些其余大佬的博客哦~~~~

相关文章
相关标签/搜索