Javascript的this详解


Posted in Javascript onMarch 23, 2019

在理解javascript的this之前,首先先了解一下作用域。

作用域分为两种:

  1. 1、词法作用域:引擎在当前作用域或者嵌套的子作用域查找具有名称标识符的变量。(引擎如何查找和在哪查找。定义过程发生在代码书写阶段)
  2. 2、动态作用域:在运行时被动态确定的作用域。

词法作用域和动态作用域的区别是:词法作用域是在写代码或定义时确定的;动态作用域是在运行时确定的。

this的绑定规则

this是在调用时被绑定,取决于函数的调用位置。由此可以知道,一般情况下(非严格模式下),this都会根据函数调用(调用栈)的上下文来绑定对象。

一、默认绑定

默认绑定:默认绑定是指在非严格模式下,且没有使用别的绑定规则时,this根据函数调用(调用栈)的上下文来绑定对象(全局对象)。(严格模式下则绑定undefined)

举个栗子:

function foo() {
  console.log(this.a);
};
function bar() {
  var a = 3;
  foo();
}
var a = 2;
bar(); //调用栈在全局作用域,this绑定全局对象

运行结果为: 2

//加上"use strict"运行结果则会变成this is undefined

这里的函数调用时,使用了默认绑定,函数调用(调用栈)的上下文是全局作用域,因此this绑定了全局对象(global)。

eg2:

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

运行结果为: 2

 这里需要注意:对于默认绑定,决定this绑定对象的不是调用位置是否处于严格模式,而是函数体是否处于严格模式(函数体处于严格模式则this绑定undefined;否则this绑定全局对象)。另外:严格模式和非严格模式虽然有可能可以绑定,但是最好不混用。

间接引用一般也是会应用默认绑定规则。

function foo() {
  console.log(this.a);
};
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo();  //3
(p.foo = o.foo)(); //2

赋值表达式 p.foo = o.foo的返回值是直接引用目标函数foo。

二、隐式绑定

隐式绑定:由上下文对象调用,绑定到上下文对象。

举个栗子:

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

这段代码中,foo()被当做引用属性添加到obj对象中,obj调用这个引用属性函数时,会使用该引用属性上下文,this会被绑定到obj对象。(这个函数严格来说不属于obj对象,只是作为引用属性)。属于隐式绑定。

而下面foo()函数的直接执行,并不是obj对象引用,所以上下文对象是全局对象。故this绑定了undefined。属于默认绑定。

对象引用链中只有上一层或者说最后一层在调用位置中起作用。

注意:

1.隐式绑定的函数会丢失绑定对象。此时它会应用默认绑定,将this绑定到全局对象或者undefined上,取决于是否是严格模式。

eg:

function foo() {
  console.log(this.a);
};
var obj = {
  a: 2;
  foo: foo
}
var bar = obj.foo;
var a = 'biubiubiu';
bar();

运行结果:"biubiubiu"

解析:看似bar是obj.foo的一个引用,实际上bar是直接引用了函数foo,是一个单纯的函数调用,故实为默认绑定。

2.参数传递就是隐式赋值,因此传入函数时也会被隐式赋值。

eg:

function foo() {
  console.log(this.a);
};
var obj = {
  a: 2,
  foo: foo
};
function bar(fn) {
  fn();
};
var a = "biubiubiu";
bar(obj.foo);

运行结果: "biubiubiu"

解析:实际上参数也是隐式赋值,但是参数传入函数中,并在函数中执行。此时也是直接引用了函数foo,因此也是单纯的函数调用,采用了默认绑定。

3.把函数传入语言内置函数。(与上面情况基本相似,将自己声明函数改成语言内置函数)回调函数丢失this的情况比较常见,况且还有调用回调函数的函数可能还会修改this。

三、显式绑定

显式绑定:直接将this绑定到指定对象上。Javascript中绝大多数函数和自己所创建的函数都可以使用这两种显式绑定的方法。

1、.call()
2、.apply()

这两种绑定方法,第一个参数是this绑定的对象。(如果传入的参数是原始值(字符串类型、布尔类型、数字类型),这个原始值就会被转成对象形式(new String、new Boolean、new Number)这个称为:装箱)

