文档章节

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

傅易
 傅易
发布于 2016/12/05 00:53
字数 1127
阅读 260
收藏 5

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 变量,不接受修改。

© 著作权归作者所有

共有 人打赏支持
傅易
粉丝 25
博文 99
码字总数 57277
作品 0
海淀
后端工程师
【旧文新读】解释“闭包”需要几行代码?

新读(2017年10月19日) 本文写于 ,今天做了一点修改,所谓修改,其实只是删去了几句不影响技术内容的话。关于闭包,我最近写了一篇新的文章,提到了静态作用域,相比本文,是对闭包的更深一...

天方夜
07/04
0
0
JavaScript模块化开发一瞥

对于那些正在构建大型应用程序,而对JavaScript不甚了解的开发者而言,他们最初必须要面对的挑战之一就是如何着手组织代码。起初只要在标记之间嵌入几百行代码就能跑起来,不过很快代码就会变...

偶是小娃
2014/02/25
0
0
翻译 - JavaScript中的作用域与变量声明提升

本文地址:http://blog.163.com/jinluhz/blog/static/113830152201131132035178/ 原文地址:http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting 原文作者:ben cherry ......

蜗牛奔跑
2015/06/19
0
0
我所认识的JavaScript的闭包特性

闭包是一个比较抽象的概念,尤其是对js新手来说。在这里,我就我个人的理解j简单谈一下: 闭包:官方解释是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量...

longteng2013
2013/08/25
0
0
JavaScript的this和闭包

作为OO语言,c++、c#或者Java等语言,都有this的概念,JavaScript中存在this的概念,一般编程语言的this就是对象自己,而JavaScript的this却不一定! 在JavaScript中,this所引用的对象(很多...

林爵同学
2012/09/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

(三)Nginx配置·续

概述 前文写了关于Nginx环境配置,但是还没有完,接下来将会继续讲三个相关的配置 主要是以下三个 1.Nginx访问日志 2.Nginx日志切割 3.静态文件不记录日志和过期时间 Nginx访问日志 1.先看看...

杉下
今天
1
0
jquery创建类似于java的map

var map = {}; // Map map = new HashMap(); map[key] = value; // map.put(key, value); var value = map[key]; // Object value = map.get(key); var has = key in map; // boolean has = ......

SuperDabai
今天
0
0
java大数据转换16进制转10进制

public static void main(String[] args) {String hex = "0xdbf3accc683297cf0000";BigInteger amount = new BigInteger(hex.substring(2), 16);System.out.println(amount);......

任梁荣
昨天
2
0
OSChina 周六乱弹 —— 目测我们程序员丁克的几率不大

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @真Skr小机灵鬼儿:8.13分享Jocelyn Pook/Russian Red的单曲《Loving Strangers》 《Loving Strangers》- Jocelyn Pook/Russian Red 手机党少...

小小编辑
昨天
13
3
TypeScript基础入门 - 函数 - 剩余参数

转载 TypeScript基础入门 - 函数 - 剩余参数 项目实践仓库 https://github.com/durban89/typescript_demo.gittag: 1.2.1 为了保证后面的学习演示需要安装下ts-node,这样后面的每个操作都能...

durban
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部