javascript中的this
栏目: JavaScript · 发布时间: 5年前
内容简介:一般说到JS的this,都会想起在函数中变来变去的this。但是事情的发生都是有规则的约束,JS中的this也不例外,下面我们来看一下JS中this指向的规则-> 对于一个函数,如果没有被打点调用,则该函数里的this指向全局(window)其实这也是符合默认指向这个规则的,在这个小例子中,
一般说到JS的this,都会想起在函数中变来变去的this。但是事情的发生都是有规则的约束,JS中的this也不例外,下面我们来看一下JS中this指向的规则
在JS中,有四条规则影响着this的指向
- 默认指向:对于一个函数,如果没有被打点调用,则该函数里的this指向全局(window)
- 隐式指向:对于一个函数,如果被打点调用,则该函数里的this指向调用该函数的对象
- 显式指向:通过call,apply,bind改变函数里的this指向
- 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(); 复制代码运行的结果为
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(); 复制代码运行的结果为
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(); 复制代码
运行结果是 my age is: 100
由 obj.showAge()
可以看出,函数 showAge
被 obj
这个对象调用,所以 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(); 复制代码
运行结果是 my age is: 100
首先,执行 obj2.showAge = obj1.showAge
,把 obj1
的 showAge
函数的引用给 obj2
的 showAge
,所以 obj2
的 showAge
的执行内容就变成了 obj1
的 showAge
内容 然后执行 obj2.showAge()
, shoeAge
的 this
指向 obj2
,所以打印的是 obj2
的age,为100。
三、显示指向
-> 通过call,apply,bind改变函数里的this指向
先简单说一下 call
apply
bind
的作用和使用方法 call
apply
都是改变一个函数的this指向并执行该函数,区别是传参列表不同, call(obj, 参数1, 参数2, ...)
apply(obj, arguments)
bind
是改变一个函数的this指向并返回一个新的函数,传参结合 call
和 apply
的,只接收第一次绑定的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相同 复制代码
运行结果是: my age is: 18
首先,执行语句从左往右看,调用 obj
对象中的 showAge
方法,然后使用 call
方法改变 showAge
方法中的 this
为 newObj
对象。所以, 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; } 复制代码
运行结果依旧为 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 (); 复制代码
运行结果为 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
- 理解 JavaScript 中的 this
- JavaScript中的this详解
- javascript中的this继续聊
- 深入理解JavaScript中的this
- JavaScript 中的 this – JavaScript 完全手册(2018版)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!