javascript中的this

栏目: JavaScript · 发布时间: 5年前

内容简介:一般说到JS的this,都会想起在函数中变来变去的this。但是事情的发生都是有规则的约束,JS中的this也不例外,下面我们来看一下JS中this指向的规则-> 对于一个函数,如果没有被打点调用,则该函数里的this指向全局(window)其实这也是符合默认指向这个规则的,在这个小例子中,

一般说到JS的this,都会想起在函数中变来变去的this。但是事情的发生都是有规则的约束,JS中的this也不例外,下面我们来看一下JS中this指向的规则

在JS中,有四条规则影响着this的指向

  1. 默认指向:对于一个函数,如果没有被打点调用,则该函数里的this指向全局(window)
  2. 隐式指向:对于一个函数,如果被打点调用,则该函数里的this指向调用该函数的对象
  3. 显式指向:通过call,apply,bind改变函数里的this指向
  4. new一个对象:构造函数的this指向该实例化对象

优先级为: 4 > 3 > 2 > 1

一 、默认指向

-> 对于一个函数,如果没有被打点调用,则该函数里的this指向全局(window)

1)先看一个30km/h的小例子

var age = 18;
function showAge () {
  var age = 100;
  console.log('my age is: ' + this.age);
}
showAge();
复制代码
javascript中的this
运行的结果为 my age is : 18

其实这也是符合默认指向这个规则的,在这个小例子中, showAge 函数在执行的时候并没有被打点调用 ×××.showAge() ,所以 showAge 函数里面的 this 指向全局 window ,即 this.age 就是全局的 age 为18。

可能有人会有疑问,为什么不是打印100呢。首先要清楚的一点是,在这个小例子中,打印的是 this.age 而不是 age ,所以要到 this 指向的对象上找 age ,而不是单纯地去找 age 这个变量,如果改成 console.log('my age is: ' + age) ,打印的就是100了。

2)再来一个60km/h的小例子

var age = 18;
var obj = {
    age : 100,
    showAge : function () {
      setTimeout(function () {
        console.log('my age is: ' + this.age);        
      }, 300);
      return '请稍等300毫秒';
    }
}
obj.showAge();
复制代码
javascript中的this
运行的结果为 my age is : 18

嗯???为什么还是18???其实把函数执行的位置捋清楚也是符合规则的。 首先, showAge 函数定义在一个对象中,里面有个定时器 setTimeout ,定时器等待300毫秒后执行打印操作,这应该没问题。 然后,在外面执行 obj.showAge() ,打点调用 obj 里的 showAge 方法,所以 showAge 里的 this 指向 obj ,但是,我们要操作的语句是在定时器 setTimeout 里面的函数里的,所以我们可以粗略地模拟一下这个函数

function setTimeout (callback, delay) {
    ...
    if (...) {
        callback();
    }
    ...
}
复制代码

好了,大概就是这样子~~ 我们可以看出,在定时器中,作为回调函数的 callback 只是单纯地在符合条件后执行了,那么,这里的 callback 函数是不是没有被谁打点调用,所以该函数的 this 应该指向全局对象 window 。所以打印的就是全局的 age ,等于18。

同时也可以得到一个普遍的结论:一般的回调函数的this指向全局对象 window

二、隐式指向

-> 对于一个函数,如果被打点调用,则该函数里的this指向调用该函数的对象

1)先看一个30kn/h的例子

var age = 18;
var obj = {
    age : 100,
    showAge : function () {
      console.log('my age is: ' + this.age);
    }
}
obj.showAge();
复制代码
javascript中的this

运行结果是 my age is: 100

obj.showAge() 可以看出,函数 showAgeobj 这个对象调用,所以 showAge 函数里面的 this 指向 obj 这个对象 所以你也可以把 showAge 函数的执行语句理解为 console.log('my age is: ' + obj.age) ,所以打印的就是对象 obj 上的 age ,为100。

2)再来一个60km/h的例子

var obj1 = {
    age : 18,
    showAge : function () {
      console.log('my age is: ' + this.age);
    }
}
var obj2 = {
    age : 100,
    showAge : function () {}
}
obj2.showAge = obj1.showAge;
obj2.showAge();
复制代码
javascript中的this

运行结果是 my age is: 100

首先,执行 obj2.showAge = obj1.showAge ,把 obj1showAge 函数的引用给 obj2showAge ,所以 obj2showAge 的执行内容就变成了 obj1showAge 内容 然后执行 obj2.showAge()shoeAgethis 指向 obj2 ,所以打印的是 obj2 的age,为100。

三、显示指向

-> 通过call,apply,bind改变函数里的this指向

先简单说一下 call apply bind 的作用和使用方法 call apply 都是改变一个函数的this指向并执行该函数,区别是传参列表不同, call(obj, 参数1, 参数2, ...) apply(obj, arguments) bind 是改变一个函数的this指向并返回一个新的函数,传参结合 callapply 的,只接收第一次绑定的this

