js函数绑定方式(即this指向)

2,793 阅读5分钟

函数绑定方式(即this指向)

函数绑定和this指向在js中非常的重要,不同的函数绑定方式会影响函数中this的指向。函数主要的绑定方式有以下四种:

  1. new绑定
  2. 默认绑定
  3. 隐式绑定
  4. 显式绑定

思考:函数声明有几种方式?

下面通过具体的面试题目来做一个代码逻辑分析:

new绑定

知识点: 箭头函数 setTimeout 就近原则查找 捕获上下文变量 this和变量的指向是不一样的 构造函数

var a = 1;
a = 2;
window.a = 3;
function Test() {
    let a = 4;
    this.a = 5;
    setTimeout(function () {
        // 输出 4,就近原则
        console.log(a);
    }, 10);
    setTimeout(function () {
        // 输出 3,this被指向window
        console.log(this.a);
    }, 20);
    setTimeout(() => {
        // 输出 4,箭头函数 捕获上下文变量
        console.log(a);
    }, 30);
    setTimeout(() => {
        // 输出 5,箭头函数无this,捕获上下文this
        console.log(this.a);
    }, 40);
    // 此时window.a 是多少呢?结果是3;在浏览器和浏览器控制台中执行 a, window.a 是一个对象
}
// new 绑定, this指向新对象
new Test();

构造函数定义:通过new函数名来实例化对象的函数叫构造函数。任何的函数都可以作为构造函数存在。

默认绑定

知识点: 默认绑定this指向 直接调用

var a = 1;
a = 2;
window.a = 3;
function Test() {
    let a = 4;
    // 修改window
    this.a = 5;
    setTimeout(function () {
        // 输出 5,this被指向window
        console.log(this.a);
    }, 20);
    setTimeout(() => {
        // 输出 5,箭头函数无this,捕获上下文this,上下文this指向window
        console.log(this.a);
    }, 40);
}
// 默认绑定 this指向window
Test();

默认绑定、隐式绑定、显式绑定练习

知识点:判断好绑定方式是确定this指向的关键一步

var number = 5;
var obj = {
    number: 3,
    // fn1定义时就是一个闭包, 
    fn1: (function () {
        var number;
        // 过程一:默认绑定,this指向window; this.number = 10
        this.number *= 2;
        number = number * 2;
        number = 3;
        // 自执行函数被执行过,fn1 = 下面的函数体,在过程二中只执行函数内的代码;且过程一已经改变了obj的值了
        return function () {
            // 过程一:默认绑定,this指向window
            // 过程二:隐式绑定,注意过程一对全局参数的改变;this指向obj
            var num = this.number;
            // 过程一:window.number 已经是20了
            this.number *= 2;
            // 过程一: num 输出 10
            // 过程二: 这里的this是obj  输出 3
            console.log(num);
            number *= 3;
            // 过程一:输出 9;闭包指向 var number;
            // 过程二:输出 27;闭包
            console.log(number);
        }
    })()
}
// ********************** 过程一 **********************

// 1.隐式绑定, 但fn1在声明式就形成了一个闭包, 这段代码nmuber不会超出自执行函数的范围,于是obj下的number就没有用了
var fn1 = obj.fn1;

// 2.显式绑定,但显式绑定null,undefined时 会变成默认绑定,最终还是隐式绑定,不过此时的fn1指向引用和obj没有关系,可以看做是【默认绑定】
// 判断好绑定方式是确定this指向的关键一步
fn1.call(null);

// ********************** 过程二 **********************
// 隐式绑定
obj.fn1();
console.log(window.number);

结果:
10
9
3
27
20

这道题要把this的指向变化和闭包的逻辑分开来看, 这里特别容易混淆。

关联知识点

闭包(closure)

在函数外部自然无法读取函数内的局部变量

复习一下什么是闭包?

简单讲即两种情况:

  1. 函数作为返回值
  2. 函数作为参数传递

域 Scopes

  • ES6有了 block块级作用域
  • closure 闭包的内容

一道关于闭包的题目

var x = 1, y = 2;
var z = function () {
    var x = 2;
    return {
        // 就近原则(return时, x被赋值等于2),注意此时的x: 2,和上面的var x = 2; 已经没有关系了
        x: x,
        // y相当于函数名,此处就是一个闭包
        y: function (a, b) {
            // 闭包的参数会从父函数中查找,即var x = 2,当前代码下,当y被执行时x变成了3
            x = a + b;
        },
        z: function () {
            // 当a.z()时 返回的x为3  
            return x;
        }
    }
};
// 默认绑定
a = z();
// 闭包中的x变成了3 
a.y(x, y);
console.log(a.z(), a.x, x);

结果:
3
2
1

a = z();时 观察一下a对象

再添加一个变量w来观察一下,更加直观

var x = 1, y = 2;
var z = function () {
    var x = 2;
    // 新增一个变量
    var w = 3;
    return {
        x: x,
        y: function (a, b) {
            x = a + b;
            console.log(y);
            console.log(w);
        },
        z: function () {
            return x;
        }
    }
};
a = z();

思考: 闭包中的this指向谁?

拓展

箭头函数编译后的es5怎么实现的?

// 源代码
function Test() {
    setTimeout(() => {
        console.log(this.a);
    }, 40);
}
// 编译完成
"use strict";
function Test() {
  // 是不是很熟悉,babel用箭头函数变了个魔术
  var _this = this;
  setTimeout(function () {
    console.log(_this.a);
  }, 40);
}

箭头函数的优缺点?

在vue的methods、watch中,不应该使用箭头函数来定义 watcher 函数。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.vue选项中的方法将是 undefined。

函数和方法的区别?

函数(function)是一段代码,需要通过名字来进行调用。它能将一些数据(函数的参数)传递进去进行处理,然后返回一些数据(函数的返回值),也可以不返回数据。

function whoami() {
    console.log(this);
}

方法(method)是通过对象调用的javascript函数。也就是说,方法也是函数,只是比较特殊的函数。

let obj = Object();
obj.whoami = whoami;
obj.whoami();

当将函数和对象和写在一起时,函数(function)就变成了方法(method)。

什么是解析器?

JavaScript解析器的作用是将JavaScript代码分解成AST, 具体的过程要认真了解一下编译原理。

闭包的优缺点?

参考

IIFE: Immediately Invoked Function Expression(立即执行函数表达式)

  • 在javascript(ES5)中,是没有块级作用域的概念的。
  • 创建块级(私有)作用域,避免了向全局作用域中添加变量和函数,因此也避免了多人开发中全局变量和函数的命名冲突
  • IIFE中定义的任何变量和函数,都会在执行结束时被销毁

函数声明有几种方式?

1.直接声明;2.函数表达式(匿名函数) www.cnblogs.com/sticktong/p…