javascript中的this-笔记

     2022-03-14     312

关键词:

之前一直对this的指向很模糊,找了一些别人的博客看,又重新看了一下《你不知道的JavaScript》,感觉基本上是弄懂了,挑一些重点的地方记录一下,有些地方对我来说书上解释写的不够多,所以自己做下补充以方便理解,有理解错的地方还望指出。

 

一.澄清误区


 

首先你需要知道:

1.this并不指向函数自身

2.this的作用域在任何情况下都不指向函数的词法作用域。

举个例子:

function foo() {
    var a = 2;
    this.bar();
}

function bar() {
    console.log(this.a);
}

foo();//ReferenceError: a is not defined

第一次看这里的时候就掉坑里了,想当然的以为foo()中this指向window,而bar()又在全局中故能调用bar(),但实际上这是错的,不能使用this来引用一个词法作用域内部的东西。

this是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里调用。跟函数声明的位置没有任何关系,要区别于函数的作用域,函数的作用域是在它被定义的时候确定的。

要判断一个运行中函数的this绑定,需要找到这个函数的直接调用位置。

 

二.调用位置


 

调用位置就是函数在代码中被调用到的位置。

this有四条绑定规则:

 

1.默认绑定:非严格模式下this指向全局对象,严格模式绑定到undefined。 var bar = foo()

2.隐式绑定:this指向包含它的函数的对象,要注意隐式丢失的情况。 var bar = obj1.foo()

3.显示绑定:this指向call()、apply()和bind()方法指定的对象。 var bar = foo.call(obj2)

4.new绑定:this指向构造的新对象。 var bar = new foo()

优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

 

具体看下面:

1.默认绑定

无法应用其他规则时默认使用默认绑定。如果函数独立调用,使用默认绑定。

function foo() {
//看函数体是否处于严格模式,是看这个位置(
"use strict"
  console.log(this.a);
}

var a = 2;

foo(); //2  仅foo()函数本身,为独立调用

在上面的代码中,foo()是使用不带任何修饰的函数引用进行调用的,所以会使用默认绑定,非严格模式下this指向全局对象,严格模式绑定到undefined。不是指调用位置是否处于严格模式,而是函数体是否处于严格模式。

 

2.隐式绑定

考虑函数的调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。

function foo() {
  console.log(this.a);
}

var obj = {
  a: 2,
  foo: foo  //foo()被当作引用属性添加到obj中,此时它被obj对象包含,此时this指向obj
}

obj.foo(); //2

对象属性引用链中只有最后一层会影响调用位置。

function foo() {
  console.log(this.a);
}

var obj2 = {
  a: 42,
  foo: foo
}

var obj1 = {
  a: 2,
  obj2: obj2
}

obj1.obj2.foo();  //42  虽然这里有obj1对象和obj2对象,但是obj2才是处于最后一层最接近foo(),所以会指向obj2

 

隐式丢失的情况

来看下面的代码:

function foo() {
  console.log(this.a);
}

var obj = {
  a: 2,
  foo: foo
}

var bar = obj.foo;  //虽然bar是obj.foo的一个引用,但实际上bar引用的只是foo函数本身,可以看成bar() = foo(),此时foo()是独立调用故绑定到全局对象
var a = "oops, global";

bar(); //oops, global  

还有一种常见的隐式丢失的情况是传入回调函数

function foo() {
  console.log(this.a); 
}

function doFoo(fn) {
  fn();
}

var obj = {
  a: 2,
  foo: foo
}

var a = "oops, global";

doFoo(obj.foo); //oops, global  obj.foo作为参数传入实际上隐式赋值给了fn,可以看成fn = obj.foo,
//此时又回到了上一例代码的情况,引用的是foo函数本身,看成fn()= foo(),独立调用指向全局对象

 

3.显示绑定

使用函数的call()和apply()方法可以使用显示绑定,这两个方法的第一个参数都是一个对象,他们会把这个对象绑定到this,在调用函数时指定这个this,因为可以指定绑定对象故称做显示绑定。

function foo() {
  console.log(this.a);
}

var obj = {
  a: 2
}

foo.call(obj);

但是这种显示绑定没有解决隐式丢失的问题,要解决这个问题可以使用硬绑定。

function foo() {
  console.log(this.a);
}

var obj = {
  a: 2
}

var bar = function() {
  foo.call(obj);
}

bar(); //2

setTimeout(bar, 100) //2

bar.call(window); //2

创建了一个函数bar(),并在它的内部手动调用foo.call(obj),因此强制把foo的this绑定到了obj,之后无论如何调用函数bar,它总会手动在obj上调用foo,这叫做硬绑定。

硬绑定非常常用,ES5中提供了内置的方法Function.prototype.bind,bind()会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。