举个栗子:

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

运行结果: 2

 然鹅,显示绑定并不能解决绑定丢失的问题。这个时候来了一位新朋友 -- 硬绑定(bind)。

3、.bind() (硬绑定是常见场景,故es5提供了该内置方法 Function.prototype.bind。)
bind()会返回一个新编码函数,把this绑定在指定参数上,并调用函数。

举个栗子:

function foo(e) {
  console.log(this.a + e);
  return this.a + e;
};
var obj = {
  a: 2
}
var bar = foo.bind(obj); //新编码函数
var b = bar(3); // 2 3
console.log(b); // 5

bind()还有一个功能:将除了第一个用于绑定this的参数之外的其他参数传给下层的函数(部分应用,是“柯里化”的一种)。

这里涉及到一个概念:把null或者undefined作为this的绑定对象传入call、apply、bind,这些值在调用的时候会被忽略,实际应用默认绑定规则。
应用场景:

  1. 1、使用apply()展开一个数组,并作为参数传递给一个函数。
  2. 2、bind()对参数进行柯里化(预先设置一些参数)。

举个栗子:

function foo(a,b) {
  console.log("a:" + a + ",b:" + b);
};
//数组“展开”成参数
foo.apply(null,[2,3]); //a:2,b:3
//bind()柯里化
var bar = foo.bind(null,2);
bar(3); //a:2,b:3

解析:传入一个参数作为this绑定对象,如果不传则使用占位符(null),此时会使用默认绑定规则。

上面这个例子可能会产生一定的副作用,如果需要运用这种场景并且更加安全。可以创建一个空对象(可以用任意喜欢的名字来命名)。

var ∅ = Object.create(null);
//上面这个例子就可以改写为:
foo.apply(∅,[2,3]); //a:2,b:3
var bar = foo.bind(∅,2);
bar(3); //a:2,b:3

注意:硬绑定之后不能使用隐式绑定和显式绑定对this进行修改
在这里介绍一种软绑定的方法softBind(),检查this绑定到全局对象或者undefined后,绑定this到指定的默认对象。绑定后效果和硬绑定一样,但是保留隐式绑定或者显式绑定修改this的能力。

四、new绑定

Javascript中的new机制与面向类语言的完全不同。在Javascript中,构造函数只是一些使用new操作符时被调用的函数,不属于一个类,也不会实例化一个类。称为对函数的“构造调用”。

举个栗子:

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

使用new的过程会创建一个全新的对象,this会绑定这个新对象。如果函数没有返回其他对象,则new表达式函数调用会返回该新对象。(这个新对象会连接prototype)

四种绑定规则的优先级为:new>显式>隐式>默认

箭头函数

箭头函数是根据外层作用域(函数或全局)来决定this。(词法作用域取代this机制)
箭头函数this会绑定调用时的对象,且箭头函数的绑定无法修改(new也不行)。

其实可以理解为,箭头函数的this在词法上继承的是它所在的作用域(函数或全局)的this,而它继承的函数作用域的this绑定的是在该函数调用上下文对象,所以箭头函数的this间接的绑定在调用上下文对象。

简述: 箭头函数this(绑定作用域this)-- 作用域this(绑定在调用上下文对象)。

故:箭头函数this == 调用的上下文对象

举个栗子:

function foo() {
  setTimeout(function() {
    //这里的this在词法上继承自foo()
    console.log(this.a);
  },100);
};
var obj = { a: 2 };
foo.call(obj); //2

其实这个栗子也等价于:

function foo() {
  var that = this; //lexical capture of this
  setTimeout(function() {
    console.log(self.a)
  },100);
}
...与上面一样

所以,有两种风格:this风格(四种规则)和词法作用域风格(that = this和箭头函数)可供使用。使用时尽量避免混用,否则会造成难以维护的后果。

