JS基础之函数

JS中 我认为设计最出色的便是函数,它几乎接近完美,但是也有些许瑕疵

函数对象

JS中的函数就是对象, 对象是 key value对的集合并拥有一个连到原型对象的隐藏连接,对象字面量产生的对象连接到Object.prototype 函数对象连接到Function.prototype。每个函数在创建时都会附加两个隐藏属性: 函数的上下文和实现函数行为的代码。

因为函数式对象,所以它们可以像其他值一样被使用,函数可以保存变量 对象和数组, 函数可以被当做参数传递给其他函数,函数也可以再返回函数,而且,因为函数是对象,所以函数可以拥有方法。

函数的与众不同在于他们可以被调用。

函数字面量

var add = function(a,b){
  return a+ b;
}
  1. 第一个为保留字 function
  2. 第二部分为函数名,可以被省略 如上例,这个被称为匿名函数
  3. 函数的参数,它们不像普通的变量那样将初始化为undefined 而是该函数被调用时初始化实际提供的参数
  4. 大括号内部的一组语句,这些是函数的主体。

调用

调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数,每个函数还接受两个附加的参数: this和arguments,在刚写JS的代码的时候经常被this搞崩溃,this在面向对象编程的时候非常的重要,它的值取决于调用的模式。JS中一共有六种调用模式: 每一种调用方式在如何初始化this存在差异性。

  1. 方法调用 保存为一个对象的属性的时候
  2. 函数调用 不是为一个对象的属性的时候
  3. 构造器调用
  4. apply call调用
  5. bind()调用 es6 用来返回一个重新绑定this的函数
  6. 箭头符号调用 es6 this的绑定从它的封闭域来绑定

函数的参数问题: 当实际的参数的个数和形式参数的个数不匹配时,不会导致运行时错误,如果实际参数过多,过多的参数会被忽略掉,如果实际参数过少,则缺失的值会被替换为undefined 对参数值不会进行类型检查,任何类型的值都可以呗传递给任何参数

this

调用点 为了理解this绑定,我们不得不理解调用点: 函数在代码中被调用的位置。在实际中,我们要考虑到调用栈,首先来段代码来熟悉一下调用栈和调用点

function baz() {
    // 调用栈是: `baz`
    // 我们的调用点是global scope(全局作用域)

    console.log( "baz" );
    bar(); // <-- `bar`的调用点
}

function bar() {
    // 调用栈是: `baz` -> `bar`
    // 我们的调用点位于`baz`

    console.log( "bar" );
    foo(); // <-- `foo`的call-site
}

function foo() {
    // 调用栈是: `baz` -> `bar` -> `foo`
    // 我们的调用点位于`bar`

    console.log( "foo" );
}

baz(); // <-- `baz`的调用点