硬绑定的两个典型应用场景:

  • 创建一个包裹函数,传入所有的参数并返回接收到的所有值
function foo(something) { 
  console.log(this.a, something);
  return this.a + something; //返回接收到的所有this.a和something
}

var obj = {
  a: 2
}

var bar = function() {
  return foo.apply(obj, arguments); //this指向obj,arguments为传入的参数3
}

var b = bar(3); //2 3
console.log(b); //5
  • 创建一个可以重复使用的辅助函数
function foo(something) {
  console.log(this.a, something);
  return this.a + something;
}

function bind(fn, obj) {
  return function() {
    return fn.apply(obj, arguments);
  };
}

var obj = {
  a: 2
}

var bar = bind(foo, obj);
var b = bar(3); //2 3
console.log(b); //5

 

4.new绑定

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

①创建(构造)一个全新的对象。

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

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

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

function foo(a) {
  this.a = a;
}

var bar = new foo(2);//使用new操作符调用foo(),构造了一个新对象bar并把它绑定到foo()的this上
console.log(bar.a); //2

 

三.绑定例外

有一些例外情况需要注意

1.把null或undefined作为this的绑定对象传入call、apply或bind。

null和undefined在调用时会被忽略,然后应用默认绑定。

function foo() {
  console.log(this.a);
}

var a = 2;

foo.call(null);//2

有时你可能选择null作为一个占位值而选择null作为参数,但是总是用null来忽略this的绑定可能产生一些副作用。一种更安全的做法是传入一个空的非委托对象,把this绑定到这个对象不会对你的程序产生任何副作用。

 

2.无意间创建了一个函数的“间接引用”

同样会应用默认绑定,最容易在赋值时发生。

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的返回值是目标函数的引用,故调用的位置是foo(),又会应用默认绑定

 

3.软绑定 softBind()

硬绑定会降低函数的灵活性,使用硬绑定后就无法使用隐式绑定或显示绑定来修改this。

软绑定:给默认绑定指定一个全局对象和undefined以外的值,可以实现和硬绑定相同的效果,同时保留隐式绑定或显示绑定修改this的能力。

除了软绑定外,softBind()的其他原理和bind()类似。首先检查调用时的this,如果this绑定到全局对象或者undefined,就把指定的对象绑定到this,否则不会修改this。

function foo() {
  console.log("name:" + this.name);
}

var obj = {
  name: "obj"
}
var obj2 = {
  name: "obj2"
}
var obj3 = {
  name: "obj3"
}

var fooOBJ = foo.softBind(obj);

fooOBJ();//obj

obj2.foo = foo.softBind(obj);
obj2.foo();//obj2 obj2.foo()调用时,this绑定到obj2上,不是全局对象也不是undefined,所以不会调用指定的obj,而是使用原来的obj2。

fooOBJ.call(obj3);//硬绑定时绑定了obj3,后面不可再修改

setTimeout(obj2.foo, 10);//假设setTimeout(fn,10),fn实际上引用的只是foo(),可以看成fn = obj2.foo,类似前面的隐式绑定丢失,
//会使用默认绑定到全局,这时会发生软绑定,绑定到指定对象obj

 

4.箭头函数

箭头函数无法使用this中的四种绑定规则,而是根据外层(函数或全局)作用域来决定this。

function foo() {
  return (a) => {
    console.log(this.a);
  }
}

var obj1 = {
  a: 2
}

var obj2 = {
  a: 3
}

var bar = foo.call(obj1);
bar.call(obj2);//2 箭头函数会捕获调用时foo()的this,foo()的this会显示绑定到obj1,
//bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改

箭头函数常用于回调函数中,例如事件处理器或者定时器

function foo() {
  setTimeout( () => {
    console.log(this.a);//箭头函数会继承外层函数调用的this绑定,这里箭头函数的外层函数是foo(),foo()的this绑定到obj,所以箭头函数的this也绑定到obj。
  }, 100);
}

var obj = {
  a: 2
}

foo.call(obj);//2

 





学习笔记---js基础类

JavaScript中的this!https://qiutc.me/post/this-this-this-in-javascript.html#call,_apply,_bind讲解了JS中this在特殊情况下的变化。以及如何保持this值。  查看详情

javascript高级编程笔记06(面相对象2)