以上所述是小编给大家介绍的Javascript的this的作用详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
一些易混淆且不常用的属性,希望有用
Jan 29 Javascript
Extjs入门之动态加载树代码
Apr 09 Javascript
node.js中的url.format方法使用说明
Dec 10 Javascript
asp.net中oracle 存储过程(图文)
Aug 12 Javascript
Bootstrap轮播加上css3动画,炫酷到底!
Dec 22 Javascript
jquery实现折叠菜单效果【推荐】
Mar 08 Javascript
Vue.js实现一个SPA登录页面的过程【推荐】
Apr 29 Javascript
利用prop-types第三方库对组件的props中的变量进行类型检测
May 02 Javascript
vue实现图片滚动的示例代码(类似走马灯效果)
Mar 03 Javascript
vue实现底部菜单功能
Jul 24 Javascript
浅谈vue 二级路由嵌套和二级路由高亮问题
Aug 06 Javascript
HTML+JS实现在线朗读器
Feb 15 Javascript
如何在Angular应用中创建包含组件方法示例
Mar 23 #Javascript
vue中组件的3种使用方式详解
Mar 23 #Javascript
ES6入门教程之Array.from()方法
Mar 23 #Javascript
setTimeout与setInterval的区别浅析
Mar 23 #Javascript
如何通过setTimeout理解JS运行机制详解
Mar 23 #Javascript
vue中axios请求的封装实例代码
Mar 23 #Javascript
vueScroll实现移动端下拉刷新、上拉加载
Mar 22 #Javascript
You might like
PHP关联数组的10个操作技巧
2013/01/21 PHP
php中利用explode函数分割字符串到数组
2014/02/08 PHP
php遍历、读取文件夹中图片并分页显示图片的方法
2016/11/15 PHP
一个用javascript写的select支持上下键、首字母筛选以及回车取值的功能
2009/09/09 Javascript
javascript学习笔记(十八) 获得页面中的元素代码
2012/06/20 Javascript
Javascript获取HTML静态页面参数传递值示例
2013/08/18 Javascript
jquery无刷新验证邮箱地址实现实例
2014/02/19 Javascript
JavaScript数据结构和算法之二叉树详解
2015/02/11 Javascript
jQuery基本选择器(实例及表单域value的获取方法)
2016/05/20 Javascript
ionic js 模型 $ionicModal 可以遮住用户主界面的内容框
2016/06/06 Javascript
关于JavaScript中的this指向问题总结篇
2017/07/23 Javascript
angular6.0开发教程之如何安装angular6.0框架
2018/06/29 Javascript
使用 electron 实现类似新版 QQ 的登录界面效果(阴影、背景动画、窗体3D翻转)
2018/10/23 Javascript
你不知道的Vue技巧之--开发一个可以通过方法调用的组件(推荐)
2019/04/15 Javascript
vue实现配置全局访问路径头(axios)
2019/11/01 Javascript
详解node.js创建一个web服务器(Server)的详细步骤
2021/01/15 Javascript
[01:03:36]DOTA2-DPC中国联赛 正赛 VG vs Magma BO3 第二场 1月26日
2021/03/11 DOTA
Python字符串处理实例详解
2017/05/18 Python
基于python实现在excel中读取与生成随机数写入excel中
2018/01/04 Python
python 实现对文件夹内的文件排序编号
2018/04/12 Python
keras的backend 设置 tensorflow,theano操作
2020/06/30 Python
细说CSS3中的选择符
2008/10/17 HTML / CSS
林清轩官方网站:山茶花润肤油开创者
2016/10/26 全球购物
垃圾回收的优点和原理
2014/05/16 面试题
酒店员工检讨书
2014/02/18 职场文书
婚纱摄影师求职信范文
2014/04/17 职场文书
国际商贸专业自荐信
2014/06/09 职场文书
音乐兴趣小组活动总结
2014/07/07 职场文书
生物学专业求职信
2014/07/23 职场文书
品牌转让协议书
2014/08/20 职场文书
党的群众路线剖析材料
2014/10/09 职场文书
买房子个人收入证明
2014/10/12 职场文书
企业文化学习心得体会
2016/01/21 职场文书
互联网的下一个风口:新的独角兽将诞生
2019/08/02 职场文书
go语言求任意类型切片的长度操作
2021/04/26 Golang
python实现学生信息管理系统(面向对象)
2022/06/05 Python