1)先来一个30km/h的例子

var obj = {
    age : 100,
    showAge : function () {
      console.log('my age is: ' + this.age);
    }
}
var newObj = {
    age : 18
}
obj.showAge.call(newObj);  //apply效果一样

//var func = obj.showAge.bind(newObj);  //拿个变量接收bind方法执行后返回的新函数
//func();  //结果和call apply相同

复制代码
javascript中的this

运行结果是: my age is: 18

首先,执行语句从左往右看,调用 obj 对象中的 showAge 方法,然后使用 call 方法改变 showAge 方法中的 thisnewObj 对象。所以, showAge 函数里的执行语句可以理解为 console.log('my age is: ' + newObj.age) ,所以打印的是 newObj 里的 age ,为18。

2)剖析一下call改变this的原理,车速100km/h

虽然知道 call ( apply bind 不做解释,有兴趣的可以看完 call 的原理后自己尝试还原)能改变一个函数this的指向,那么它里面是怎样操作然后改变一个函数的this指向的呢?

首先,在隐式指向中我们知道,对于一个函数,如果被打点调用,则该函数里的this指向调用该函数的对象,那么我们就可以利用这点来模拟一下 call 方法

在原型链上编写,模拟就要真实一点~~

Function.prototype.myCall = function (context) {
    var ctx = null;
    //保证不污染原对象
    if (context) {
        ctx = JSON.parse(JSON.stringify(context));
    }else {
        ctx = window;
    }
    
    var arg = arguments,  //保存函数的实参列表
        len = arg.length,  
        args = [];  

    ctx.fn = this; 
    for (var i = 1; i < len; i++) {
        //从实参列表的第一位开始遍历,因为第零位为指向的上下文
        args.push('arg[' + i + ']');
    }
    //遍历完后 args = [ 'arg[1]', 'arg[2]', ...]

    var result = eval( 'ctx.fn(' + args.join(',') + ')' );
    //上述语句可理解为 eval('ctx.fn(arg[1], arg[2], ...)')

    delete ctx.fn;
    return result;
}
复制代码
javascript中的this
javascript中的this

运行结果依旧为 my age is: 18

首先,从结果看, myCall 函数没有问题,然后我们来分析一下函数里面到底主要干了些啥 (整体思路见代码注释)

①通过 ctx.fn = this 把调用 myCall 的函数变为目标上下文的一个方法,由此可以改变 fn 方法的 this 为目标上下文,也就是调用 myCall 的函数的 this 指向了目标上下文

②把调用 myCall 的函数的参数整合,通过 eval 方法把字符串解析为脚本并执行

好了,整体思路就是如此,这块不懂也没关系,我们主要讲的是改变this的指向~

四、通过new实例化一个对象(主要讲解this问题,构造函数知识不做过多概述)

-> 构造函数的this指向该实例化对象

1)先来一个30km/h的例子

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.introduceYourself = function () {
      console.log('my name is ' + this.name, 'and I\'m ' + this.age + ' years old.');
    }
}
var person = new Person('xuyede', 18);
person.introduceYourself ();
复制代码
javascript中的this

运行结果为 my name is xuyede and I'm 18 years old.

为了更好地让大家理解,我们可以把 new Person() 的过程分成4个步骤理解 (当然不是真的是这个过程,只是为了让大家能理解 new 的过程,稍后会揭晓真的过程~)

①在 Person 函数里面的第一行隐式生成一个对象 var this = {} ②让该 this 的原型指向该函数的原型 this.__proto__ = Person.prototype ③执行 this.xxx = xxx ④在函数最后一行把这个 this 返回出去

所以,你可以理解为变量 person 拿到了构造函数里面 this 的引用,所以 person 可以使用在构造函数里设置在 this 上的值

构造函数还涉及原型、原型链的知识,在这里就不过多阐述这方面的知识

2)剖析一下new这个东西到底干了什么,车速180km/h

话不多说,上代码~~~

function myNew() {
    var func = [].shift.call(arguments);  //把实参列表的第一位截取出来,获得构造函数
    var instance = Object.create(func.prototype);  //声明一个上下文,原型为构造函数的原型
    func.apply(instance, arguments);  //执行该构造函数,并改变构造函数的this为instance
    return instance;  //返回这个上下文
}
复制代码
javascript中的this

是的,你没看错,就只有四行代码,但是这四行代码理解起来可不简单,具体的请看代码的注释~~

最后的最后,希望你能从这篇文章中能学到东西就好~感谢阅读


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Code Reading

Code Reading

Diomidis Spinellis / Addison-Wesley Professional / 2003-06-06 / USD 64.99

This book is a unique and essential reference that focuses upon the reading and comprehension of existing software code. While code reading is an important task faced by the vast majority of students,......一起来看看 《Code Reading》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具