JavaScript 函数中的外部变量——理解 this

作者: 字母数字或汉字 | 来源:发表于2016-12-22 09:44 被阅读115次

js 中的 this 指向确实是个坑,网上有人轰轰烈烈地讨论它,讨论 js 闭包,其实并没有那么玄学,让我们一点点剥开它的面纱。

很多内容来自 邱桐城《JavaScript 中的 this》 的启发,基于他的文章,我写下了我的总结。

在全局作用域下

在浏览器环境下:

console.log(this);
// Window { .. }
this === window;
// true

全局作用域下,this 指向 Window 对象,这很好理解,仍然是传统 js 的结果。

在 node 环境下:

console.log(this);
// global
this === global;
// true

全局作用域下,this 指向 global 对象。

严格模式,在 node 环境下:

'use strict';
console.log(this);
// {}

遵循严格模式的规范,this 不再指向全局对象。

函数对象作用域下

function foo() {
    console.log(this);
}
foo();
// global / Window

严格模式,在 node 环境下:

'use strict';
function foo() {
    console.log(this);
}
foo();
// undefined

经过我的测试,虽然满足了规范要求,但在 node 7.2.0 下仍然出现了如上所示的不一致结果。

对象方法作用域下

let obj = {
    foo: function() {
        console.log(this);
    }
};
obj.foo();
// { foo: [Function] }
// obj 的值实际上是个匿名类的对象,foo 的值实际上是个匿名函数

作为对象方法时,this 指向该对象。

function func() {
    console.log(this);
}
let obj = {
    foo: func
};
obj.foo();
// { foo: [Function func] }

let foo1 = obj.foo;
foo1();
// global

注意到:在函数体内使用的、在函数体外定义(声明)的变量,是 传引用 的。

你可能对这个例子印象深刻:

var foos = [];
for (var i = 0; i < 3; ++i) {
    foos.unshift(function () {
        console.log(i);
    });
}
console.log(i);
// 3
for (var j in foos) {
    ++i;
    console.log(i);
    // 4 5 6
    foos[j]();
    // 4 5 6
}

i 变量在函数内是外部变量的引用,所以当函数外的 i 值变化了,函数内的 i 值也一同变化。

避免这样的外部变量引用也很简单,使用 constlet 这样的新关键字是最简单的一种,它们比传统的 var 有着更严谨的定义域,使该变量无法被运行时上下文访问到而保证其值不被替换:

const foos = [];
for (let i = 0; i < 3; ++i) {
    foos.unshift(function () {
        console.log(i);
    });
}
for (let i in foos) {
    foos[i]();
}
// 0 1 2

并不是新的关键字解决了该问题,而是新的关键字拥有更严谨的作用域。如果该变量在运行时上下文中仍能访问,那问题依旧:

const foos = [];
let i;
for (i = 0; i < 3; ++i) {
    foos.unshift(function () {
        console.log(i);
    });
}
for (let j in foos) {
    foos[j]();
}
// 3 3 3

看过上面的例子,你应该明白,函数内的 this 也是这样一个外部变量的引用。在传统使用 function 关键字的语法中,确实如此;但在 ES6 引入的新式 => 语法中,事情不一样了,你可以看做在函数内插入了这么一行伪代码:

// 这不是真正的 js 代码
const this = outer.this;

试看一例:

'use strict';

const obj1 = {
    foo: function() {
        console.log(this);
        return () => {
            console.log(this);
        };
    }
};
const foo1 = obj1.foo();
// { foo: [Function: foo] }
foo1();
// { foo: [Function: foo] }

const obj2 = {
    foo: function() {
        console.log(this);
        return function () {
            console.log(this);
        };
    }
};
const foo2 = obj2.foo();
// { foo: [Function: foo] }
foo2();
// undefined

=> 语法定义的函数对象,其 (const) this 指向定义时的上下文的 this,而不像 function 关键字定义的函数对象,其 (var) this 会跟随外部 this 的变化而变化。

在构造函数对象作用域下(使用 new 关键字)

'use strict';

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

var a = new A();
// A {}
console.log(a);
// A {}

var b = A();
// undefined
console.log(b);
// undefined

构造函数中 this 指向其构造出来的对象,但是一定不要忘记使用 new 关键字。

call / apply / bind

js 中的函数对象,其 prototype 中定义了如下三个函数:

func.call(thisArg[, arg1[, arg2[, ...]]]);

执行函数 func,使用第一个参数作为 this,其他参数作为 func 的实参,一一对应。

func.apply(thisArg[, [arg1, arg2, ...]]);

执行函数 func,使用第一个参数作为 this,第二个参数为数组,数组中的每个元素作为 func 的实参,一一对应。

var foo = func.bind(thisArg[, arg1[, arg2[, ...]]]);

绑定 func 的 this 和所有参数,返回一个新的函数,但不执行它。

bind 的 this 对 new 关键字无效,但其他实参有效:

function A(name) {
    console.log(this.name);
    this.name = name;
    console.log(this.name);
}
var obj = {
    name: "obj"
};
var B = A.bind(obj, "B");
var b = new B('b');
// undefined B
console.log(obj.name);
// obj

要注意,=> 语法下的 this 不受影响,该语法下 this 视为 const 变量,不接受修改。

相关文章

  • JavaScript 函数中的外部变量——理解 this

    js 中的 this 指向确实是个坑,网上有人轰轰烈烈地讨论它,讨论 js 闭包,其实并没有那么玄学,让我们一点点...

  • js之闭包

    变量的作用域 在JavaScript中,函数内部可以读取全局变量。 但是,函数外部无法读取函数内部的局部变量。 这...

  • 函数(三)闭包和作用域

    本文对应《JavaScript忍者秘籍》第5章内容。 1.理解闭包 闭包允许函数访问并操作函数外部的变量,只要变量...

  • JavaScript闭包

    预备知识: 变量 全局变量 局部变量Javascript语言特点:函数内部可以读取外部的全局变量函数外部无法读取内...

  • Javascript学习笔记——8.5 做为命名空间的函数

    Javascript中的作用域: 函数作用域:在函数中声明的变量只在函数体内部可见,包括嵌套函数内,在函数外部不可...

  • 前端面试题

    1、闭包的理解 函数内部可以读取函数外部的全局变量;在函数外部无法读取函数内的局部变量。 函数内部声明变量的时候...

  • 什么是闭包,有哪些应用场景

    在javascript中,内部函数总是可以访问其所在外部函数中,声明的参数和变量,即使在其被返回(寿命终结)。闭包...

  • 我在闭包在node.js里的应用与思考

    闭包的定义 学术来说,闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量...

  • JS | Javascript中函数的作用域

    Javascript中变量有两种作用域: 1.全局变量:声明在函数外部的变量(所有没有var直接赋值的变量都属于全...

  • JavaScript(一)

    Javascript 输出 引入外部文件 更改html中的内容 获取其值 声明变量使用var 声明函数functi...

网友评论

本文标题:JavaScript 函数中的外部变量——理解 this

本文链接:https://www.haomeiwen.com/subject/zdmcvttx.html