1) 构造函数模式es中的构造函数可以用来创建特定类型的对象,像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中,此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法,例如:functionPerson(na... 查看详情

javascript学习笔记——this原型javascript面向对象

一、this在JavaScript中this表示:谁调用它,this就是谁。JavaScript是由对象组成的,一切皆为对象,万物皆为对象。this是一个动态的对象,根据调用的对象不同而发生变化,当然也可以使用call、apply修改this指向的对象。它代表函数... 查看详情

javascript对象(复习笔记)

js对象对象构造器functionperson(firstname,lastname,age,eyecolor){ this.firstname=firstname; this.lastname=lastname; this.age=age;this.eyecolor=eyecolor;}myFather=newperson("John","Doe",50,"blue"); 或者person={f 查看详情

javascript面向对象学习笔记

面向对象:变量和属性一样的。属性:属于一个对象变量:属于任何一个人函数:方法:属于一个对象难点:this:当前的方法属于谁,this就是谁的例子window.show=function(){ alert(this);}//this是windowfunctionshow(){ alert(this);}Object:用来实现对... 查看详情

js日常笔记之this

在javascript中自己创建构造函数时可以利用this来指向新创建的对象上。这样就可以避免函数中的this指向全局了,如下 varx=2;  functiontest(){    this.x=1;  }  varo=newtest();  alert(x);//2 反之,如果不使用new,只把上面... 查看详情

《javascript设计模式与开发》笔记2.this指针

 1.作为对象方法调用 2.作为普通函数调用1.作为普通函数进行调用2.将函数赋值给一个对象3.callback内部的this指向了window4.解决callback内部的this问题 3.构造器调用1.普通构造器2.如果构造器显示地返回了一个object类型对象... 查看详情

jquery中的$(this)和javascript中的this

  this是JavaScript中的关键字。 $(this)可以认为是用jQuery包装过JavaScript中的this,包装后$(this)就会继承jQuery的方法。本质就是JavaScript与jQuery对象的转换$(‘a‘).click(function(){//这里的this指向当前点击的DOM节点,也就是a。可以调... 查看详情

javascript中的this作用域

javaScript中的this作用域  javaScript中的this作用域java的区别是,java中的this是在编译中确定,    javaScript中的this是在运行时确定的,不同的调用方式,决定js中的this指向不同的对象。 代码实现:  //this作用域... 查看详情

javascript跟aaron大神学习jquery源码笔记

/*通过new操作符构建一个对象,一般经过四步:A.创建一个新对象B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象)C.执行构造函数中的代码D.返回这个新对象最后一点就说明了,我们只要返回一个新对象即可。其... 查看详情

javascript面向对象的程序设计记录笔记4

组合使用构造函数模式和原型模式:  functionPerson(name,age,job){this.name=name;this.age=age;this.job=job;this.friends=[‘Shelby‘,‘Court‘];}Person.prototype={connstructor:Person,sayName:function(){returnthis.name;}}varp 查看详情

javascript基础笔记总结(全部)(代码片段)

...函数16.5函数做为参数16.6函数作用域16.7函数的作用域链17.JavaScript对象18.this用法18.1指向对象中的this18.2指向全局对象的this18.3this的指向19.对象的遍历和删除20.包装对象21.数学对象(标准库对象,内置对象)22.日期对象2... 查看详情

详解javascript中的this

JavaScript中的this总是让人迷惑,应该是js众所周知的坑之一。个人也觉得js中的this不是一个好的设计,由于this晚绑定的特性,它可以是全局对象,当前对象,或者…有人甚至因为坑大而不用this。其实如果完全掌握了this的工作原... 查看详情

详解javascript中的this

JavaScript中的this总是让人迷惑,应该是js众所周知的坑之一。个人也觉得js中的this不是一个好的设计,由于this晚绑定的特性,它可以是全局对象,当前对象,或者…有人甚至因为坑大而不用this。其实如果完全掌握了this的工作原... 查看详情

javascript中的this

JavaScript中的this指向问题在JavaScript中,this是经常使用的一个关键字,按照规定,this指向调用者,即谁调用该对象,则this指向谁,但是this存在一个缺陷,当在一个函数中定义另一个函数时,内部函数的this不会指向外部函数,而... 查看详情

读书笔记《你不知道的javascript(上卷)》——第二部分this和对象原型(代码片段)

文章目录第6章行为委托6.1面向委托的设计6.1.1类理论6.1.2委托理论1.互相委托(禁止)2.调试6.1.3比较思维模型6.2类与对象6.2.1控件“类”ES6的class语法糖6.2.2委托控件对象6.3更简洁的设计反类6.4更好的语法反词法6... 查看详情

javascript中的this陷阱的最全收集

JavaScript来自一门健全的语言,所以你可能觉得JavaScript中的this和其他面向对象的语言如java的this一样,是指存储在实例属性中的值。事实并非如此,在JavaScript中,最好把this当成哈利波特中的博格特的背包,有着深不可测的魔力... 查看详情

javascript语言中的this

JavaScript语言中的this由于其运行期绑定的特性,JavaScript中的this含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript中函数的调用有以下几种方式:作为对象方法调用,作为函数... 查看详情