前言

  关于this的指向问题是前端面试中常考的知识点,也是我们开发学习中较难理解的问题。作为JavaScript的基础,需要我们彻底理解这一关键词。this作为JavaScript中非常复复杂的机制,值得我们付出更大的代价来学习理解。这里分享一下我的学习笔记。

正文

  1.this是什么?this指向什么?

  学习this之前首先要知道this永远指向一个对象,this就是函数运行时候所在的环境,我们在学习执行上下文的时候提到,每一个执行上下文都包含三个重要对象,分别是变量对象、作用域链和 this,JavaScript支持运行环境动态切换,也就是说,this的指向是动态的,并不是固定的指向一个对象,this的指向完全取绝于函数调用的位置。接下来我们看下下面的代码:

        var a=1
function foo(){
var a=3
console.log(a)
}
foo()//3

  上面的代码执行用到函数的作用域问题,foo()函数执行的时候,函数内部定义一个变量a等于3,然后输出打印a,此时foo上下文中存在a变量,因此输出3,若函数foo()内部没有定义a这个变量,在执行打印这句代码的时候,函数内部找不到该变量,此时会沿着作用域链向上次查找,即全局作用域,此时打印结果便为1。

        var a=1
function foo(){
var a=3
console.log(this.a)
}
foo()//1

  上面的代码执行结果会打印出1,对比第一段代码你会发现就这里多了this这一个关键词,为什么加了this之后输出结果会发生改变呢?莫非此时this指向window全局对象?

        var a=1
function foo(){
var a=3
console.log(this.a)
}
var obj={a:100,foo:foo}
obj.foo()//100

  再来看下上面的代码,打印结果变为100,对比前面两段代码,foo函数增加了obj对象的一层引用,输出的this.a结果再次发生改变?难道此时this指向obj对象?

    this的指向为什么会发生改变,this的指向到底什么时候发生的?是因为函数在JavaScript中既可以当作值传递和返回,也可以当作对象和构造函数。所有函数在运行的时候才能确定其当前的运行环境,所以this就产生了,this会根据函数的运行环境的改变而改变,同时,函数中的this也只能在函数运行时最终确定其运行环境。

  2.this不指向它自身。

    function foo(num) {
console.log( "foo: " + num ); // 记录 foo 被调用的次数
this.count++;
}
foo.count = 0;
for (var i=0; i<5; i++) {
foo( i );
}
console.log("foo.count:"+foo.count)
//foo: 0
//foo: 1
//foo: 2
//foo: 3
//foo: 4
//foo.count:0

  上面的代码打印输出foo:01234,说明foo函数被调用了五次,然后打印foo.count输出还是0,说明this.count中的this并不是指向的函数foo本身,foo.count只是函数foo的一个属性,this无法指向他本身。我们可以通过arguments.callee来引用当前正在执行的函数对象,即对象本身。

  3.this不指向它的作用域。

  另一种常见的误解就是this指向函数的作用域。我们需要牢记,this在任何情况下都不指向函数的词法作用域,this的指向完全取决于函数调用的位置。因此不能使用this来引用一个函数词法作用域内部的东西。

  4.this的绑定规则。

  this是什么?当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的 其中一个属性,会在函数执行的过程中用到。
        如何寻找函数的调用位置,从而判断函数在执行过程中会如何绑定 this?首先找到调用位置:使用开发者工具得到调用栈,然后找到栈中第二个元素,这就是真正的调用位置。

  通过函数调用的位置来确定函数绑定的this对象,需要遵守以下四条规则:

  (1)默认绑定

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

  上面的代码中,foo()函数直接调用,不带任何修饰的函数引用进行调用,因此采用默认绑定,此时this指向window对象,无法应用其他规则。在非 strict mode 下时,默认绑定才能绑定到全局对象,严格模式下 this 绑定在undefined。

  (2)隐式绑定--需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。

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

  上面的代码中,foo函数被调用时 obj 对象 “ 拥有 ” 或者 “ 包含 ” 它。当函数引 用有上下文对象时,隐式绑定规则会把函数调用中的  this绑定到这个上下文对象,因为调 用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的。

            function foo() {
console.log(this.a);
}
var obj2 = {
a: 2,
fn: foo
};
var obj1 = {
a: 1,
o1: obj2
};
obj1.o1.fn();//2

  上面的代码需要值得注意的是:对象属性引用链中只有最顶层或者说最后一层会影响调用位置。

              function foo() {
console.log( this.a );
}
var obj = { a: "local", foo: foo };
var bar = obj.foo; // 函数别名!
var a = " global"; // a 是全局对象的属性
bar(); // "global"

  上面的代码,虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的 bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

          function foo(){
console.log(this.a)
}
var a="global"
var obj={a:"local",foo:foo}
var bar=obj.foo
function doFnc(fn){
fn()
}
doFnc(bar)//global

  上面的代码需要注意的是:被隐式绑定的函数会丢失绑定对象。

  (3)显示绑定或者硬绑定--call,apply,bind直接指定this的绑定对象 

  call() apply() bind()直接指定 this 的绑定对象,返回一个硬编码的新函数,它会把参数设置为 this 的上下文并调用原始函数。但是如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值 在调用时会被忽略,实际应用的是默认绑定规则。foo.call( obj );以在调用 foo 时强制把它的 this 绑定到 obj 上。

  (4)new绑定

  JavaScript 中 new 的机制实 际上和面向类的语言完全不同。

  在 JavaScript 中,构造函数只是一些 使用 new 操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上, 它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已。包括内置对象函数(比如 Number(..))在内的所有函数都可 以用 new 来调用,这种函数调用被称为构造函数调用。

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

1. 创建(或者说构造)一个全新的对象。

2. 这个新对象会被执行 [[ 原型 ]] 连接。