函数是如何被调用的决定了函数执行期间this指向哪里,this在一般人看来有点头晕在于亮点1. this不是在编译的时候确定的,相反作为一个动态语言来说 this是在函数运行的时候来确定的,2. this如何绑定取决于它是如何被调用的

  1. 函数调用

    function foo(){
      console.log(this.a);
    }
    
    var a = 2;
    foo();
    

    函数调用来说 this 默认是绑定在全局变量中, 如果js运行在浏览器中则为window 如果是node.js则为undefined。

  2. 方法调用

    如果函数式作为一个对象的属性来调用的话,也就是调用点是有一个环境对象(context object),当函数是在一个对象中 通过 . 符号来触发的时候 ,此时函数中的this 默认是指向它所在的对象种。

    function foo(){
      console.log(this.a);
    }
    
    var obj = {
      a : 2,
      foo: foo
    }
    
    obj.foo();// 2
    

    调用点使用Obj环境来引用函数,因为obj是foo调用的this 所以 this.a就是obj,a的同义词

    只有对象属性引用链中的最后一层是影响调用点的

    function foo() {
     console.log( this.a );
    }
    
    var obj2 = {
     a: 42,
     foo: foo
    };
    
    var obj1 = {
     a: 2,
     obj2: obj2
    };
    
    obj1.obj2.foo(); // 42
    

    this丢失 在方法调用的过程中,最常见的问题便是this丢失导致的错误。先看下端代码

    function foo() {
     console.log( this.a );
    }
    
    var obj = {
     a: 2,
     foo: foo
    };
    
    var bar = obj.foo; // 函数引用!
    
    var a = "oops, global"; // `a`也是一个全局对象的属性
    
    bar(); // "oops, global"
    

    上面代码 ,首先bar 看上去是obj.foo的引用,实际上其实它不过是foo的自己的引用而已,另外,起作用的调用点是bar() 为普通的函数调用,导致此处的this 其实是绑定到全局变量上面。

    这种错误在回调函数比较常见

    function foo() {
     console.log( this.a );
    }
    
    var obj = {
     a: 2,
     foo: foo
    };
    
    var a = "oops, global"; // `a`也是一个全局对象的属性
    
    setTimeout( obj.foo, 100 ); // "oops, global"
    

    不管是哪种改变this的方式,你都不能真正地控制你的回校函数引用如何被执行,所以一般我们需要固定this来解决问题

    1. apply call调用

      我们可以明确的指定this来明确绑定this, 允许我们强制函数的this指定我们需要的参数

      function foo() {
        console.log( this.a );
      }
      
      var obj = {
        a: 2
      };
      
      foo.call( obj ); // 2
      

      在函数固定绑定中 有一个小技巧,我们可以利用闭包的概念来实现硬绑定来防止我们需要绑定的this没改变。

      function foo() {
        console.log( this.a );
      }
      
      var obj = {
        a: 2
      };
      
      var bar = function() {
        foo.call( obj );
      };
      
      bar(); // 2
      setTimeout( bar, 100 ); // 2
      
      // `bar`将`foo`的`this`硬绑定到`obj`
      // 所以它不可以被覆盖
      bar.call( window ); // 2
      
    2. 构造器绑定

      something = new MyClass(..);
      

      它看起来和我们之前使用的面向对象的语言是一样,其实Js的机制和new在JS的用法面向对象的功能,没有任何联系。

      首先,让我们重新定义JavaScript的“构造器”是什么。在JS中,构造器 仅仅是一个函数,它们偶然地被前置的new操作符调用。它们不依附于类,它们也不初始化一个类。它们甚至不是一种特殊的函数类型。它们本质上只是一般的函数,在被使用new来调用时改变了行为。

      当在函数前面被加入new调用时,也就是构造器调用时,下面这些事情会自动完成:

      1. 一个全新的对象会凭空创建(就是被构建)
      2. 这个新构建的对象会被接入原形链([[Prototype]]-linked)
      3. 这个新构建的对象被设置为函数调用的this绑定
      4. 除非函数返回一个它自己的其他 对象,这个被new调用的函数将 自动 返回这个新构建的对象。

判断this

现在,我们可以按照优先顺序来总结一下从函数调用的调用点来判定this的规则了。按照这个顺序来问问题,然后在第一个规则适用的地方停下。

  1. 函数是和new一起被调用的吗(new绑定)?如果是,this就是新构建的对象。

    var bar = new foo()

  2. 函数是用callapply被调用(明确绑定),甚至是隐藏在bind 硬绑定 之中吗?如果是,this就是明确指定的对象。

    var bar = foo.call( obj2 )

  3. 函数是用环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this就是那个环境对象。

    var bar = obj1.foo()

  4. 否则,使用默认的this默认绑定)。如果在strict mode下,就是undefined,否则是global对象。 var bar = foo()

箭头函数

一般我们遵循上面的四种规则,ES6引入了一种不适合上面这些规则的函数: 箭头函数。箭头函数不是靠关键字function来声明的,而是所谓的箭头函数: =》 ,箭头函数从封闭它的作用域(function global)采取this绑定。

function foo() {
  // 返回一个arrow function
    return (a) => {
    // 这里的`this`是词法上从`foo()`采用
        console.log( this.a );
    };
}

var obj1 = {
    a: 2
};

var obj2 = {
    a: 3
};

var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是3!

foo中会创建的箭头函数捕捉foo调用的this,因为foo()被this绑定到obj1,所以bar(被返回的箭头函数的一个引用),一个箭头函数的this绑定是不能被覆盖的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容