前端学习笔记之this——懂不懂由你,反正我是懂了
this
this
对于初学 JS 的新人来说,是一道坎,看到this
就晕头晕脑,脑容量一下不够用了。
先来说下判断this
结论
-
this
是函数调用时call
的第一个参数 -
this
只有在函数执行的时候才能被确定,实际指向调用它那个对象(上一级)
先来看下函数常用的几种调用方式
fn(a,b)
fn.name(a,b)
var f = fn.name; f(a,b)
fn.call(undefined,a,b)
fn.apply(undefined,[a,b])
上面三种调用方法对于新人很熟悉,而对后面两种方法比较陌生,自然也就弄不明白this
含义了。
改写下函数的调用方式就会发现这两种是等价的,回到上面说的结论,call
的第一个参数是this
,a
、b
才是传进去的参数。
fn(a,b) === fn.call(undefined,a,b) === window.fn.apply(window,[a,b])
明白了这点,大部分的this
就不难判断了,新人在判断this
时只需改写函数调用方式就能知道this
指向了。
this例子
下面方法中暂且不讨论严格模式,this
的指向
例子1:
var user = 'window'
function fn(){
var user = 'object';
console.log(this.user); //windwow
}
fn(); //改写 window.fn.call(window)
fn
是全局对象window
的方法
例子2:
var user = 'window'
var obj = {
user:'object',
fn:function(){
console.log(this.user); //object
}
}
obj.fn(); //改写 obj.fn.call(obj)
这里的fn
是被obj
调用的,所以fn
内部的this
指向obj
内部的user
。
你可能会有疑惑了,这里是不是可以改写成window.obj.fn.call(window)
,往下看
例子3:
var user = 'window'
var obj = {
user:'object',
fn:function(){
console.log(this.user); //object
}
}
window.obj.fn(); //改写 window.obj.fn.call(window.obj)
先看下面例子
例子4:
var user = 'window'
var obj = {
user:'object',
fn1:{
user:'function',
fn2:function(){
console.log(this.user) //function
}
}
}
obj.fn1.fn2(); //改写 obj.fn1.call(obj.fn1)
例子3中正常情况下都是把window给省略的,这里写出来是为了和例子4做对比。
假设例子4中obj
对象是不是window
下的方法,而是和window
平级(实际不存在这种假设,容易理解)。
这里是链式调用,this
的最终指向是调用它的上一层对象fn1
,但是不能改写成obj.fn1.call(fn1)
,只有window
可以省略。
例子5:
var user = 'window'
var obj = {
user:'object',
fn1:{
user:'function',
fn2:function(){
console.log(this.user) //window
}
}
}
var a = obj.fn1.fn2;
a() //改写 window.a.call(window)
先看下面例子
例子6:
var a = {user:'window'}
var obj = {
user:'object',
fn1:{
user:'function',
fn2:function(){
console.log(this.user) //window
}
}
}
obj.fn1.fn2.call(a);
看到例子5和例子6是不是已经晕了。
例子5中,var a = obj.fn1.fn2
只是赋值,并没用执行,而真正执行的时候,a
是window
下的方法,所以fn2
内部的this
指向window
下的user
。
例子6中,call
的第一个参数是a
,那this
肯定指向a
的user
,因为显示绑定优先级高于隐示绑定。
那你可能要问什么是显示绑定,什么是隐示绑定呢?
obj.fn() //隐示绑定
obj.fn.call(obj) //显示绑定
也就是说你改写后的调用就是显示绑定。
new构造函数
例子7:
function Fn(){
this.user = 'object';
}
var a = new Fn();
console.log(a.user); //object
例子7中,因为构造函数的this
指向构造出来的实例,所以a
是用new
构造出来的对象,那么this
就是指向a
。
构造函数中要注意的一点是:默认情况下,构造函数是没有return
,但是非要加一个return
的话。
- 如果
return
的是引用类型值,那么构造函数的this
就指向return
的对象 - 如果
return
的是基本类型值,那么构造函数的this
就指向构造出来的实例化函数
this
基本应用就是这些,做一些实操练习巩固一下
this实操
实操1:
function X(){
return object = {
name:'object',
f1(x){
x.f2() //② 改写 options.f2.call(options)
},
f2(){
console.log(this)
}
}
}
var options = {
name:'options',
f1(){},
f2(){
console.log(this) //③ 运行这个this,打印 options 函数
}
}
var x = X();
x.f1(options); //① 改写 object.f1.call(object,options)
看到this
就马上改写,不改写就做,肯定错。
分析
- 首先把
x.f1(options)
改写object.f1.call(object,options)
-
x.f1(options)
调用object
对象内部f1
方法,把options
函数作为参数传入,所以object
内部f1(x)
中的x
是options
函数 -
f2
内部的this
指向options
实操2:
function X(){
return object = {
name:'object',
f1(x){
x.f2.call(this) //② 这里是显示绑定,改写 options.f2.call(object)
},
f2(){
console.log(this)
}
}
}
var options = {
name:'options',
f1(){},
f2(){
console.log(this) //③ 运行这个 this 打印 object
}
}
var x = X()
x.f1(options) //① 改写 object.f1.call(object,options)
分析
- 首先把
x.f1(options)
改写object.f1.call(object,options)
-
f1
内部的this
指向object
- 把
x.f2.call(this)
改写options.f2.call(object)
,这里的.call(this)
是显示指定 -
f2
内部的this
就是object
实操3:
function X(){
return object = {
name:'object',
options:null, //③ options = options
f1(x){
this.options = x //② 改写 object.options = options
this.f2() //③ 改写 object.f2.call(object)
},
f2(){
this.options.f2.call(this) //④ 显示绑定,改写
object.options.f2.call(object)
}
}
}
var options = {
name:'options',
f1(){},
f2(){
console.log(this) //⑤运行这个 this,打印 object
}
}
var x = X()
x.f1(options) //① 改写 object.f1.call(object,options)
分析
- 首先把
x.f1(options)
改写object.f1.call(object,options)
-
f1内
的this
指向object
,options
作为参数传进来 - 把
this.options = x
改写object.options = options
,同时也把this.f2()
改写称object.f2()
- 把
this.options.f2.call(this)
改写object.options.f2.call(object)
,.call(this)
是显示绑定 -
f2
内部的this
就是object