3. 这个新对象会绑定到函数调用的 this。

4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象

new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上。

  5.绑定的优先和一些特殊情况。

  判断优先级:
            (1)函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。 var bar = new foo() 
            (2)函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是 指定的对象。 var bar = foo.call(obj2) 
            (3)函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上 下文对象。 var bar = obj1.foo() 
            (4)如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到 全局对象。 var bar = foo()
  特殊情况:

    (1)间接绑定

         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 的返回值是目标函数的引用

    对比隐式绑定

          function foo() { console.log( this.a ); }
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 ,fo:o.foo};
p.fo() //4 对比间接引用

    (2)事件绑定中的this

          1.行内绑定 <input type='button' onclick="this">
<input onclick="clickFuc"> function(){ this ...}
//this指向window对象
2.事件监听与动态绑定
<input type="button" value="按钮" id="btn">
<script> var btn = document.getElementById('btn');
btn.onclick = function(){
this ; // this指向本节点对象
}</script>
//动态绑定的事件本身就是对应节点的一个属性,重新赋值为一个匿名函数,this自然就指向了本节点对象

    (3)箭头函数

    ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这其实和 ES6 之前代码中的 self = this 机制一样。

总结

  以上就是本文的全部内容,希望给读者带来些许的帮助和进步,方便的话点个关注,小白的成长之路会持续更新一些工作中常见的问题和技术点。
 

js--this指向的相关问题的更多相关文章

  1. js 时间函数 及相关运算大全

    js 时间函数 及相关运算大全 var myDate = new Date(); myDate.getYear();        //获取当前年份(2位) myDate.getFullYear(); ...

  2. js中scroll滚动相关

    js中scroll滚动相关 scroll,滚动,一般讨论的是网页整体与浏览器之间的关系. 一.元素相关 属性/方法 解释 element.scrollHeight 返回元素的整体高度. element ...

  3. JS中this指向问题相关知识点及解析

    概括:this指向在函数定义的时候是无法确定的,只有在函数调用执行的时候才能确定this最终指向了谁,this最终指向的是调用它的对象(常见的说法,后面有小小的纠正): 例1: 图中的函数fn1其实是 ...

  4. [学习笔记]JS 数组Array push相关问题

    前言: 今天用写了一个二维数组,都赋值为零,然后更新其中一个值,结果和预期是不一样,会整列的相同位置都是同一个值. 1.用Chrome的控制台样例如下: arrs[2][2] =1的赋值,竟然是三个数 ...

  5. 【本周面试题】第2周 - js单线程和异步相关问题

    硬性知识点考察: 为什么js是单线程的? 因为js设计最初是为了操作dom而生,如果是多线程的,当多个线程同时修改一个dom时就会产生冲突,所以设计成单线程,一次只能做一件事. 既然是单线程为什么要有 ...

  6. 白话js this指向问题

    前言   通过本文,你大概能了解this基础指向的问题,抛开例子去说this太虚幻,这里还是结合几篇博文做个整理,算是个人的记录了. 先说概念,this指向与申明无关,永远指向距离自己最近的最终调用者 ...

  7. vue.js的package.json相关问题解惑

    使用vue-cli创建vue.webpack项目,在项目中用到了iSlider移动端滑动插件,只在本地命令工具中npm install islider.js:提交之后,partner下载代码后一直运行 ...

  8. 面试题-JS中的作用域相关问题

    对象类型: 原始数据类型存储的是值,而对象类型存储的是地址(指针).下面的这个例子就比较有意思了. 先看题: function test(person) { person.age = 26 perso ...

  9. 彻底搞懂js this指向问题

    在这里必须要提一句的是,this指向是学习js必须要掌握的(必须),再开始之前先看底部的总结,然后回上面看例子便一目了然. 例子1: function a(){ var user = "Ta ...

  10. JS this指向

    正常模式 在正常模式下独立函数的的 this 指向 undefined 或 window. <script type="text/javascript"> functi ...

随机推荐

  1. Long Long Message POJ - 2774 后缀数组

    The little cat is majoring in physics in the capital of Byterland. A piece of sad news comes to him ...

  2. c#记两个变量进行值交换

    今天腊月二十九啦,无心上班,专注划水.然后就在那里翻帖子消磨时光. 看到了这样一个问题,有人提问为什么   a=b+(b=a)*0  ??? 第一眼看上去,我也有点蒙,仔细推敲了一下,嗯~的确是交换了 ...

  3. k8s-3-容器云监控系统

    apollo小结 课程目录 一.容器云监控prometheus概述 https://prometheus.io/docs/introduction/overview/ #官方文档 https://gi ...

  4. docker理论题-02

    1.什么是namespace? 答:名称空间,作用隔离容器 2.namespace隔离有那些? 答:ipc:共享内存.消息队列 mnt:挂载点 net:网络栈 uts:域,主机名 user:用户,组 ...

  5. element ui 渲染超过上百条数据时页面卡顿,更流畅的加载大量数据

    问题:element ui table渲染上百条数据,页面渲染开始出现延时 解决方案:使用pl-table 注意:设置use-virtual并给定table高度

  6. json-server All In One

    json-server All In One https://github.com/typicode/json-server#getting-started $ npm i -g json-serve ...

  7. console.log & front-end jobs

    console.log & front-end jobs bind & function let log = console.log; let obj = {}; log(obj); ...

  8. google advanced search operators

    google advanced search operators https://www.google.com/advanced_search js es6 site:xgqfrms.xyz http ...

  9. svg 矩阵转换

    svg 矩阵转换 https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix https://develope ...

  10. apollo-server 返回模拟数据

    模式模拟GraphQL数据 const { ApolloServer, gql } = require('apollo-server'); const typeDefs = gql` type Que ...