摘要:调用在中,通过的形式调用一个构造函数,会创建这个构造函数实例,而这个实例的指向创建的这个实例。如下例所示,在构造函数内部使用并没有改变全局变量的值。显然,箭头函数是不能用来做构造函数。
关于javascript中this指向的问题,现总结如下,如有不正确,欢迎指正。
javascript中,this的指向并不是在函数定义的时候确定的,而是在其被调用的时候确定的。也就是说,函数的调用方式决定了this指向。记住:this 就是一个指针,指向我们调用函数的对象。
在此将javascript中this的调用方式分为以下几种:
1、直接调用:直接调用是指通过 funName(..) 这种方式调用。此时,函数内部的this指向全局变量。
function foo() { console.log(this === global); } foo(); //true
注意:直接调用并不是仅仅指在全局作用域下进行调用,而是说在任何作用域下通过funName(..) 这种方式调用。如下两个例子亦是属于直接调用方式:
a、使用bind函数改变外层函数的作用域,然后在内层直接调用,其this指向依然是全局变量:
function foo() { console.log(this === global); } function foo1() { foo(); }; var foo2 = foo1.bind({}); //改变foo1函数的作用域 foo2(); //true 依然指向全局变量
b、函数表达式,将一个函数赋值给一个变量,然后通过直接调用的调用方式调用此函数,其this指向依然是全局变量:
var obj = { foo: function(){ console.log(this === obj); //false console.log(this === global);//true } } var f = obj.foo; f();2、方法调用:
方法调用是指通过对象来调用其方法函数,类似于obj.foo(..)的调用方式。此时,this指向调用该方法的对象,注意,是最终调用该方法的对象。
var obj = { foo1: function(){ console.log(this === obj); } } obj.foo2 = function() { console.log(this === obj); } function foo3() { console.log(this === obj); } obj.foo3 = foo3; obj.foo1(); // true obj.foo2(); // true obj.foo3(); // true3、new 调用:
在es5中,通过 new Constructor() 的形式调用一个构造函数,会创建这个构造函数实例,而这个实例的this指向创建的这个实例。如下例所示,在构造函数内部使用this.name并没有改变全局变量name的值。
var name = "全局"; function Person(name){ this.name = name; } var p = new Person("局部"); console.log(p.name); console.log(name);
上述三种情况是常见的调用方式,以下还有一些特殊的调用方式:如bind、call、apply以及es6中箭头函数。
4、bind函数对this的影响bind函数用于绑定this的指向,并且返回一个绑定函数,此函数绑定了this指向,注意:绑定函数的this指向将不可再次更改。
var obj = {}; function foo1() { console.log(this === obj); } foo1(); //false 此时属于上述直接调用的方式,因此其this指向global var foo2 = foo1.bind(obj); foo2(); //true 绑定函数的this指向其绑定的对象 /** * 注意:绑定函数的this指向不可更改 */ var foo3 = foo2.bind({"a": 1}); foo3(); //true foo2.apply({"a": 1}); //true foo2.call({"a": 1}); //true5、apply和call对this指向的影响
apply和call亦可以用于改变this指向,并且返回执行结果。关于bind、apply和call的区别以及实现可以参考我的博客:《bind、call、apply的区别与实现》,在此不做重复说明。需要注意的一点是:apply和call不可以改变绑定函数(使用bind返回的函数)的this指向。
var obj = {}; function foo1() { console.log(this === obj); } var foo2 = foo1.bind({"a": 1}); /** * 注意:此处并不是绑定函数,因此其返回值依然是ture,即apply改变其this指向。 */ foo1.apply(obj); //true /** * 注意:此处是绑定函数,因此不可再通过apply和call的形式改变其this指向。 */ foo2.apply(obj); //false6、es6箭头函数中的this
箭头函数没有自己的this绑定,其使用的this是其直接父级函数的this。也就是说,箭头函数内部的this是由其直接外层函数(方法)决定的,而外层函数中的this是由其调用方式决定的。
const obj = { foo: function() { const inner = () => { console.log(this === obj); }; inner(); }, far: function() { return () => { console.log(this === obj); } } } /** * inner()内的this是foo的this,其指向取决于foo的调用方式 */ obj.foo(); //true var foo1 = obj.foo; foo1(); //false 此时应该指向global const far1 = obj.far(); far1(); //true const far2 = obj.far; far2()(); //false 此时应该指向global6.1 箭头函数常见错误及其解决方案:
在对象上定义函数:
const test = { array: [1, 2, 3], sum: () => { console.log(this === window); // => true return this.array.reduce((result, item) => result + item); } }; test.sum(); // TypeError: Cannot read property "reduce" of undefined
原因就是,箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域。
对象方法内的this指向调用这个方法的对象,如果使用箭头函数,this和对象方法在调用的时候所处环境的this值一致。因为 test.sum()是在全局环境下进行调用,此时this指向全局。
解决方法也很简单,使用函数表达式或者方法简写(ES6 中已经支持)来定义方法,这样能确保 this 是在运行时是由包含它的上下文决定的。
const test = { array: [1, 2, 3], sum() { console.log(this === test); // => true return this.array.reduce((result, item) => result + item); } }; test.sum(); // 6
定义原型方法
在对象原型上定义函数也是遵循着一样的规则
function Person(name) { this.name = name; } Person.prototype.sayName = () => { console.log(this === window); // => true return this.name; }; const cat = new Person("Mew"); cat.sayName(); // => undefined
使用传统的函数表达式就能解决问题
function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this === Person); // => true return this.name; }; const cat = new Person("Mew"); cat.sayName(); // => Mew
定义事件回调函数
this是JS中非常强大的特点,他让函数可以根据其调用方式动态的改变上下文,然后箭头函数直接在声明时就绑定了this对象,所以不再是动态的。
在客户端,在DOM元素上绑定事件监听函数是非常普遍的行为,在DOM事件被触发时,回调函数中的this指向该DOM,但是,箭头函数在声明的时候就绑定了执行上下文,要动态改变上下文是不可能的,在需要动态上下文的时候它的弊端就凸显出来:
const button = document.getElementById("myButton"); button.addEventListener("click", () => { console.log(this === window); // => true this.innerHTML = "Clicked button"; });
因为这个回调的箭头函数是在全局上下文中被定义的,所以他的this是window。换句话说就是,箭头函数预定义的上下文是不能被修改的,这样 this.innerHTML 就等价于 window.innerHTML,而后者是没有任何意义的。
使用函数表达式就可以在运行时动态的改变 this:
const button = document.getElementById("myButton"); button.addEventListener("click", function() { console.log(this === button); // => true this.innerHTML = "Clicked button"; });
定义构造函数
如果使用箭头函数会报错。
显然,箭头函数是不能用来做构造函数。
const Message = (text) => { this.text = text; }; const helloMessage = new Message("Hello World!"); // Throws "TypeError: Message is not a constructor"
理论上来说也是不能这么做的,因为箭头函数在创建时this对象就绑定了,更不会指向对象实例。
箭头函数带来了很多便利。恰当的使用箭头函数可以让我们避免使用早期的.bind()函数或者需要固定上下文的地方并且让代码更加简洁。
箭头函数带来了很多便利。恰当的使用箭头函数可以让我们避免使用早期的.bind()函数或者需要固定上下文的地方并且让代码更加简洁。
以上是个人对JavaScript中this的理解,欢迎您的留言。
欢迎访问我的个人博客了解更多信息。
参考文献:
1、《JavaScript 的 this 指向问题深度解析》
2、《ES6使用箭头函数注意点》
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/98170.html
摘要:他的组成如下对的就是你关注的那个变量对象作用域链跟闭包相关由于是单线程的,一次只能发生一件事情,其他事情会放在指定上下文栈中排队。 闭包和this,是两个相当高频的考点,然而你有没有想过,实际上他们两个都跟同一个知识点相关? 有请我们的这篇文章的主角,执行上下文 执行上下文 执行上下文是什么 可以简单理解执行上下文是js代码执行的环境,当js执行一段可执行代码时,会创建对应的执行上下文...
摘要:而在构造函数中,返回了的实例对象。在中直接返回过的实例,这里的是的真正构造函数最后对外暴露入口时,将字符与对等起来。因此当我们直接使用创建一个对象时,实际上是创建了一个的实例,这里的正真构造函数是原型中的方法。 showImg(https://segmentfault.com/img/remote/1460000008749398); 早几年学习前端,大家都非常热衷于研究jQuery源...
摘要:但是有一个总的原则,那就是指的是,调用函数的那个对象。纯粹的函数调用这是函数的最通常用法,属于全局性调用,因此就代表全局对象此时的是作为对象方法的调用函数还可以作为某个对象的方法调用,这时就指这个上级对象。 函数this的用法 his是Javascript语言的一个关键字。 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。比如 function test(){ thi...
摘要:本文节选自设计模式就该这样学使用备忘录模式实现草稿箱功能大家都用过网页中的富文本编辑器,编辑器通常都会附带草稿箱撤销等操作。首先创建发起人角色编辑器类。 本文节选自《设计模式就该这样学》1 使用备忘录模式实现草稿箱功能大家都用过网页中的富文本编辑器,编辑器通常都会附带草稿箱、撤销等操作。下面用一段代码来实现一个...
阅读 970·2021-11-24 10:42
阅读 3479·2021-11-19 11:34
阅读 2609·2021-09-29 09:35
阅读 2534·2021-09-09 09:33
阅读 646·2021-07-26 23:38
阅读 2520·2019-08-30 10:48
阅读 1389·2019-08-28 18:07
阅读 425·2019-08-26 13:44